Module 2 - Past Exams

Exercises from past exams (both full and partial exams)

Exercises 1

Look at the BST implemented below:

_images/1562023_tree.png

We have the BST shown above and a number N. Our task is to find the greatest number (node) in the binary search tree that is less than or equal to N.

You are asked to implement the missing method findLargestSmallerKey(root, N). Do not modify other parts of the code!

This method finds the largest key (value of node, as integer) in the BST that is smaller than N. If such a number doesn’t exist, return -1.

Examples

Input : N = 24 Output : 21 (searching for 24 will be like 5->12->21)

Input : N = 4 Output : 3 (searching for 4 will be like 5->2->3)

Input : N = 10 Output : 9 (searching for 10 will be like 5->12->9)

TIPS

There are two key parts for the algorithm:

  • Part 1: traversing the tree Starting from the root, for each node we choose its left or its right child as the next step, based on a comparison between that node’s key and N: If the current node holds a key smaller than N, we proceed to its right subtree looking for larger keys. Otherwise, we proceed to its left subtree looking for smaller keys.

  • Part 2: finding the key During this iteration, when the current key is smaller than N, we store it as our result and keep looking for a larger key that is still smaller than N.

[8]:
class BinarySearchTree:
    def __init__(self, value):
        self.__data = value
        self.__right = None
        self.__left = None
        self.__parent = None

    def getValue(self):
        return self.__data
    def setValue(self, newValue):
        self.__data = newValue

    def getParent(self):
        return self.__parent
    def setParent(self, tree):
        self.__parent = tree

    def getRight(self):
        return self.__right
    def getLeft(self):
        return self.__left

    def insertRight(self, tree):
        if self.__right == None:
            self.__right = tree
            tree.setParent(self)
    def insertLeft(self, tree):
        if self.__left == None:
            self.__left = tree
            tree.setParent(self)

def createBST(intList):

    BST = None
    if len(intList) > 0:
        BST = BinarySearchTree(intList[0])
        for el in intList[1:]:
            cur_el = BST
            alreadyPresent = False
            prev_el = None
            while cur_el != None:
                prev_el = cur_el
                cv = cur_el.getValue()
                if  cv > el:
                    cur_el = cur_el.getLeft()
                elif cv < el:
                    cur_el = cur_el.getRight()
                else:
                    alreadyPresent = True
                    break

            if not alreadyPresent:
                node = BinarySearchTree(el)
                node.setParent(prev_el)
                if prev_el.getValue() > el:
                    prev_el.insertLeft(node)
                else:
                    prev_el.insertRight(node)

    return BST


def printTree(root):
    cur = root
    nodes = [(cur,0)]
    tabs = ""
    lev = 0
    while len(nodes) >0:
        cur, lev = nodes.pop(-1)
        if cur.getRight() != None:
            print ("{}{} (r)-> {}".format("\t"*lev,
                                          cur.getValue(),
                                          cur.getRight().getValue()))
            nodes.append((cur.getRight(), lev+1))
        if cur.getLeft() != None:
            print ("{}{} (l)-> {}".format("\t"*lev,
                                          cur.getValue(),
                                          cur.getLeft().getValue()))
            nodes.append((cur.getLeft(), lev+1))


# START CODING BELOW HERE:

def findLargestSmallerKey(root, N):

    result = -1

    #to implement


    return result


# DO NOT modify code below:

if __name__ == "__main__":

    inList = [5,2,1,3,12,9,21,19,25]

    BST = createBST(inList)

    print("Tree:\n")
    printTree(BST)


    print("\nGreatest number in the BST that is less than or equal to 24 --> \n")

    print(findLargestSmallerKey(BST, 24))

    print("\nGreatest number in the BST that is less than or equal to 4 --> \n")

    print(findLargestSmallerKey(BST, 4))


    print("\nGreatest number in the BST that is less than or equal to 10 --> \n")

    print(findLargestSmallerKey(BST, 10))

    assert findLargestSmallerKey(BST, 24) == 21 , "It should be 21"

    assert findLargestSmallerKey(BST, 4) == 3, "It should be 3"

    assert findLargestSmallerKey(BST, 10) == 9, "It should be 9"

Tree:

5 (r)-> 12
5 (l)-> 2
        2 (r)-> 3
        2 (l)-> 1
        12 (r)-> 21
        12 (l)-> 9
                21 (r)-> 25
                21 (l)-> 19

Greatest number in the BST that is less than or equal to 24 -->

-1

Greatest number in the BST that is less than or equal to 4 -->

-1

Greatest number in the BST that is less than or equal to 10 -->

-1
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb Cell 4 line 1
    <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X15sZmlsZQ%3D%3D?line=114'>115</a> print("\nGreatest number in the BST that is less than or equal to 10 --> \n")
    <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X15sZmlsZQ%3D%3D?line=116'>117</a> print(findLargestSmallerKey(BST, 10))
--> <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X15sZmlsZQ%3D%3D?line=118'>119</a> assert findLargestSmallerKey(BST, 24) == 21 , "It should be 21"
    <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X15sZmlsZQ%3D%3D?line=120'>121</a> assert findLargestSmallerKey(BST, 4) == 3, "It should be 3"
    <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X15sZmlsZQ%3D%3D?line=122'>123</a> assert findLargestSmallerKey(BST, 10) == 9, "It should be 9"

AssertionError: It should be 21

Exercises 2

Consider the DiGraphLL class provided in the code chunk below implementing a directed graph by adjacency linked list.

The graph structure is:

_images/13032023_DG.png

Please check carefully how the class is implemented since it might slightly differ from the one you have seen during the practical class.

Implement the two missing methods:

  1. checkSelfEdge(self, node): This method checks whether a node has a “self” edge, meaning an edge pointing to itself (e.g. node 4, 6,7). This method returns a boolean value.

    HINT: Remember to make sure that the node you are checking for a self edge is actually in the dictionary of nodes.

  2. isolatedNodes(self): This method finds the nodes that are isolated from the graph (i.e. node 3, node 7), meaning those that do not have any incoming/outgoing edges. Keep in mind that a self-edge should not be considered as incoming/outgoing (i.e. Node 7 is considered isolated). This method returns a list.

    HINT: A node A is isolated only if:

    • its dictionary of edges (inner dict) does not contain other nodes except for itself or it’s empty

    • node A is not present in the inner dict of other nodes of the graph (no incoming edges).

[7]:
class DiGraphLL:
    def __init__(self):
        self.nodes = dict()

    def insertNode(self, node):
        test = self.nodes.get(node, None)

        if test == None:
            self.nodes[node] = {}

    def insertEdge(self, node1, node2, weight):
        test = self.nodes.get(node1, None)
        test1 = self.nodes.get(node2, None)
        if test != None and test1 != None:
            test = self.nodes[node1].get(node2, None)
            if test != None:
                exStr= "Edge {} --> {} already existing.".format(node1,node2)
                raise Exception(exStr)
            else:
                self.nodes[node1][node2] = weight


    def __len__(self):
        return len(self.nodes)

    def nodes(self):
        return list(self.nodes.keys())

    def graph(self):
        return self.nodes

    def __str__(self):
        ret = ""
        for n in self.nodes:
            for edge in self.nodes[n]:

                ret += "{} -- {} --> {}\n".format(str(n),
                                                  str(self.nodes[n][edge]),
                                                  str(edge))
        return ret

    # do not modify code ABOVE this line!

    def checkSelfEdge(self, node):
        """
        Return Boolean based on whether the node has a self-edge or not
        """
        raise NotImplementedError


    def isolatedNodes(self):
        """
        Return a list of isolated nodes (see pdf)
        """
        raise NotImplementedError


if __name__ == "__main__":

    G = DiGraphLL()
    for i in range(7):
        n = "Node_{}".format(i+1)
        G.insertNode(n)

    G.insertEdge("Node_2", "Node_1", 7)
    G.insertEdge("Node_1", "Node_2", 2)
    G.insertEdge("Node_1", "Node_4", 5)
    G.insertEdge("Node_2", "Node_5", 6)
    G.insertEdge("Node_6", "Node_5", 3)
    G.insertEdge("Node_4", "Node_5", 15)
    G.insertEdge("Node_6", "Node_6", 2)
    G.insertEdge("Node_7", "Node_7", 4)
    G.insertEdge("Node_4", "Node_4", 4)

    print("Size is: {}".format(len(G)))
    print("Nodes: {}".format(G.nodes))

    #PART 1
    print("\nPART 1\n")
    print("Check for self edge --> \n")
    print("Node_10:")
    print(G.checkSelfEdge("Node_10"))
    print("Node_2:")
    print(G.checkSelfEdge("Node_2"))
    print("Node_6:")
    print(G.checkSelfEdge("Node_6"))
    print("Node_7:")
    print(G.checkSelfEdge("Node_7"))

    print("\nPART 2\n")
    print("Isolated nodes:\n")
    print(G.isolatedNodes())

    assert(G.isolatedNodes() == ['Node_3', 'Node_7'])
Size is: 7
Nodes: {'Node_1': {'Node_2': 2, 'Node_4': 5}, 'Node_2': {'Node_1': 7, 'Node_5': 6}, 'Node_3': {}, 'Node_4': {'Node_5': 15, 'Node_4': 4}, 'Node_5': {}, 'Node_6': {'Node_5': 3, 'Node_6': 2}, 'Node_7': {'Node_7': 4}}

PART 1

Check for self edge -->

Node_10:
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb Cell 7 line 8
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X25sZmlsZQ%3D%3D?line=79'>80</a> print("Check for self edge --> \n")
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X25sZmlsZQ%3D%3D?line=80'>81</a> print("Node_10:")
---> <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X25sZmlsZQ%3D%3D?line=81'>82</a> print(G.checkSelfEdge("Node_10"))
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X25sZmlsZQ%3D%3D?line=82'>83</a> print("Node_2:")
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X25sZmlsZQ%3D%3D?line=83'>84</a> print(G.checkSelfEdge("Node_2"))

/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb Cell 7 line 4
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X25sZmlsZQ%3D%3D?line=43'>44</a> def checkSelfEdge(self, node):
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X25sZmlsZQ%3D%3D?line=44'>45</a>     """
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X25sZmlsZQ%3D%3D?line=45'>46</a>     Return Boolean based on whether the node has a self-edge or not
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X25sZmlsZQ%3D%3D?line=46'>47</a>     """
---> <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X25sZmlsZQ%3D%3D?line=47'>48</a>     raise NotImplementedError

NotImplementedError:

Exercises 3

Gnome Sort (also called stupid sort) is a variation of the insertion sort algorithm.
Dick Grune described the sorting method with the following story:

Gnome Sort is based on the technique used by the standard Dutch Garden Gnome (Du.: tuinkabouter). Here is how a garden gnome sorts a line of flower pots. Basically, he looks at the flower pot next to him and the previous one; if they are in the right order he steps one pot forward, otherwise, he swaps them and steps one pot backward. Boundary conditions: if there is no previous pot, he steps forwards; if there is no pot next to him, he is done. — “Gnome Sort - The Simplest Sort Algorithm”. Dickgrune.com

The steps of the Gnome Sort algorithm are basically the following:

  1. If you are at the start of the array then go to the right element (from arr[0] to arr[1]).

  2. If the current array element is larger or equal to the previous array element then go one step right.

  3. If the current array element is smaller than the previous array element then swap these two elements and go one step backwards

  4. Repeat steps 2) and 3) till the index reaches the end of the array.

If the end of the array is reached then stop and the array is sorted.

You are asked to implement the gnome sort algorithm by filling the sort method below.

[9]:
import random

class SortingAlgorithm:
    def __init__(self, data, verbose = True):
        self.data = data
        self.comparisons = 0
        self.operations = 0
        self.verbose = verbose

    def getData(self):
        return self.data

    def getOperations(self):
        return self.operations

    def getComparisons(self):
        return self.comparisons

    def sort(self):
        raise NotImplementedError

class GnomeSort(SortingAlgorithm):

    def sort(self):
        self.comparisons = 0
        self.operations = 0
        """
        to implement
        """


if __name__ == "__main__":

    d = [7, 5, 10, -11 ,3, -4, 99, 1]
    print("Before sorting:\n")
    print(d)
    gnSort = GnomeSort(d, verbose = True)
    gnSort.sort()
    print("After sorting:\n")
    print(d)
    d = []

    for i in range(0,1000):
        d.append(random.randint(0,1000))
    gnSort = GnomeSort(d, verbose = False)
    gnSort.sort()
    print("\nNumber of elements: {}".format(len(d)))
    print("Number of comparisons: {}".format(gnSort.getComparisons()))
    print("Number of swaps: {}".format(gnSort.getOperations()))

    d = []
    for i in range(0,2000):
        d.append(random.randint(0,1000))
    gnSort = GnomeSort(d, verbose = False)
    gnSort.sort()
    print("\nNumber of elements: {}".format(len(d)))
    print("Number of comparisons: {}".format(gnSort.getComparisons()))
    print("Number of swaps: {}".format(gnSort.getOperations()))

    test = True
    for el in range(0,len(d)-1):
        test = test and (d[el]<= d[el+1])
    print("\nSorting test passed? {}".format(test))
Before sorting:

[7, 5, 10, -11, 3, -4, 99, 1]
After sorting:

[7, 5, 10, -11, 3, -4, 99, 1]

Number of elements: 1000
Number of comparisons: 0
Number of swaps: 0

Number of elements: 2000
Number of comparisons: 0
Number of swaps: 0

Sorting test passed? False

Exercises 4

Consider the DiGraphLL class provided during the course, implementing a directed graph by adjacency linked list.
Implement the two missing methods:
  1. MinNumConnection(n) that returns a list of nodes with at least n incoming edges.

  2. edgeWeightSum() that returns the sum of the weights for all the edges in the graph. To test your implementation, the code below has already a main to build this graph:

_images/graph_2.png
[5]:
class DiGraphLL:
    def __init__(self):
        self.nodes = dict()

    def insertNode(self, node):
        test = self.nodes.get(node, None)

        if test == None:
            self.nodes[node] = {}
            #print("Node {} added".format(node))

    def insertEdge(self, node1, node2, weight):
        test = self.nodes.get(node1, None)
        test1 = self.nodes.get(node2, None)
        if test != None and test1 != None:
            #if both nodes exist othewise don't do anything
            test = self.nodes[node1].get(node2, None)
            if test != None:
                exStr= "Edge {} --> {} already existing.".format(node1,node2)
                raise Exception(exStr)
            else:
                #print("Inserted {}-->{} ({})".format(node1,node2,weight))
                self.nodes[node1][node2] = weight


    def __len__(self):
        return len(self.nodes)

    def getnodes(self):
        return list(self.nodes.keys())


    def __str__(self):
        ret = ""
        for n in self.nodes:
            for edge in self.nodes[n]:

                ret += "{} -- {} --> {}\n".format(str(n),
                                                  str(self.nodes[n][edge]),
                                                  str(edge))
        return ret

    def MinNumConnection(self, n):
        """
        Methods that returns the nodes with at least n incoming edges
        """
        raise NotImplementedError


    def edgeWeightSum(self):
        """
        Methods that returns the sum of all the edges
        """
        sum_weights = 0
        raise NotImplementedError



if __name__ == "__main__":
    G = DiGraphLL()

    for i in range(6):
        n = "Node_{}".format(i+1)
        G.insertNode(n)


    for i in range(2,4):
        n = "Node_" + str(i+1)
        six = "Node_6"
        n_plus = "Node_" + str((i+2) % 6)
        G.insertEdge(n, n_plus,3)
        G.insertEdge(n, six,2)

    G.insertEdge("Node_2","Node_1",3)
    G.insertEdge("Node_2","Node_4",4)
    G.insertEdge("Node_5","Node_4",10)
    G.insertEdge("Node_5", "Node_1", 4)
    G.insertEdge("Node_5", "Node_6", 2)
    G.insertEdge("Node_6", "Node_6", 2)
    print(G)

    #part 1
    print("\n Nodes with at least n incoming edges:\n")
    assert(['Node_1', 'Node_4', 'Node_5', 'Node_6'] == G.MinNumConnection(1))
    print("n=1 --> "+ str(G.MinNumConnection(1)))
    assert(['Node_1', 'Node_4', 'Node_6'] == G.MinNumConnection(2))
    print("n=2 --> "+ str(G.MinNumConnection(2)))
    assert(['Node_4' , 'Node_6'] == G.MinNumConnection(3))
    print("n=3 --> "+ str(G.MinNumConnection(3)))
    assert(['Node_6'] == G.MinNumConnection(4))
    print("n=4 --> "+ str(G.MinNumConnection(4)))
    assert([] == G.MinNumConnection(5))
    print("n=5 --> "+ str(G.MinNumConnection(5)))

    #part 2

    print("\nSum of all weights: ")
    print(G.edgeWeightSum())
    assert(35 == G.edgeWeightSum())


Node_2 -- 3 --> Node_1
Node_2 -- 4 --> Node_4
Node_3 -- 3 --> Node_4
Node_3 -- 2 --> Node_6
Node_4 -- 3 --> Node_5
Node_4 -- 2 --> Node_6
Node_5 -- 10 --> Node_4
Node_5 -- 4 --> Node_1
Node_5 -- 2 --> Node_6
Node_6 -- 2 --> Node_6


 Nodes with at least n incoming edges:

---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb Cell 13 line 8
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X35sZmlsZQ%3D%3D?line=81'>82</a> #part 1
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X35sZmlsZQ%3D%3D?line=82'>83</a> print("\n Nodes with at least n incoming edges:\n")
---> <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X35sZmlsZQ%3D%3D?line=83'>84</a> assert(['Node_1', 'Node_4', 'Node_5', 'Node_6'] == G.MinNumConnection(1))
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X35sZmlsZQ%3D%3D?line=84'>85</a> print("n=1 --> "+ str(G.MinNumConnection(1)))
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X35sZmlsZQ%3D%3D?line=85'>86</a> assert(['Node_1', 'Node_4', 'Node_6'] == G.MinNumConnection(2))

/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb Cell 13 line 4
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X35sZmlsZQ%3D%3D?line=42'>43</a> def MinNumConnection(self, n):
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X35sZmlsZQ%3D%3D?line=43'>44</a>     """
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X35sZmlsZQ%3D%3D?line=44'>45</a>     Methods that returns the nodes with at least n incoming edges
     <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X35sZmlsZQ%3D%3D?line=45'>46</a>     """
---> <a href='vscode-notebook-cell:/home/davidebrex/Documents/ESERCITAZIONI/sciprog-ds-M2-23_24/M2_pastExam.ipynb#X35sZmlsZQ%3D%3D?line=46'>47</a>     raise NotImplementedError

NotImplementedError: