Esempio n. 1
0
    def resize_table(self, new_capacity: int) -> None:
        """
        Changes the capacity of the internal hash table. All existing key / value pairs
        must remain in the new hash map and all hash table links must be rehashed.
        If new_capacity is less than 1, this method should do nothing.
        """

        # create a new table with the new capacity if new_capacity is greater than 1
        if new_capacity > 0:
            resized_table = DynamicArray()
            for _ in range(new_capacity):
                resized_table.append(LinkedList())

            # assign the current hash table in temp_table & reassign self.buckets to the resized_table
            temp_table = self.buckets
            self.buckets = resized_table
            self.capacity = new_capacity
            self.size = 0

            # Walk through each bucket in the temp_table and visit each element
            for i in range(temp_table.length()):
                temp_bucket = temp_table.get_at_index(i)
                if temp_bucket.length() > 0:
                    cur = temp_bucket.head
                    # insert the element into the new hash table at the rehashed index
                    while cur:
                        self.put(cur.key, cur.value)
                        cur = cur.next
Esempio n. 2
0
 def __init__(self, capacity: int, function) -> None:
     """
     Init new HashMap based on DA with SLL for collision resolution
     DO NOT CHANGE THIS METHOD IN ANY WAY
     """
     self.buckets = DynamicArray()
     for _ in range(capacity):
         self.buckets.append(LinkedList())
     self.capacity = capacity
     self.hash_function = function
     self.size = 0
Esempio n. 3
0
    def __init__(self, start_heap=None):
        """
        Initializes a new MinHeap
        DO NOT CHANGE THIS METHOD IN ANY WAY
        """
        self.heap = DynamicArray()

        # populate MH with initial values (if provided)
        # before using this feature, implement add() method
        if start_heap:
            for node in start_heap:
                self.add(node)
Esempio n. 4
0
    def get_keys(self) -> DynamicArray:
        """
        Returns a Dynamic Array that contains all keys stored in your hash map.
        The order of the keys in the DA does not matter.
        """
        key_arr = DynamicArray()

        # Traverse the hash map. If a bucket is not empty, walk through the chained linked list
        # to retrieve each element's key
        for i in range(self.buckets.length()):
            bucket = self.buckets.get_at_index(i)
            if bucket is not None:
                cur = bucket.head
                while cur is not None:
                    key_arr.append(cur.key)
                    cur = cur.next
        return key_arr
Esempio n. 5
0
    def build_heap(self, da: DynamicArray) -> None:
        """
        Receives a dynamic array with objects in any order and builds a proper MinHeap from them.
        Current content of the MinHeap is lost.
        Runtime complexity must be O(N).
        """
        self.heap = DynamicArray()
        counter1 = 0

        # copy elements in da to the MinHeap array (O(N))
        for i in range(da.length()):
            self.heap.append(da.get_at_index(i))

        last_index = self.heap.length() - 1
        first_nonleaf = int((last_index - 1) / 2)

        # Start with the first non-leaf node in the tree and continue swapping nodes until
        # the tree is a valid heap (O(N))
        for pos in range(first_nonleaf, -1, -1):
            self.percolate_down(pos)
Esempio n. 6
0
class MinHeap:
    def __init__(self, start_heap=None):
        """
        Initializes a new MinHeap
        DO NOT CHANGE THIS METHOD IN ANY WAY
        """
        self.heap = DynamicArray()

        # populate MH with initial values (if provided)
        # before using this feature, implement add() method
        if start_heap:
            for node in start_heap:
                self.add(node)

    def __str__(self) -> str:
        """
        Return MH content in human-readable form
        DO NOT CHANGE THIS METHOD IN ANY WAY
        """
        return 'HEAP ' + str(self.heap)

    def is_empty(self) -> bool:
        """
        Return True if no elements in the heap, False otherwise
        DO NOT CHANGE THIS METHOD IN ANY WAY
        """
        return self.heap.length() == 0

    def add(self, node: object) -> None:
        """
        Adds a new object to the MinHeap maintaining heap property.
        Runtime complexity of its implementation must be O(logN)
        """

        self.heap.append(node)
        cur_index = self.heap.length() - 1
        parent_index = int((cur_index - 1) / 2)

        # the added node percolates up the tree until it reaches the root or parent node's value is less
        while cur_index > 0 and self.heap.get_at_index(
                parent_index) > self.heap.get_at_index(cur_index):
            self.heap.swap(parent_index, cur_index)
            cur_index = parent_index
            parent_index = int((cur_index - 1) / 2)

    def get_min(self) -> object:
        """
        Returns an object with a minimum key without removing it from the heap.
        If heap is empty, the method raises an Exception.
        Runtime complexity must be O(1).
        """
        if self.heap.length() == 0:
            raise MinHeapException

        return self.heap.get_at_index(0)

    def remove_min(self) -> object:
        """
        Returns an object with a minimum key and removes it from the heap.
        If the heap is empty, the method raises an Exception.
        Runtime complexity of this implementation must be O(logN).
        """
        if self.heap.length() == 0:
            raise MinHeapException

        min_value = self.heap.get_at_index(0)
        self.heap.swap(0,
                       self.heap.length() -
                       1)  # replace the min node with the last filled node
        self.heap.pop()  # remove the min node
        self.percolate_down(0)  # call percolate_down helper method
        return min_value

    def percolate_down(self, cur_index):
        """
        Helper method to percolate node down the tree until the subtree is a valid heap
        Runtime complexity is O(logN)
        """
        while cur_index >= 0:
            swap_index = -1
            right_index = 2 * cur_index + 2
            left_index = 2 * cur_index + 1

            # Case where right child is less than the current node
            if right_index < self.heap.length() and self.heap.get_at_index(
                    right_index) < self.heap.get_at_index(cur_index):
                # check if the left child is less than or equal to the right child. parent node will swap with left child
                if self.heap.get_at_index(
                        left_index) <= self.heap.get_at_index(right_index):
                    swap_index = left_index
                else:
                    swap_index = right_index
            # Case where left child is less than the current node
            elif left_index < self.heap.length() and self.heap.get_at_index(
                    left_index) < self.heap.get_at_index(cur_index):
                swap_index = left_index

            # swap the current node and the smaller child node (swap_index > 0 means that a swap is needed)
            if swap_index >= 0:
                self.heap.swap(cur_index, swap_index)

            cur_index = swap_index

    def build_heap(self, da: DynamicArray) -> None:
        """
        Receives a dynamic array with objects in any order and builds a proper MinHeap from them.
        Current content of the MinHeap is lost.
        Runtime complexity must be O(N).
        """
        self.heap = DynamicArray()
        counter1 = 0

        # copy elements in da to the MinHeap array (O(N))
        for i in range(da.length()):
            self.heap.append(da.get_at_index(i))

        last_index = self.heap.length() - 1
        first_nonleaf = int((last_index - 1) / 2)

        # Start with the first non-leaf node in the tree and continue swapping nodes until
        # the tree is a valid heap (O(N))
        for pos in range(first_nonleaf, -1, -1):
            self.percolate_down(pos)
Esempio n. 7
0
    print("-------------------")
    h = MinHeap(['fish', 'bird'])
    print(h)
    for value in ['monkey', 'zebra', 'elephant', 'horse', 'bear']:
        h.add(value)
        print(h)

    print("\nPDF - get_min example 1")
    print("-----------------------")
    h = MinHeap(['fish', 'bird'])
    print(h)
    print(h.get_min(), h.get_min())

    print("\nPDF - remove_min example 1")
    print("--------------------------")
    h = MinHeap([1, 10, 2, 9, 3, 8, 4, 7, 5, 6])
    while not h.is_empty():
        print(h, end=' ')
        print(h.remove_min())

    print("\nPDF - build_heap example 1")
    print("--------------------------")
    da = DynamicArray([100, 20, 6, 200, 90, 150, 300])
    h = MinHeap(['zebra', 'apple'])
    print(h)
    h.build_heap(da)
    print(h)
    da.set_at_index(0, 500)
    print(da)
    print(h)
Esempio n. 8
0
class HashMap:
    def __init__(self, capacity: int, function) -> None:
        """
        Init new HashMap based on DA with SLL for collision resolution
        DO NOT CHANGE THIS METHOD IN ANY WAY
        """
        self.buckets = DynamicArray()
        for _ in range(capacity):
            self.buckets.append(LinkedList())
        self.capacity = capacity
        self.hash_function = function
        self.size = 0

    def __str__(self) -> str:
        """
        Return content of hash map t in human-readable form
        DO NOT CHANGE THIS METHOD IN ANY WAY
        """
        out = ''
        for i in range(self.buckets.length()):
            list = self.buckets.get_at_index(i)
            out += str(i) + ': ' + str(list) + '\n'
        return out

    def new_index(self, key):
        """Helper method to calculate the index to insert an element to the hash map"""
        hash = self.hash_function(key)
        return abs(hash) % self.capacity

    def clear(self) -> None:
        """
        Clears the contents of the hash map. It does not change the underlying hash table capacity.
        """
        for i in range(self.buckets.length()):
            if self.buckets.get_at_index(i).length() > 0:
                self.size -= self.buckets.get_at_index(i).length()   # reduce self.size by length of linked list
                self.buckets.set_at_index(i, LinkedList())

    def get(self, key: str) -> object:
        """
        Returns the value associated with the given key.
        If the key is not in the hash map, the method returns none.
        """
        bucket = self.buckets.get_at_index(self.new_index(key))

        if bucket.contains(key) is not None:
            return bucket.contains(key).value

        else:
            return None

    def put(self, key: str, value: object) -> None:
        """
        Updates the key / value pair in the hash map. If a given key already exists in the hash map,
        its associated value should be replaced with the new value. If a given key is not in the hash map,
        a key / value pair should be added.
        """
        bucket = self.buckets.get_at_index(self.new_index(key))

        # if the same key exists, replace the value of the node with the new value
        if bucket.contains(key) is not None:
            bucket.contains(key).value = value
        # if the key doesn't exist, insert the element as a new node
        else:
            bucket.insert(key, value)
            self.size += 1

    def remove(self, key: str) -> None:
        """
        Removes the given key and its associated value from the hash map.
        If a given key is not in the hash map, the method does nothing (no exception needs to be raised).
        """
        bucket = self.buckets.get_at_index(self.new_index(key))
        if bucket.contains(key) is not None:
            bucket.remove(key)
            self.size -= 1

    def contains_key(self, key: str) -> bool:
        """
        Returns True if the given key is in the hash map, otherwise it returns False.
        An empty hash map does not contain any keys.
        """
        bucket = self.buckets.get_at_index(self.new_index(key))
        if bucket.contains(key) is not None:
            return True
        return False

    def empty_buckets(self) -> int:
        """
        Returns a number of empty buckets in the hash table.
        """
        empty_buckets = 0
        for index in range(self.buckets.length()):
            if self.buckets.get_at_index(index).length() == 0:
                empty_buckets += 1
        return empty_buckets

    def table_load(self) -> float:
        """
        Returns the current hash table load factor.
        """
        return self.size / self.capacity

    def resize_table(self, new_capacity: int) -> None:
        """
        Changes the capacity of the internal hash table. All existing key / value pairs
        must remain in the new hash map and all hash table links must be rehashed.
        If new_capacity is less than 1, this method should do nothing.
        """

        # create a new table with the new capacity if new_capacity is greater than 1
        if new_capacity > 0:
            resized_table = DynamicArray()
            for _ in range(new_capacity):
                resized_table.append(LinkedList())

            # assign the current hash table in temp_table & reassign self.buckets to the resized_table
            temp_table = self.buckets
            self.buckets = resized_table
            self.capacity = new_capacity
            self.size = 0

            # Walk through each bucket in the temp_table and visit each element
            for i in range(temp_table.length()):
                temp_bucket = temp_table.get_at_index(i)
                if temp_bucket.length() > 0:
                    cur = temp_bucket.head
                    # insert the element into the new hash table at the rehashed index
                    while cur:
                        self.put(cur.key, cur.value)
                        cur = cur.next

    def get_keys(self) -> DynamicArray:
        """
        Returns a Dynamic Array that contains all keys stored in your hash map.
        The order of the keys in the DA does not matter.
        """
        key_arr = DynamicArray()

        # Traverse the hash map. If a bucket is not empty, walk through the chained linked list
        # to retrieve each element's key
        for i in range(self.buckets.length()):
            bucket = self.buckets.get_at_index(i)
            if bucket is not None:
                cur = bucket.head
                while cur is not None:
                    key_arr.append(cur.key)
                    cur = cur.next
        return key_arr