树状结构:树的基本概念、N叉树、二叉树、二叉搜索树、AVL树(平衡树)、伸展树、B树、最大/最小堆(优先队列)、Huffman编码、广义表;

C语言实现模板:N叉树、二叉树、二叉搜索树、AVL树(平衡树)、最大/最小堆
本文为本人学习过程中整理的学习笔记,想顺带学英语所以用英文呈现。发现错误还烦请指正。欢迎交流。

未经同意,请勿转载。

文章目录

  • 未经同意,请勿转载。
  • Trees
    • Definition
    • Basis Concepts
      • Average Search Time
    • N-ary Tree
      • Implementation
    • Binary Tree
      • Traversal
        • Height & Depth
      • Implementation
    • Binary Search Tree (BST)
      • Max & Min
      • Removal
      • Implementation
    • AVL Tree
      • Maintain Operation
        • Single Rotation
        • Double Rotation
      • Implementation
    • Splay Tree
      • Implementation
    • B Tree
      • Insertion
      • Removal
    • Heap
      • Priority Queue
        • Limitation of Simple Implementation
      • Binary Heap
        • Implementation
      • d-Heap
    • Huffman Coding
      • Weighted Path Length (WPL)
      • Optimal Tree and Huffman Codes
      • Huffman's Algorithm
    • General List
      • Definition
      • Representation
      • Pseudocode

Trees

Definition

(Recursive)

A tree is a collection of nodes. The collection can be empty; otherwise, a tree consists of
(1) a distinguished node rrr, called the root;
(2) and zero or more nonempty (sub)trees T1,T2,...,TkT_1, T_2, ..., T_kT1​,T2​,...,Tk​ , each of whose roots are connected by a directed edge from rrr.

Basis Concepts

  • Degree of a node

    number of subtrees of the node.

  • Degree of a tree
    Degreetree=max⁡nodei∈tree{Degreenodei}Degree_{tree} = \max_{node_i \in tree}\{Degree_{node_i}\} Degreetree​=nodei​∈treemax​{Degreenodei​​}

  • Parent

  • Child

  • Sibling(s)

  • Leaf (Terminal node)

    node with 0 degree

  • Path from n1n_1n1​ to nkn_knk​

    a (unique) sequence of nodes n1,n2,…,nkn_1, n_2, …, n_kn1​,n2​,…,nk​ such that nin_ini​ is the parent of ni+1n_{i+1}ni+1​ for 1≤i<k1 \le i < k1≤i<k.

  • Length of path

    number of edges on the path

  • Depth of nin_ini​

    length of the unique path from the root to nin_ini​. Depth(root) = 0.

  • Height of nin_ini​

    length of the longest path from nin_ini​ to a leaf. Height(leaf) = 0.

  • Height (Depth) of a tree

    height(root) = depth(deepest leaf).

  • Ancestor of a node

    all the nodes along the path from the node up to the root.

  • Descendants(后裔) of a node

    all the nodes in its subtrees.

Average Search Time

And the depth DiD_{i}Di​ of a root node iii equals 0.

The search time of a node TiT_iTi​ is defined as Ti=Di+1T_i = D_i + 1Ti​=Di​+1. (the search time of a root is 1)

Then the average search time (AST) of a tree is define as follow
AST=1N∑nodei∈treeTi\text{AST} = \frac{1}{N}\sum_{node_i \in tree} T_i AST=N1​nodei​∈tree∑​Ti​

N-ary Tree

Implementation

  • Properties

    typedef struct _tnode{ElementType val;struct _tnode *next_sibling;struct _tnode *first_child;
    } TNode, *Tree;
    
  • Constructor

    DO WE NEED A SENTINEL IN FIRST-CHILD & NEXT-SIBLING LINK LSITS?

    – NO. If doing this, we need as many dummy nodes (sentinel) as leaves of the tree, which will bring too much cost on space just for coding convenience. (A link-lists only need one sentinel)

    Tree create_tree(ElementType val){Tree root = (Tree)malloc(sizeof(TNode));root->val = val;root->next_sibling = NULL;root->first_child = NULL;return root;
    }
    
  • Destructor

    void delete_tree(Tree root){postorder_traversal(root, (void (*)(TNode*))free));
    }
    
  • Insertion (Building Tree)

    // Add a new node to the root as a child, return the pointer of the new node
    TNode* add_node(ElementType val, Tree root){if(root == NULL)return NULL;TNode *node = create_tree(val);// consider whether the root has child or notif(root->first_child){TNode *pos;for(pos = root->first_child; pos->next_sibling; pos = pos->next_sibling);pos->next_sibling = node;}else{root->first_child = node;}return node;
    }
    
  • Removal

  • Preorder Traversal

    Same loop for in/post-order traversal.

    void preorder_traversal(Tree root, void (*visit)(TNode*)){if(root == NULL)return ;(*visit)(root); // any operation// recursion on every child of the rootTNode *pos;for(pos = root->first_child; pos; pos = pos->next_sibling){preorder_traversal(pos);}
    }
    
  • Level-order Traversal (BFS)

    void bfs(Tree root, void (*visit)(TNode*)){queue<TNode*> q;q.enqueue(root);TNode *pos, *node;while(!q.empty()){node = q.dequeue();for(pos = node->first_child; pos; pos = pos->next_sibling)q.enqueue(pos);(*visit)(node); // any operation}
    }
    

Binary Tree

Traversal

Pre/In/Post/Level-order traversal of binary tree share the same routines with n-ary trees, except visiting only left and right subtrees instead of scanning all children linearly.

Height & Depth

With storing and maintaining height/depth in nodes, get_height can be implemented by postoreder traversal while get_depth can be implemented by preorder traversal.

Implementation

  • Properties

    typedef struct _tnode{ElementType val;struct _tnode *left, *right;
    } TNode, *BiTree;
    
  • Constructor

    Alternative: Adding allocation error check. Example is in comment.

    AVLTree create_bitree(ElementType root_val){TNode *node = (TNode*)malloc(sizeof(TNode));// if(node == NULL){//     AllocationError();//     return NULL;// }node->val = root_val;node->left = NULL;node->right = NULL;return node;
    }
    
  • Destructor

    void delete_bitree(AVLTree root){if(root == NULL)return ;delete_bitree(root->left);delete_bitree(root->right);free(root);
    }
    
  • Print Tree in 90 Degree Rotation

    #define IDENTATION 4void rotate_90(AVLTree root, int depth){if(root == NULL)return ;rotate_90(root->right, depth+1);int i;int num_space = depth * IDENTATION;for(i = 0; i < num_space; i++)printf(" ");PRINT(root->val); // print the noderotate_90(root->left, depth+1);return ;
    }void print_bitree(AVLTree root){// package interface (hide depth)rotate_90(root, 0);return ;
    }
    

Binary Search Tree (BST)

Max & Min

The leftmost node in a BST has the smallest value while the rightmost one has the largest value.

Removal

  • Delete a leaf node : Reset its parent link to NULL.
  • Delete a degree 1 node : Replace the node by its single child.
  • Delete a degree 2 node :
    • Replace the node by the largest one in its left subtree or the smallest one in its right subtree.
    • Delete the replacing node from the subtree.

Implementation

  • Properties

    The member of bst structures are the same as vanilla binary tree.

    typedef BiTree BST;
    
  • Constructor / Destructor

    Same as binary tree’s.

    BST create_bst(ElementType val){return create_bitree(val);
    }void delete_bst(BST root){delete_bitree(root);
    }
    
  • Insertion

    Alternative: Adding allocation error check. Example is in comment.

    It’s easy to implement iterative version with a moving pointer.

    Recursive Version

    // Return the root of new tree after insertion
    AVLTree add_node(ElementType val, BST root){if(root == NULL){root = create_avl(val);// if(root == NULL)//     AllocationError();}else{if(val > root->val)add_node(root->right);else if (val < root->val)add_node(root->left);// do nothing if val == root->valelse;}return root;
    }
    
  • Finding Min & Max

    ddd : depth of binary tree

    Iterative Example

    time complexity: O(d)O(d)O(d) , space complexity: O(d)O(d)O(d)

    // Return null for empty tree (root == null).
    TNode* find_max(AVLTree root){TNode *pos_max = root;while(pos_max && pos_max->right)pos_max = pos_max->right;return pos_max;
    }
    

    Recursive Example

    time complexity: O(d)O(d)O(d) , space complexity: O(d)O(d)O(d)

    // Return null for empty tree (root == null).
    TNode* find_min(AVLTree root){if(root == NULL)return NULL;return (root->left) ? find_min(root->left) : root;
    }
    
  • Removal

    Recursive Version

    The code shown is inefficient, because it makes two passes down the tree using find_min and remove to delete the smallest node in the right subtree, which both take O(d)O(d)O(d). It is easy to reduce this inefficiency by writing a function completing two operations in one pass down.

    Meanwhile, the recursive implementation takes O(d)O(d)O(d) extra space which you can also reduce it by changing the recursion to iteration with little more work.

    // Return the root of new tree after removal.
    // Return NULL if target is not found.
    // call: root = remove(target, root);
    BST remove(ElementType target, BST root)
    {    // if ( T == NULL ){//     Error( "Element not found" ); //     return NULL;// }BST pos = root;if(target < pos->val)root->left = remove(target, root->left); else if(target > pos->val)root->right = remove(target, root->right);else{// two children: Replace with smallest in right subtree.if(root->left && root->right){TNode *pos_min = find_min(root->right);root->val = pos_min->val;root->right = remove(root->val, root->right);}// one or zero childelse{TNode *to_del = root;if(root->left == NULL) // also handles 0 child in this branchroot = root->right;else if(root->right == NULL)root = root->left;free(root);}}return root;
    }
    

AVL Tree

An AVL tree is identical to a binary search tree, except that for every node in the tree, the height of the left and right subtrees can differ by at most 1.

  • The height of an empty tree is defined to be -1.

  • Height information is kept for each node.

It is easy to show that the height of an AVL tree is at most roughly 1.44log(n+2)−0.3281.44 log(n + 2) - 0.3281.44log(n+2)−0.328, but in practice it is about log(n+1)+0.25log(n + 1) + 0.25log(n+1)+0.25 (although the latter claim has not been proven). (proof of the former one?)

Thus, all the tree operations can be performed in O(log⁡N)O(\log N)O(logN) time, except possibly insertion and deletion (lazy deletion will be O(log⁡N)O(\log N)O(logN)).

Maintain Operation

Single Rotation

  • Maintaining BST property: X<k1<Y<k2<ZX < k_1 < Y < k_2 < ZX<k1​<Y<k2​<Z

  • Fixing AVL property: left tree ↔\leftrightarrow↔ right tree

If an insertion causes a node (k2k_2k2​ in left →\to→ right) in an AVL tree to lose the balance property, do a rotation at that node to fix it up .

The basic algorithm is to start at the node inserted and travel up the tree, updating the balance information at every node on the path. Do a rotation at the first bad node found to adjust its balance, and then stop travelling up. Essentially, what the rotation does is exchanging subtrees to move the taller subtree of the imbalanced node to the layer where the node itself is.

  1. RR Rotation (right →\to→ left)

    The rotation should be done after RR insertion, which is the insertion of a trouble node on the right subtree of the right subtree of the node that discovers imbalance.

    In right figure, k1k_1k1​ discovers imbalance when a trouble node inserts to ZZZ (the left-left subtree of k1k_1k1​). The operation focuses on modifying the positions of k1,k2k_1, k_2k1​,k2​.

  2. LL Rotation

    The rotation should be done after LL insertion, which is the insertion of a trouble node on the left subtree of the left subtree of the node that discovers imbalance.

    In left figure, k2k_2k2​ discovers imbalance when a trouble node inserts to XXX (the left-left subtree of k2k_2k2​). The operation focuses on modifying the positions of k1,k2k_1, k_2k1​,k2​.

Double Rotation

The problem occurs when a node inserts into the tree containing the middle elements (YYY) at the same time as the other tree (ZZZ) had identical heights.

Therefore we need double rotation to fix this.

Assume k2k_2k2​ loses balance:

  1. RL Rotation

    The rotation should be done after RL insertion, which is the insertion of a trouble node on the left subtree of the right subtree of the node that discovers imbalance.

    In right figure, k3k_3k3​ discovers imbalance when a trouble node inserts to k2k_2k2​ (the right-left subtree of k3k_3k3​) on BBB or CCC. The operation focuses on modifying the positions of k1,k2,k3k_1, k_2, k_3k1​,k2​,k3​.

  2. LR Rotation

    The rotation should be done after LR insertion, which is the insertion of a trouble node on the right subtree of the left subtree of the node that discovers imbalance.

    In right figure, k3k_3k3​ discovers imbalance when a trouble node inserts to k2k_2k2​ (the left-right subtree of k3k_3k3​) on BBB or CCC. The operation focuses on modifying the positions of k1,k2,k3k_1, k_2, k_3k1​,k2​,k3​.

Implementation

NOTICE:

Whenever you update a height of node, don’t forget to PLUS ONE!
updated height=max⁡{subtree heights}+1\text{updated height} = \max\{\text{subtree heights}\} + 1 updated height=max{subtree heights}+1
OPTION:

You can choose either store the height or balance factor (fb=hL−hRf_b = h_L - h_Rfb​=hL​−hR​) of each node.

If using balance factor, you need to check ∣fb∣=2|f_b| = 2∣fb​∣=2 before doing rotation. But you still need to use get_height function to calculate and update balance factor.

  • Properties

    typedef struct _tnode{ElementType val;int height;struct _tnode *left, *right;
    } TNode, *AVLTree;
    
  • Constructor

    AVLTree create_avl(ElementType root_val){TNode *node = (TNode*)malloc(sizeof(TNode));node->val = root_val;node->height = 0;node->left = NULL;node->right = NULL;return node;
    }
    
  • Destructor

    void delete_avl(AVLTree root){if(root == NULL)return ;delete_avl(root->left);delete_avl(root->right);free(root);
    }
    
  • Height

    // the height of an empty tree (root == null) is defined to be -1
    int get_height(AVLTree root){return root ? root->height : -1;
    }
    
  • Single Rotation

    #define max(a,b) (((a)>(b))?(a):(b))// Return the new root
    // call: root = rotation(root);
    AVLTree rr_rotation(AVLTree root){// Rotate, then maintain heights of two nodes(new & old roots).// Caller should guarantee that root has a right subtree.AVLTree new_root = root->right;root->right = new_root->left;new_root->left = root;root->height = max(get_height(root->left), get_height(root->right)) + 1;new_root->height = max(get_height(new_root->left), get_height(new_root->right)) + 1;return new_root;
    }AVLTree ll_rotation(AVLTree root){AVLTree new_root = root->left;root->left = new_root->right;new_root->right = root;root->height = max(get_height(root->left), get_height(root->right)) + 1;new_root->height = max(get_height(new_root->left), get_height(new_root->right)) + 1;return new_root;
    }
    
  • Double Rotation

    a. Simple Version

    AVLTree rl_rotation(AVLTree root){root->right = ll_rotation(root->right);return rr_rotation(root);
    }AVLTree lr_rotation(AVLTree root){root->left = rr_rotation(root->left);return ll_rotation(root);
    }
    

    b. Efficient Version

    The version without the inefficiency of doing two single rotations.

    AVLTree RL_rotation(AVLTree root){TNode *new_root = root->right->left;root->right->left = new_root->right;new_root->right = root->right;root->right = new_root->left;new_root->left = root;return new_root;
    }AVLTree LR_rotation(AVLTree root){TNode *new_root = root->left->right;root->left->right = new_root->left;new_root->left = root->left;root->left = new_root->right;new_root->right = root;return new_root;
    }
    
  • Insertion

    Notice: We don’t need to do if check when doing insertion to decide whether call create_avl in current layer or add_node to dive in next layer. Calling add_node in current layer covers both situations.

    // call: root = add_node(val, root);
    // Skip allocation error check for clarity of avl related algorithmAVLTree add_node(ElementType val, AVLTree root){// In each branch, do insertion firstly,// then check for rotation need.// Return the new tree root (considering the null input root).if(root == NULL)return create_avl(val);if(val > root->val){root->right = add_node(val, root->right);if(get_height(root->right) - get_height(root->left) == 2){if(val > root->right->val)root = rr_rotation(root);elseroot = rl_rotation(root);}}else if(val < root->val){root->left = add_node(val, root->left);if(get_height(root->right) - get_height(root->left) == -2){if(val < root->left->val)root = ll_rotation(root);elseroot = lr_rotation(root);}}// do nothing if val is already in avltreeelse;// DON'T FORGET TO PLUS ONE ON THE MAXIMUMroot->height = max(get_height(root->left), get_height(root->right)) + 1;return root;
    }
    

Splay Tree

Any MMM consecutive(连续的) tree operations take at most O(Mlog⁡N)O(M\log N)O(MlogN) time, which means the amortized(分期偿还) time is log⁡N\log NlogN.

Idea: After a node is accessed, it will be pushed to the root by a series of AVL rotations.

As the figure shown, in case 2, when XXX is on its grandparent’s LR (left-right) or RL (right-left), a double rotation (LR or RL) will be applied on GGG. But when XXX is on its grandparent’s LL (left-left) or RR (right-right), two single rotations (LL or RR) will be applied – one on GGG firstly, another on PPP secondly.

What splaying tree do is keeping using the rotations until XXX becomes root of the tree. Each rotate operation means XXX moves 2 layers upwards, which mean the operation roughly reaches log⁡N\log NlogN.

Informally, splaying not only moves the accessed node to the root, but also roughly halves the depth of most nodes on the path (this may not be observed easily in a more balanced BST). And some shallow nodes are pushed down at most two levels. This crucial property of splay tree shows when access paths are long, thus leading to a longer-than-normal search time, the rotations tend to be good for future operations while when accesses are cheap, the rotations are not as good and can be bad.

Compared with AVL tree, splay tree is easier to code (without considering too many special conditions) and has no need for storing the balance factor (or height) in each tree node.

Implementation

  • Building and Maintaining

    The rotations for splay trees are performed in pairs from the bottom up, a recursive implementation DOES NOT work. The pairs of nodes to consider are not known until the length of the path is determined to be even or odd. Thus, splay trees are coded non-recursively and work in two passes. The first pass goes down the tree and the second goes back up, performing rotations. This requires that the path be saved (maybe by using a stack or by adding an extra field to the node record that will point to the parent.).

  • Removal

    The deletion is performed by accessing the node to be removed. This puts the node at the root. If it is deleted, we get two subtrees TLT_LTL​ and TRT_RTR​ (left and right). If we find the largest node in TLT_LTL​, then this node will be rotated to the root of TLT_LTL​, and TLT_LTL​ will now have a root with no right child. We can finish the deletion by making TRT_RTR​ the right child.

B Tree

A B-tree of order m is a tree with the following structural properties:

  • The root is either a leaf or has between 222 and mmm children.
  • All non-leaf nodes (except the root) have between ⌈m/2⌉\lceil m/2 \rceil⌈m/2⌉ and mmm children.
  • All leaves are at the same depth.

A B-tree of order 4 is more popularly known as a 2-3-4 tree, and a B-tree of order 3 is known as a 2-3 tree.

All the actual data are stored in leaves in ascending order.

The illustration can be drawn as follow.

Insertion

With general B-trees of order mmm, when a key is inserted to a node already has m keys. This key gives the node m+1m + 1m+1 keys, which we can split into two nodes with ⌈(m+1)/2⌉\lceil (m + 1) / 2 \rceil⌈(m+1)/2⌉ and ⌊(m+1)/2⌋\lfloor (m + 1) / 2 \rfloor⌊(m+1)/2⌋ keys respectively. As this gives the parent an extra node, we have to check whether this node can be accepted by the parent and split the parent if it already has mmm children. We repeat this until we find a parent with less than mmm children. If we split the root, we create a new root with two children.

Removal

If the key to be deleted was one of only two keys in a node, then its removal leaves only one key (which break the property of a B-Tree). We can fix this by combining this node with a sibling. If the sibling has three keys, we can steal one and have both nodes with two keys. If the sibling has only two keys, we combine the two nodes into a single node with three keys. The parent of this node now loses a child, so we might have to percolate(渗透) this strategy all the way to the top. If the root loses its second child, then the root is also deleted and the tree becomes one level shallower. As we combine nodes, we must remember to update the information kept at the internal nodes.

Heap

Priority Queue

Limitation of Simple Implementation

If using binary search tree, the search efficiency will drop steeply because of the unbalance brought by a sequence of deletions.

If using balance tree, the implementation will includes extra operations to maintain the balance properties which are unrelated to the properties of a priority queue.

Binary Heap

  • Structure Properties

    A heap is a binary tree that is completely filled, with the possible exception of the bottom level, which is filled from left to right. Such a tree is known as a complete binary tree.
    Assuming the height of a tree with only the root node is 0, it is easy to show that a complete binary tree of height hhh has number of node in [2h,2h+1−1][2^h, 2^{h+1}-1][2h,2h+1−1].

    This implies(说明) that the height of a complete binary tree is ⌊log⁡n⌋\lfloor \log n \rfloor⌊logn⌋, which is clearly O(log⁡n)O(\log n)O(logn).

    An important observation is that because a complete binary tree is so regular, it can be represented in an array and no pointers are necessary.

  • Order Properties

    In a max (min) heap, its root is the max (mini) node of all elements with two max (min) heaps as subtrees, which means every node having a larger (smaller) key than its descendants have.

Implementation

  • Properties

    typedef struct{ElementType *data;int size, capacity;int (*cmp)(ElementType, ElementType);
    } _heap, *Heap;// Comparison functions defining the relative size of elements to pass.
    // Notice: x-y may cause overflowing
    int cmp_max(ElementType a, ElementType b){return (a > b) ? 1 : (a < b) ? -1 : 0;
    }int cmp_min(ElementType a, ElementType b){return (a > b) ? -1 : (a < b) ? 1 : 0;
    }
    
  • Constructor

    Heap create_heap(int capacity, int (*cmp_func)(ElementType, ElementType)){Heap h = (Heap)malloc(sizeof(_heap));// Remember the capacity should be 1 larger than input because the index 1 cell will be empty or sentinel.h->data = (ElementType*)calloc(capacity + 1, sizeof(ElementType));h->capacity = capacity;h->size = 0;h->cmp = cmp_func;return h;
    }
    
  • Destructor

    void delete_heap(Heap h){free(h->data);free(h);
    }
    
  • Size

    int get_size(Heap h){return h->size;
    }int is_full(Heap h){return get_size(h) == h->capacity;
    }int is_empty(Heap h){return get_size(h) == 0;
    }
    
  • Built (in place)

    T(N)=O(Nlog⁡N)T(N) = O(N\log N)T(N)=O(NlogN)

    Heap Sort

    // percolate (the cell with index p) down
    void perc_down(Heap h, int p){int parent, child;ElementType x;x = h->data[p];for(parent = p; parent*2 <= h->size; parent = child){child = parent * 2;if(child != h->size && h->cmp(h->data[child+1], h->data[child]) > 0)child++;if(h->cmp(x, h->data[child]) >= 0)break;elseh->data[parent] = h->data[child];}h->data[parent] = x;
    }void build_heap(Heap h){int i;for(i = h->size / 2; i > 0; i--)perc_down(h, i);
    }
    
  • Insertion

    T(N)=O(log⁡N)T(N) = O(\log N)T(N)=O(logN)

    “Put” the new element into the end of the array (tree), then percolate it up to the correct position.

    In order to reach child using i/2, cell at index 0 doesn’t store data.

    Additionally, we can use it as a sentinel by assigning a bound value to it, after which we can reduce i > 1 check in every loop.

    void insert(ElementType val, Heap h){if(is_full(h)){printf("insert Error: Heap is full.\n");return ;}// cell with index 0 does not store dataint p;for(p = ++h->size; p > 1 && h->cmp(val, h->data[p/2]) > 0; p /= 2)h->data[p] = h->data[p/2];h->data[p] = val;
    }
    
  • Peek Top

    ElementType get_top(Heap h){if(is_empty(h)){printf("get_top Error: Heap is empty.\n");return h->data[0];}  return h->data[1];
    }
    
  • Pop (Max/Min)

    T(N)=O(log⁡N)T(N) = O(\log N)T(N)=O(logN)

    Shift the element in the last cell to the top, then percolate it down.

    ElementType del_top(Heap h){if(is_empty(h)){printf("del_top Error: Heap is empty.\n");return h->data[0]; // return the sentinel}ElementType top_elem = get_top(h);h->data[1] = h->data[h->size--];perc_down(h, 1);return top_elem;
    }
    

d-Heap

Huffman Coding

Weighted Path Length (WPL)

WPL of a tree is defined to be the sum of the weighted depths of its all leaves.
WPL=∑nodei∈leaveswidiWPL = \sum_{node_i \in leaves} w_i d_i WPL=nodei​∈leaves∑​wi​di​

Optimal Tree and Huffman Codes

Optimal tree is a full tree with smallest WPL of a specific set of characters to be coded.

(full tree: All nodes either are leaves or have two children. An optimal code will
always have this property.)

All characters are represented at leaves, which means any sequence of bits can always be decoded unambiguously. Thus, it does not matter if the character codes are different lengths, as long as no character code is a prefix of another character code. Such an encoding is known as a prefix code.

For one set of characters, there is more than one optimal Huffman coding tree.

Huffman’s Algorithm

Maintain a forest of trees. The weight of a tree is equal to the sum of the frequencies of its leaves. C−1C - 1C−1 times, select the two trees, T1T_1T1​ and T2T_2T2​, of smallest weight, merge them together to form a new tree whose weight is the sum of two subtree weights.

At the beginning of the algorithm, there are CCC single-node trees – one for each character. At the end of the algorithm there is one tree, which is the optimal Huffman coding tree.

In the building of Huffman tree, we merely select the two smallest trees in each round. If we maintain the trees in a priority queue (min heap), ordered by weight, then the running time is O(Clog⁡C)O(C \log C)O(ClogC), since there will be one build_heap, 2C−22C - 22C−2 delete_mins, and C−2C - 2C−2 inserts, on a priority queue that never has more than CCC elements.

  • Decoding: FSA (Finite State Automaton)

General List

Definition

LS=(α1,α2,...,,αn)LS = (\alpha_1, \alpha_2, ..., , \alpha_n) LS=(α1​,α2​,...,,αn​)

where αi\alpha_iαi​ is atom (element) or sub-list (general list).

  • List name: LSLSLS

  • List head: α1\alpha_1α1​, which is a single element.

  • List tail: (α2,...,,αn)(\alpha_2, ..., , \alpha_n)(α2​,...,,αn​), which is defined as a general list of the rest elements.

  • List length: nnn

  • Depth: Depth of parenthesis nesting. Depth of atom is defined as 000 and depth of an empty general list is defined to be 111.

When every element in the general list is atom in the same type, the general list actually degenerates into a linear list.

Representation

Pseudocode

void create_tree(Tree t, GList L){// Create root nodecreate_root(t, head(L));if(tail(L) is not empty){// Create the first childcur_head = head(tail(L));create_tree(t->firstchild, curhead);// iteratively create childrencur_tail = tail(tail(L));pos = t->firstchild;while(cur_child){cur_head = head(cur_tail);create_tree(p->nextsibling, cur_head);cur_tail = tail(cur_tail);p = p->next_sibling;}p->next_sibling = NULL;}
}

【DSA_Fall2020】2. Trees (Templates in C)相关推荐

  1. wilf tree java_伴读 | 牛津树【2-9】New Trees

    原标题:伴读 | 牛津树[2-9]New Trees Hello,大家好,我是娜塔莉. 今天,绘本的题目是:"New Trees" New Tree 新的树 tree 树,今天故事 ...

  2. 【有根树】Rooted Trees C++

    题目来源:Aizu - ALDS1_7_A 题目: A graph G = (V, E) is a data structure where V is a finite set of vertices ...

  3. 【solidworks】此文档 templates\gba0.drwdot 使用字体长仿宋体,而该字体不可用

    一.问题背景 在SolidWorks中绘制工程图纸时,新建一个图纸,打开后就弹出字体错误 此文档 templates\gba0.drwdot 使用字体长仿宋体,而该字体不可用. 二.解决办法 点击选择 ...

  4. 【LeetCode】深搜DFS(共85题)

    [98]Validate Binary Search Tree [99]Recover Binary Search Tree [100]Same Tree [101]Symmetric Tree [1 ...

  5. 暑假N天乐【比赛篇】 —— 2019牛客暑期多校训练营(第三场)

    这场相对来说友好一点,本来前几天就补差不多了,然后忘记发了... 以下题解包括:\(A \ \ \ B \ \ \ F \ \ \ G \ \ \ H \ \ \ J\) \(D\) 题队友补了,我也 ...

  6. 【IntelliJ】IntelliJ IDEA常用设置及快捷键以及自定义Live templates

    IntelliJ IDEA是一款非常优秀的JAVA编辑器,初学都可会对其中的一些做法感到很别扭,刚开始用的时候我也感到很不习惯,在参考了网上一些文章后在这里把我的一些经验写出来,希望初学者能快速适应它 ...

  7. 【Elasticsearch】Elasticsearch 动态模板(Dynamic templates)

    1.概述 动态映射请参考: [Elasticsearch]Elasticsearch 7 : 动态映射 dynamic 本博客摘抄自:Elastic Stack 实战手册(早鸟版).pdf 原文可看, ...

  8. 【Codeforces Round #548(Div. 2)】Edgy Trees(数学+bfs求连通块)

    题目链接 C. Edgy Trees time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  9. CodeForces 711C.Coloring Trees【DP】

    看上去就是DP的一个题,由于自己太菜了,还是不会做 先给个提交的地方:cf 711C 这个题看到数据,很明显是dp,因为n,m,k的值都不大,我们可以建立矩阵来推理 很明显答案跟dp[n][k]有关 ...

最新文章

  1. 【收藏】网络故障处理手册大全,看完再也不怕出问题了
  2. 前端学习(2982):实现商品功能列表
  3. matlab出错及解决办法,Linux下使用Matlab符号函数出错的解决办法
  4. UML(1) - 概述
  5. python技术简介_Python多线程技术简介,简单,阐述,python
  6. php 业务管理,PHPOA集团版协同套件:整合集团业务的管理平台
  7. 如何在微信小程序中实现与客户实时会话(聊天)
  8. springboot +vue实现打印PDF(实现批量打印快递单)
  9. 阿里云吴翰清:我对计算的理解
  10. 商品详情页系统架构-笔记12 - 商品详情页整体架构组成+前端介绍
  11. web语意化的深入理解
  12. Bootstrap4 div居中
  13. OpenWrt——进行PPPoE拨号时透过路由器访问光猫的方法
  14. 使用Selenium爬取网易云音乐的所有排行榜歌曲
  15. OpenGL-曲面细分
  16. [总结]蓝牙各个版本的关系和区别
  17. 霍兰德AI型,高考志愿填报(选专业),霍兰德职业兴趣测试
  18. linux 'pthread_create'未定义的引用,如何解决`_imp__pthread_create'的未定义引用
  19. Python: 异常处理
  20. Elasticsearch创建一个索引怎么也这么复杂

热门文章

  1. 表格布局管理器TableLayout
  2. 窗口全屏化得方法大全(5种方法,你都知道吗?)
  3. 电脑明明有网络,但是就是进不去B站(其他网页可以正常使用)
  4. 老旧笔记本安装openwrt实践:
  5. SSM框架实现插入图片显示图片到JSP界面
  6. 11.7亿、苹果仅20%、1/4的时间看短视频...关于互联网,你必须知道的几个数字......
  7. Linux安装小企鹅输入法
  8. 云主机、云服务器、VPS的区别性能比较
  9. 数据库被置疑后的解决方法
  10. Java中Double与BigDecimal的互转,Date和LocalDateTime互转