def _post_order_helper(node_lst: List[ReadNode], root_index: int, flag: bool = True, right_index: int = 0) -> HuffmanTree: """ A helper function that generates a tree based on <node_lst> ReadNodes, and uses <root_index> and <flag> and <right_index> to do so. """ # if internal node if node_lst[root_index].l_type == 1 and flag: # Making Tree tree = HuffmanTree(None) tree.number = root_index - 1 - right_index # Creating Left and Right Trees tree.right = _post_order_helper(node_lst, tree.number, False) right_index = _find_height(tree.right) if right_index is None: right_index = 0 else: right_index = len(right_index) tree.left = _post_order_helper( node_lst, tree.number, True, right_index) return tree elif node_lst[root_index].r_type == 1 and not flag: # Making Tree tree = HuffmanTree(None) tree.number = root_index - 1 # Creating Left and Right Trees tree.right = _post_order_helper(node_lst, tree.number, False) right_index = _find_height(tree.right) if right_index is None: right_index = 0 else: right_index = len(right_index) tree.left = _post_order_helper( node_lst, tree.number, True, right_index) return tree elif node_lst[root_index].l_type == 0 and flag: return HuffmanTree(node_lst[root_index].l_data) elif node_lst[root_index].r_type == 0 and not flag: return HuffmanTree(node_lst[root_index].r_data) return HuffmanTree(None)
def number_nodes(tree: HuffmanTree) -> None: """ Number internal nodes in <tree> according to postorder traversal. The numbering starts at 0. >>> leftleft = HuffmanTree(None, HuffmanTree(4), HuffmanTree(12)) >>> left = HuffmanTree(None, leftleft, HuffmanTree(2)) >>> right = HuffmanTree(None, HuffmanTree(9), HuffmanTree(10)) >>> tree = HuffmanTree(None, left, right) >>> number_nodes(tree) >>> tree.left.left.number 0 >>> tree.left.number 1 >>> tree.right.number 2 >>> tree.number 3 """ curr_number = 0 list_of_nodes = [] dict_of_nodes = find_internal_nodes(tree, 0) for el in dict_of_nodes: list_of_nodes.append(el) list_of_nodes.reverse() for el in list_of_nodes: for tree in dict_of_nodes[el]: tree.number = curr_number curr_number += 1
def _clean_huff_tree(tree: HuffmanTree): """ convert all HuffmanTree.number to None after using it to keep list sorted """ if tree: _clean_huff_tree(tree.left) _clean_huff_tree(tree.right) tree.number = None
def _number_nodes_helper(tree: HuffmanTree, number: int = 0) -> int: """ A helper function that uses a <tree> to <number> the internal nodes. """ if tree.is_leaf(): return number - 1 else: number = _number_nodes_helper(tree.left, number) + 1 number = _number_nodes_helper(tree.right, number) + 1 tree.number = number return number
def _gen_tree_helper(node_lst: List[ReadNode], root_index: int, flag: bool = True) -> HuffmanTree: """ A helper function that generates a tree based on <node_lst> ReadNodes, and uses <root_index> and <flag> to do so. """ # if internal node if node_lst[root_index].l_type == 1 and flag: # Making Tree tree = HuffmanTree(None) tree.number = node_lst[root_index].l_data # Creating Left and Right Trees tree.left = _gen_tree_helper(node_lst, tree.number, True) tree.right = _gen_tree_helper(node_lst, tree.number, False) return tree elif node_lst[root_index].r_type == 1 and not flag: # Making Tree tree = HuffmanTree(None) tree.number = node_lst[root_index].r_data # Creating Left and Right Trees tree.left = _gen_tree_helper(node_lst, tree.number, True) tree.right = _gen_tree_helper(node_lst, tree.number, False) return tree elif node_lst[root_index].l_type == 0 and flag: return HuffmanTree(node_lst[root_index].l_data) elif node_lst[root_index].r_type == 0 and not flag: return HuffmanTree(node_lst[root_index].r_data) return HuffmanTree(None)
def _recursive_numbering(tree: HuffmanTree, cur_num: list): """ Algorithm that recursively builds and mutates the huff_map """ if tree.is_leaf(): # dont number leaves return elif not tree: # blank tree, graceful termiantion return else: _recursive_numbering(tree.left, cur_num) # then right _recursive_numbering(tree.right, cur_num) # finally root tree.number = cur_num[0] cur_num[0] += 1
def _post_order_set_none(tree: HuffmanTree) -> None: """" Sets all the internal nodes of a <tree> to None. >>> left = HuffmanTree(None, HuffmanTree(3, None, None), \ HuffmanTree(2, None, None)) >>> right = HuffmanTree(5) >>> tree = HuffmanTree(None, left, right) >>> _print_postorder(tree) >>> tree = build_huffman_tree(build_frequency_dict(b"helloworld")) >>> number_nodes(tree) >>> _print_postorder(tree) """ if tree: _post_order_set_none(tree.left) _post_order_set_none(tree.right) tree.number = None
def number_nodes(tree: HuffmanTree) -> None: """ Number internal nodes in <tree> according to postorder traversal. The numbering starts at 0. >>> left = HuffmanTree(None, HuffmanTree(3), HuffmanTree(2)) >>> right = HuffmanTree(None, HuffmanTree(9), HuffmanTree(10)) >>> tree = HuffmanTree(None, left, right) >>> number_nodes(tree) >>> tree.left.number 0 >>> tree.right.number 1 >>> tree.number 2 """ # cant be recursive because different in/out\ # use list so we can mutate value if tree.is_leaf(): # leafs arent given a number tree.number = None else: _recursive_numbering(tree, [0])
def build_huffman_tree(freq_dict: Dict[int, int]) -> HuffmanTree: """ Return the Huffman tree corresponding to the frequency dictionary <freq_dict>. Precondition: freq_dict is not empty. >>> freq = {104: 1, 101: 1, 108: 3, 111: 2, 119: 1, 114: 1, 100: 1} >>> t = build_huffman_tree(freq) # do something with this! >>> freq = {2: 6, 3: 4, 7: 5} >>> t = build_huffman_tree(freq) >>> result = HuffmanTree(None, HuffmanTree(2), \ HuffmanTree(None, HuffmanTree(3), HuffmanTree(7))) >>> t == result True >>> import random >>> symbol = random.randint(0,255) >>> freq = {symbol: 6} >>> t = build_huffman_tree(freq) >>> any_valid_byte_other_than_symbol = (symbol + 1) % 256 >>> dummy_tree = HuffmanTree(any_valid_byte_other_than_symbol) >>> result = HuffmanTree(None, HuffmanTree(symbol), dummy_tree) >>> t.left == result.left or t.right == result.right True >>> freq = {2: 6, 3: 4} >>> t = build_huffman_tree(freq) >>> result = HuffmanTree(None, HuffmanTree(3), HuffmanTree(2)) >>> t == result True """ if len(freq_dict) == 1: symbol = list(freq_dict.keys())[0] symbol_2 = random.randint(0, 255) while symbol_2 in freq_dict: # can take infinite time... symbol_2 = random.randint(0, 255) left = HuffmanTree(symbol) right = HuffmanTree(symbol_2) return HuffmanTree(None, left, right) else: huff_list = [] for key in freq_dict: # n time new_huff = HuffmanTree(key) new_huff.number = freq_dict.get(key) huff_list.append(new_huff) while len(huff_list) > 1: # n time # sort list to easily get 2 lowest _huffman_insertion_sort(huff_list) # get lowest 2 low_0 = huff_list.pop(0) low_1 = huff_list.pop(0) new_huffman = HuffmanTree(None, low_0, low_1) # left has higher fre new_huffman.number = low_0.number + low_1.number # add it huff_list.append(new_huffman) _clean_huff_tree(huff_list[0]) return huff_list[0]
def __number_nodes_helper(tree: HuffmanTree, ranger: list) -> None: """Helper to number_nodes""" if tree is not None and tree.left and tree.right: __number_nodes_helper(tree.left, ranger) __number_nodes_helper(tree.right, ranger) tree.number = ranger.pop(0)