Data structure and algorithm (3): tree theory (tree structure, binary tree, binary search tree, red-black tree, Btree&B+Tree, Huffman tree, heap tree)

Tree theory (tree structure, binary tree, binary search tree, red-black tree, Btree, B+Tree, Huffman tree, heap tree)

tree structure concept

Important terms in the tree structure:

  1. Node: The element in the tree.

  2. Parent-child relationship: edges connecting nodes

  3. Subtree: When the node is greater than 1, the rest of the nodes are divided into mutually disjoint sets called subtrees

  4. Degree: The number of subtrees a node has is called the degree of the node

  5. Leaf: a node with degree 0

  6. Child: The root of the subtree of a node is called a child node

  7. Parents: correspond to child nodes

  8. Brother: the same parent node

  9. Forest: A deep forest is composed of N mutually disjoint trees

  10. The height of the node: the longest path from the node to the leaf node

  11. Node depth: the number of edges from the root node to the node

  12. The number of layers of the node: the depth of the node plus 1

  13. Tree height: the height of the root node

insert image description here

binary tree

Binary Tree: A special tree structure, each node has at most two subtrees

2^(N-1)There is at most one node on the Nth level of the binary tree . There is 2^N-1a maximum number of nodes.

Classification:

  • Full binary tree: In addition to leaf nodes, each node has two left and right child nodes.
  • Complete binary tree: Except for the last layer, the number of other nodes must reach the maximum, and the nodes in the last layer are all arranged continuously to the left.

insert image description here

think

Why do we need to divide full binary tree and complete binary tree? Because it can be seen by definition that a complete binary tree is only a subset of a full binary tree

Array: high performance, if it is not a waste of space for a complete binary tree
Linked list: it can also be implemented, the performance is not as high as an array

binary tree traversal

insert image description here

  • Important formula: root node output! subtree
  • Preamble: Root Left and Right (ABCDEFGHK)
  • Inorder: left root right (BCDAEFGHK)
  • Postorder: Left and Right Roots (BCDEFGHKA)
/**
 * 二叉树--前中后链式遍历
 * 前: A B C D E F G H K
 * 中: B C D A E F G H K
 * 后: B C D E F G H K A
 * 层:
 * A
 * B E
 * C F
 * D G
 * H K
 */
@Data
class TreeNode {
    
    
    private char data;
    private TreeNode left;
    private TreeNode right;

    public TreeNode(char data, TreeNode left, TreeNode right) {
    
    
        this.data = data;
        this.left = left;
        this.right = right;
    }
}

public class BinaryTree {
    
    

    public void print(TreeNode node) {
    
    
        System.out.print(node.getData() + " ");
    }

    public void pre(TreeNode root) {
    
     //前序:根(输出) 左 右  A B C D E F G H K
        print(root);
        if (root.getLeft() != null) {
    
    
            pre(root.getLeft()); //认为是子树,分解子问题
        }
        if (root.getRight() != null) {
    
    
            pre(root.getRight());
        }
    }

    public void in(TreeNode root) {
    
     //中序:左 根(输出) 右  B C D A E F G H K
        if (root.getLeft() != null) {
    
    
            pre(root.getLeft()); //认为是子树,分解子问题
        }
        print(root);
        if (root.getRight() != null) {
    
    
            pre(root.getRight());
        }
    }

    public void post(TreeNode root) {
    
     //后序:左 右 根(输出)  B C D E F G H K A
        if (root.getLeft() != null) {
    
    
            pre(root.getLeft()); //认为是子树,分解子问题
        }
        if (root.getRight() != null) {
    
    
            pre(root.getRight());
        }
        print(root);
    }

    public List<List<Character>> level(TreeNode root) {
    
     //层次遍历
        if (root == null) return Collections.EMPTY_LIST;

        List<List<Character>> res = new ArrayList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
    
    
            List<Character> raw = new ArrayList<>();
            int size = queue.size();
            for (int i = 0; i < size; i++) {
    
    
                TreeNode item = queue.poll();
                raw.add(item.getData());
                if (item.getLeft() != null) {
    
    
                    queue.add(item.getLeft());
                }
                if (item.getRight() != null) {
    
    
                    queue.add(item.getRight());
                }
            }
            res.add(raw);
        }
        return res;
    }



    public static void main(String[] args) {
    
    
        TreeNode D = new TreeNode('D', null,null);
        TreeNode H = new TreeNode('H', null,null);
        TreeNode K = new TreeNode('K', null,null);
        TreeNode C = new TreeNode('C', D,null);
        TreeNode G = new TreeNode('G', H,K);
        TreeNode B = new TreeNode('B', null,C);
        TreeNode F = new TreeNode('F', G,null);
        TreeNode E = new TreeNode('E', F,null);
        TreeNode A = new TreeNode('A', B,E);

        BinaryTree tree = new BinaryTree();
        System.out.print("前: ");
        tree.pre(A);
        System.out.println();
        System.out.print("中: ");
        tree.in(A);
        System.out.println();
        System.out.print("后: ");
        tree.post(A);
        System.out.println();
        System.out.println("层: ");
        List<List<Character>> res = tree.level(A);
        for (List<Character> re : res) {
    
    
            for (Character character : re) {
    
    
                System.out.print(character + " ");
            }
            System.out.println();
        }
    }
}

Binary search tree (binary search tree, binary sort number)

  1. If its left subtree is not empty, the value of the node on the left subtree is less than the root node
  2. If its right subtree is not empty, the values ​​of the nodes on the right subtree are greater than the root node
  3. Subtrees also follow the above two points

insert image description here

Inorder traversal - left root (output) right: 0 3 4 5 6 8

performance analysis

  1. find logn
  2. insert nlogn
  3. delete
      1. The node to be deleted is a leaf node O(1)
      1. The node to be deleted has only one subtree (left or right) O(1)
      1. The node to be deleted has two subtrees: find the successor node, and the left subtree of the successor node must be empty logn
/**
 * 二叉搜索树 增删改查
 */
class BinaryNodeTeacher {
    
    
    int data;
    BinaryNodeTeacher left;
    BinaryNodeTeacher right;
    BinaryNodeTeacher parent;

    public BinaryNodeTeacher(int data) {
    
    
        this.data = data;
        this.left = null;
        this.right = null;
        this.parent = null;
    }
}

public class BinarySearchTreeTeacher {
    
    

    public BinaryNodeTeacher find(BinaryNodeTeacher root, int key) {
    
    
        BinaryNodeTeacher current = root;
        while (current != null) {
    
    
            if (key < current.data) {
    
    
                current = current.left;
            } else if (key > current.data) {
    
    
                current = current.right;
            } else {
    
    
                return current;
            }
        }
        return null;
    }

    public void insert(BinaryNodeTeacher root, int data) {
    
    
        if (root.data < data) {
    
    
            if (root.right != null) {
    
    
                insert(root.right, data);
            } else {
    
    
                BinaryNodeTeacher newNode = new BinaryNodeTeacher(data);
                newNode.parent = root;
                root.right = newNode;

            }
        } else {
    
    
            if (root.left != null) {
    
    
                insert(root.left, data);
            } else {
    
    
                BinaryNodeTeacher newNode = new BinaryNodeTeacher(data);
                newNode.parent = root;
                root.left = newNode;
            }
        }
    }

    public BinaryNodeTeacher finSuccessor(BinaryNodeTeacher node) {
    
     // 查找node的后继节点
        if (node.right == null) {
    
     // 表示没有右边 那就没有后继
            return node;
        }
        BinaryNodeTeacher cur = node.right;
        BinaryNodeTeacher pre = node.right; // 开一个额外的空间 用来返回后继节点,因为我们要找到为空的时候,那么其实返回的是上一个节点
        while (cur != null) {
    
    
            pre = cur;
            cur = cur.left; // 注意后继节点是要往左边找,因为右边的肯定比左边的大,我们要找的是第一个比根节点小的,所以只能往左边
        }
        return pre; // 因为cur会变成null,实际我们是要cur的上一个点,所以就是pre来代替
    }

    public BinaryNodeTeacher remove(BinaryNodeTeacher root, int data) {
    
     // 删除data
        BinaryNodeTeacher delNode = find(root, data);
        if (delNode == null) {
    
    
            System.out.println("要删除的值不在树中");
            return root;
        }

        // 1.删除的点没有左右子树
        if (delNode.left == null && delNode.right == null) {
    
    
            if (delNode == root) {
    
    
                root = null;
            } else if (delNode.parent.data < delNode.data) {
    
     // 说明删除的点是右子节点
                delNode.parent.right = null;
            } else {
    
    
                delNode.parent.left = null;
            }
        } else if (delNode.left != null && delNode.right != null) {
    
     // 2.删除的节点有两颗子节点
            BinaryNodeTeacher successor = finSuccessor(delNode); // 先找的后继节点
            // 后继节点和删除节点进行交换,首先后继节点的左节点是肯定为空的
            successor.left = delNode.left; // 后继的左边变为删除的左边
            successor.left.parent = successor; // 删除点的左边parent指向后继节点
            // 再来看后继节点的右边
            if (successor.right != null && successor.parent != delNode) {
    
     // 后继节点有右边,这其实就是下面情况3的第一种
                successor.right.parent = successor.parent;
                successor.parent.left = successor.right;
                successor.right = delNode.right;
                successor.right.parent = successor;
            }else if(successor.right == null) {
    
    	//如果后继节点没有右边,那其实就是情况1,没有左右子树
                if(successor.parent != delNode) {
    
    		//如果后继节点的parent不等于删除的点 那么就需要把删除的右子树赋值给后继节点
                    successor.parent.left = null;		//注意原来的后继节点上的引用要删掉,否则会死循环
                    successor.right = delNode.right;
                    successor.right.parent = successor;
                }
            }
            // 替换做完接下来就要删除节点了
            if (delNode == root) {
    
    
                successor.parent = null;
                root = successor;
                return root;
            }
            successor.parent = delNode.parent;
            if (delNode.data > delNode.parent.data) {
    
     // 删除的点在右边,关联右子树
                delNode.parent.right = successor;
            } else {
    
    
                delNode.parent.left = successor;
            }

        } else {
    
     // 3.删除点有一个节点
            if (delNode.right != null) {
    
     // 有右节点
                if (delNode == root) {
    
    
                    root = delNode.right;
                    return root;
                }
                delNode.right.parent = delNode.parent; // 把右节点的parent指向删除点的parent
                // 关联父节点的左右子树
                if (delNode.data < delNode.parent.data) {
    
     // 删除的点在左边
                    delNode.parent.left = delNode.right;
                } else {
    
    
                    delNode.parent.right = delNode.right;
                }
            } else {
    
    
                if (delNode == root) {
    
    
                    root = delNode.left;
                    return root;
                }
                delNode.left.parent = delNode.parent;
                if (delNode.data < delNode.parent.data) {
    
    
                    delNode.parent.left = delNode.left;
                } else {
    
    
                    delNode.parent.right = delNode.left;
                }
            }
        }
        return root;
    }

    public void inOrde(BinaryNodeTeacher root) {
    
    
        if (root != null) {
    
    
            inOrde(root.left);
            System.out.print(root.data);
            inOrde(root.right);
        }
    }

    // 用于获得树的层数
    public int getTreeDepth(BinaryNodeTeacher root) {
    
    
        return root == null ? 0 : (1 + Math.max(getTreeDepth(root.left), getTreeDepth(root.right)));
    }

    /**
     *
     * 测试用例
     * 15
     * 10
     * 19
     * 8
     * 13
     * 16
     * 28
     * 5
     * 9
     * 12
     * 14
     * 20
     * 30
     * -1
     * 删除:15 8 5 10 12 19 16 14 30 9 13 20 28
     *
     *             15
     *          /     \
     *       10          19
     *     /   \       /   \
     *   8       13  16      28
     *  / \     / \         / \
     * 5   9   12  14      20  30
     */
    public static void main(String[] args) {
    
    
        BinarySearchTreeTeacher binarySearchTree = new BinarySearchTreeTeacher();
        BinaryNodeTeacher root = null;
        Scanner cin = new Scanner(System.in);
        int t = 1;
        System.out.println("二叉搜索树假定不存重复的子节点,重复可用链表处理,请注意~~");
        System.out.println("请输入根节点:");
        int rootData = cin.nextInt();
        root = new BinaryNodeTeacher(rootData);
        System.out.println("请输入第" + t + "个点:输入-1表示结束");
        while (true) {
    
     //
            int data = cin.nextInt();
            if (data == -1)
                break;
            binarySearchTree.insert(root, data);
            t++;
            System.out.println("请输入第" + t + "个点:输入-1表示结束");
        }
        binarySearchTree.show(root);		//找的别人写的打印二叉树形结构,感觉还不错,可以更加清晰
        System.out.println("删除测试:");
        while(true) {
    
    
            System.out.println("请输入要删除的点:-1表示结束");
            int key = cin.nextInt();
            root = binarySearchTree.remove(root, key);
            binarySearchTree.show(root);
            if(root == null) {
    
    
                System.out.println("树已经没有数据了~~");
                break;
            }
        }
    }



    private void writeArray(BinaryNodeTeacher currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) {
    
    
        // 保证输入的树不为空
        if (currNode == null)
            return;
        // 先将当前节点保存到二维数组中
        res[rowIndex][columnIndex] = String.valueOf(currNode.data);

        // 计算当前位于树的第几层
        int currLevel = ((rowIndex + 1) / 2);
        // 若到了最后一层,则返回
        if (currLevel == treeDepth)
            return;
        // 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔)
        int gap = treeDepth - currLevel - 1;

        // 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值
        if (currNode.left != null) {
    
    
            res[rowIndex + 1][columnIndex - gap] = "/";
            writeArray(currNode.left, rowIndex + 2, columnIndex - gap * 2, res, treeDepth);
        }

        // 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值
        if (currNode.right != null) {
    
    
            res[rowIndex + 1][columnIndex + gap] = "\\";
            writeArray(currNode.right, rowIndex + 2, columnIndex + gap * 2, res, treeDepth);
        }
    }

    public void show(BinaryNodeTeacher root) {
    
    
        if (root == null) {
    
    
            System.out.println("EMPTY!");
            return ;
        }
        // 得到树的深度
        int treeDepth = getTreeDepth(root);

        // 最后一行的宽度为2的(n - 1)次方乘3,再加1
        // 作为整个二维数组的宽度
        int arrayHeight = treeDepth * 2 - 1;
        int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;
        // 用一个字符串数组来存储每个位置应显示的元素
        String[][] res = new String[arrayHeight][arrayWidth];
        // 对数组进行初始化,默认为一个空格
        for (int i = 0; i < arrayHeight; i++) {
    
    
            for (int j = 0; j < arrayWidth; j++) {
    
    
                res[i][j] = " ";
            }
        }

        // 从根节点开始,递归处理整个树
        writeArray(root, 0, arrayWidth / 2, res, treeDepth);

        // 此时,已经将所有需要显示的元素储存到了二维数组中,将其拼接并打印即可
        for (String[] line : res) {
    
    
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < line.length; i++) {
    
    
                sb.append(line[i]);
                if (line[i].length() > 1 && i <= line.length - 1) {
    
    
                    i += line[i].length() > 4 ? 2 : line[i].length() - 1;
                }
            }
            System.out.println(sb.toString());
        }
    }

}

Red-black tree (lab: AVL balanced binary tree) binary search tree degenerates into a linked list

insert image description here
insert image description here

Properties of red-black trees:

  1. Each node is either red or black
  2. It is impossible to have connected red nodes (black ones are fine), and each leaf node is a black empty node (NIL), that is, the leaf node does not store data
  3. The root nodes are all black roots
  4. For each node, all paths from that node to its reachable leaf nodes contain the same number of black nodes

Rotation and color transformation rules when inserting:

  1. Color-changing situation: the parent of the current node is red, and another child node of its grandparent node
    is also red. (Uncle node):
    (1) Set the parent node to black
    (2) Set the uncle to black
    (3) Set the grandfather, that is, the father of the father, to red (grandpa)
    (4) Define the pointer to the grandfather node (Grandpa) is set to the current operation.
  2. Left rotation: when the current parent node is red and the uncle is black, and the current node is the right subtree. Left-handed
    Use the parent node as left-handed. Pointer to parent node
  3. Right rotation: when the current parent node is red and the uncle is black, and the current node is the left subtree. Right rotation
    (1) turn the parent node black
    (2) turn the grandfather node red (grandpa)
    (3) rotate with the grandfather node (grandpa)

Left-handed
insert image description here
Right-handed

insert image description here

insert image description here

insert image description here

Application of red-black tree

  1. HashMap
  2. TreeMap
  3. Windows Bottom: Find
  4. Linux process scheduling, nginx, etc.

COPY

public class RedBlackTree {
    
    

	private final int R = 0;
	private final int B = 1;

	private class Node {
    
    

		int key = -1;
		int color = B; // 颜色
		Node left = nil; // nil表示的是叶子结点
		Node right = nil;
		Node p = nil;

		Node(int key) {
    
    
			this.key = key;
		}

		@Override
		public String toString() {
    
    
			return "Node [key=" + key + ", color=" + color + ", left=" + left.key + ", right=" + right.key + ", p=" + p.key + "]" + "\r\n";
		}

	}

	private final Node nil = new Node(-1);
	private Node root = nil;

	public void printTree(Node node) {
    
    
		if (node == nil) {
    
    
			return;
		}
		printTree(node.left);
		System.out.print(node.toString());
		printTree(node.right);
	}

	private void insert(Node node) {
    
    
		Node temp = root;
		if (root == nil) {
    
    
			root = node;
			node.color = B;
			node.p = nil;
		} else {
    
    
			node.color = R;
			while (true) {
    
    
				if (node.key < temp.key) {
    
    
					if (temp.left == nil) {
    
    
						temp.left = node;
						node.p = temp;
						break;
					} else {
    
    
						temp = temp.left;
					}
				} else if (node.key >= temp.key) {
    
    
					if (temp.right == nil) {
    
    
						temp.right = node;
						node.p = temp;
						break;
					} else {
    
    
						temp = temp.right;
					}
				}
			}
			fixTree(node);
		}
	}

	private void fixTree(Node node) {
    
    
		while (node.p.color == R) {
    
    
			Node y = nil;
			if (node.p == node.p.p.left) {
    
    
				y = node.p.p.right;

				if (y != nil && y.color == R) {
    
    
					node.p.color = B;
					y.color = B;
					node.p.p.color = R;
					node = node.p.p;
					continue;
				}
				if (node == node.p.right) {
    
    
					node = node.p;
					rotateLeft(node);
				}
				node.p.color = B;
				node.p.p.color = R;
				rotateRight(node.p.p);
			} else {
    
    
				y = node.p.p.left;
				if (y != nil && y.color == R) {
    
    
					node.p.color = B;
					y.color = B;
					node.p.p.color = R;
					node = node.p.p;
					continue;
				}
				if (node == node.p.left) {
    
    
					node = node.p;
					rotateRight(node);
				}
				node.p.color = B;
				node.p.p.color = R;
				rotateLeft(node.p.p);
			}
		}
		root.color = B;
	}

	void rotateLeft(Node node) {
    
    
		if (node.p != nil) {
    
    
			if (node == node.p.left) {
    
    
				node.p.left = node.right;
			} else {
    
    
				node.p.right = node.right;
			}
			node.right.p = node.p;
			node.p = node.right;
			if (node.right.left != nil) {
    
    
				node.right.left.p = node;
			}
			node.right = node.right.left;
			node.p.left = node;
		} else {
    
    
			Node right = root.right;
			root.right = right.left;
			right.left.p = root;
			root.p = right;
			right.left = root;
			right.p = nil;
			root = right;
		}
	}

	void rotateRight(Node node) {
    
    
		if (node.p != nil) {
    
    
			if (node == node.p.left) {
    
    
				node.p.left = node.left;
			} else {
    
    
				node.p.right = node.left;
			}

			node.left.p = node.p;
			node.p = node.left;
			if (node.left.right != nil) {
    
    
				node.left.right.p = node;
			}
			node.left = node.left.right;
			node.p.right = node;
		} else {
    
    
			Node left = root.left;
			root.left = root.left.right;
			left.right.p = root;
			root.p = left;
			left.right = root;
			left.p = nil;
			root = left;
		}
	}

	public void creatTree() {
    
    
		int data[]= {
    
    23,32,15,221,3};
		Node node;
		System.out.println(Arrays.toString(data));
		for(int i = 0 ; i < data.length ; i++) {
    
    
			node = new Node(data[i]);
			insert(node);
		}
		printTree(root);
	}

	/**
	 * [23, 32, 15, 221, 3]
	 * Node [key=3, color=0, left=-1, right=-1, p=15]
	 * Node [key=15, color=1, left=3, right=-1, p=23]
	 * Node [key=23, color=1, left=15, right=32, p=-1]
	 * Node [key=32, color=1, left=-1, right=221, p=23]
	 * Node [key=221, color=0, left=-1, right=-1, p=32]
	 */
	public static void main(String[] args) {
    
    
		RedBlackTree bst = new RedBlackTree();
		bst.creatTree();
	}
}

Btree&B+Tree

The difference between B-Tree and B+Tree

  1. All nodes of B-tree will store data
  2. The b-tree leaf node has no linked list

What kind of data structure is the database index? Why can it find it so efficiently? What kind of algorithm is used?

select * from table where id = 10
select * from table where id > 12
select * from table where id > 12 and  id < 20

Transform the binary search tree:
insert image description here

Can solve all our above sql statements;

Efficiency logn

2^32=2.1 billion;

IO: Refers to reading data from disk. 32 layers will be read 32 times. CPU, memory, IO;
how much data will IO read once from the disk? Principles of computer composition. Page. Page,
how much space does 4KB Int occupy? 4B

think

Question 1: Search efficiency: 32 times (B+Tree multi-fork tree)
Question 2: Number of queries: (B+Tree range)
Question 3: Disk IO: Solve this problem; (B+Tree only stores data addresses in leaf nodes)

B+Tree data structure

insert image description here

How Mysql uses B+Tree to solve problems

Mysql is determined by the page size, which is generally 16kb. How much space does it consume to create an index with a bigint primary key type?
int 8 bytes, one pointer counts as 4 bytes, one page node: 16kb/(8+8)=1k key value + pointer third order:
1024 1024 1024=10 7374 1824

How to build the index correctly:

  1. There cannot be too many indexes, because the insertion and deletion of B+tree needs to be maintained, and too many indexes will slow down the insertion.
  2. The indexed field cannot use like '%%' otherwise it will be invalid
  3. The field type for indexing cannot be too large. The smaller the field, the larger the order and the higher the efficiency. Int and bigint, varchar(10), varchar(100), text, lontext; B+Tree. full text index
  4. The field values ​​for indexing cannot be too many and the same. There is something in mathematics called more hashing (discrete). For example, what happens when we index gender? The values ​​on the left are all the same and cannot be filtered by half. User sex is indexed separately 0 1
  5. The leftmost matching principle of the joint index. Select * from user where name = 'mx' and id = 1 My index built on (id, name) will be automatically optimized when mysql parses.
    Select * from user where name = ‘mx’ and age=10My index built on (id, name, age)
  6. NOT IN is not indexed not in (1,2,3) If there are too many values ​​of In, mysql will report an error

Huffman tree (Huffman tree, Huffman coding, prefix coding) -- compression software, communication telegram

Telegram's design:

1. The shorter the encrypted telegram, the better, and the faster the transmission.
2. Difficult to crack.
3. Easy to decode
. 4. Faster to change the encrypted tree
. 5. Reversible

Calculate the sum of the weighted path lengths of the following three binary trees:

insert image description here

WPL(a):7*2+5*2+2*2+4*2=36()
WPL(b):7*3+5*3+2*1+4*2=46()
WPL(c):7*1+5*2+2*3+4*3=35()

The edge of the left node is set to 0
and the edge of the right node is set to 1

© Huffman coding is

A:0
B:10
C:110
D:111

Build a Huffman tree:

1. Take the two nodes with the smallest values ​​each time and form them into a subtree.
2. Remove the original two points
3. Then put the composed subtree into the original sequence
4. Repeat 1 2 3 until only the last point is left

insert image description here

/**
 * 赫夫曼树
 */
class HuffmanNode implements Comparable<HuffmanNode> {
    
    
    String chars;
    int fre; //频率 权重
    HuffmanNode parent;
    HuffmanNode left;
    HuffmanNode right;

    @Override
    public int compareTo(HuffmanNode o) {
    
    
        return this.fre - o.fre;
    }
}

public class HuffmanTree {
    
    
    HuffmanNode root;
    List<HuffmanNode> leafs; //叶子节点
    Map<Character, Integer> weights; //叶子节点
    Map<Character, String> charmap;
    Map<String, Character> mapchar;


    public HuffmanTree(Map<Character,Integer> weights) {
    
    
        this.weights = weights;
        leafs = new ArrayList<>();
        charmap = new HashMap<>();
        mapchar = new HashMap<>();
    }

    public void code() {
    
    
        for (HuffmanNode node : leafs) {
    
    
            Character c = new Character(node.chars.charAt(0));
            HuffmanNode current = node;
            String code = "";
            do {
    
    
                if (current.parent != null && current == current.parent.left) {
    
     //left
                    code = "0" + code;
                } else {
    
    
                    code = "1" + code;
                }
                current = current.parent;
            } while (current.parent != null);

            charmap.put(c, code);
            mapchar.put(code, c);
        }
    }

    public void createTree() {
    
    
        Character keys[] = weights.keySet().toArray(new Character[0]);
        PriorityQueue<HuffmanNode> priorityQueue = new PriorityQueue<>();
        for (Character key : keys) {
    
    
            HuffmanNode huffmanNode = new HuffmanNode();
            huffmanNode.chars = key.toString();
            huffmanNode.fre = weights.get(key);
            priorityQueue.add(huffmanNode);
            leafs.add(huffmanNode);
        }

        int len = priorityQueue.size();
        for (int i = 1; i <= len-1; i++) {
    
    
            HuffmanNode n1 = priorityQueue.poll();
            HuffmanNode n2 = priorityQueue.poll();

            HuffmanNode newNode = new HuffmanNode();

            newNode.fre = n1.fre + n2.fre;
            newNode.chars = n1.chars + n2.chars;

            newNode.left = n1;
            newNode.right = n2;

            n1.parent = newNode;
            n2.parent = newNode;

            priorityQueue.add(newNode);
        }
        root = priorityQueue.poll();
    }

    public String encode(String body) {
    
    
        StringBuilder builder = new StringBuilder();
        for (char c : body.toCharArray()) {
    
    
            builder.append(charmap.get(c));
        }
        return builder.toString();
    }

    public String decode(String body) {
    
    
        StringBuilder builder = new StringBuilder();
        while (!body.equals("")) {
    
    
            for (String code : mapchar.keySet()) {
    
    
                if (body.startsWith(code)) {
    
    
                    body = body.replaceFirst(code,"");
                    builder.append(mapchar.get(code));
                }
            }
        }
        return builder.toString();
    }

    /**
     * a : 10110
     * b : 01
     * c : 1010
     * d : 00
     * e : 11
     * f : 10111
     * g : 100
     * encode: 0010111101111010
     * decode: dffc
     */
    public static void main(String[] args) {
    
    
        Map<Character, Integer> weights = new HashMap<>();
        weights.put('a',3);
        weights.put('b',24);
        weights.put('c',6);
        weights.put('d',20);
        weights.put('e',34);
        weights.put('f',4);
        weights.put('g',12);

        HuffmanTree huffmanTree = new HuffmanTree(weights);
        huffmanTree.createTree();
        huffmanTree.code();
        for (Map.Entry<Character, String> entry : huffmanTree.charmap.entrySet()) {
    
    
            System.out.println(entry.getKey() +  " : " + entry.getValue());
        }

        String encode = huffmanTree.encode("dffc");
        System.out.println("encode: " + encode);
        String decode = huffmanTree.decode(encode);
        System.out.println("decode: " + decode);
    }
}

pile of trees

Suppose you are given a sequence: 8 4 20 7 3 1 25 14 17

Sorting using a heap tree:

  1. First store in a complete binary tree in sequence order: build a heap
  2. Heapify from the last non-leaf node. Why is it the last non-leaf node instead of the last leaf node? (The last leaf node has no left and right nodes and cannot be compared)

There are two implementations of heap insertion:

  • bottom up
  • From top to bottom

The insertion process is called heapization

insert image description here

**
 * 堆树
 *
 * 建堆
 * 排序
 *
 * 1. 优先级队列问题: 删除最大的
 * 2. top n 热搜排行榜问题:1000万的数字
 * 3. 定时器 堆顶
 * 4. 给你1亿不重复的数字,求出top10,前10最大的数字,还可动态添加
 */
public class HeapTree {
    
    

    //建大顶堆
    public static void maxHeap(int data[], int parent, int end) {
    
    
        int leftSon = parent * 2 + 1; //下标从0开始+1
        while (leftSon < end) {
    
    
            int temp = leftSon;

            //temp表示的是 我们左右节点大的那一个
            if (leftSon + 1 < end && data[leftSon] < data[leftSon + 1]) {
    
    
                temp = leftSon + 1; //右节点比左节点大
            }

            //比较左右节点大的那一个temp和父节点比较大小
            if (data[parent] > data[temp]) {
    
    
                return; //不需要交换
            } else {
    
    
                int t = data[parent]; //交换
                data[parent] = data[temp];
                data[temp] = t;

                parent = temp; //继续堆化
                leftSon = parent * 2 + 1;
            }
        }
    }

    public static void heapSort(int data[]) {
    
    
        int len = data.length;

        //从后向上建
        //建堆从哪里开始 最后一个的父元素开始(len/2 - 1)(父元素中:最后一个父元素是第几个,从0开始)
        for (int parent = len/2 - 1; parent >= 0; parent--) {
    
     //nlog(n)
            maxHeap(data, parent, len);
        }

        //从上向下建
        //最后一个数和第一个数交换
        int headHeap = 0;
        for (int tailHeap = len - 1; tailHeap > 0; tailHeap--) {
    
     //nlog(n)
            int temp = data[headHeap];
            data[headHeap] = data[tailHeap];
            data[tailHeap] = temp;
            maxHeap(data, headHeap, tailHeap);
        }
    }

    /**
     * Arrays: [1, 3, 4, 7, 8, 14, 17, 20, 25]
     */
    public static void main(String[] args) {
    
    
        int data[] = {
    
    8, 4, 20, 7, 3, 1, 25, 14, 17};
        heapSort(data);
        System.out.printf("Arrays: " + Arrays.toString(data));
    }

}

TopK

class TopK {
    
    
    private int k = 10;
    private int data[] = new int[k];

    public void topK() {
    
    
        Random random = new Random();
        long time = System.currentTimeMillis();
        int size = 0;
        boolean init = false;
        for (int i = 0; i < 100000000; i++) {
    
    
            int num = random.nextInt(100000000);
            if (size < k) {
    
    
                data[size] = num;
                size++;
            } else {
    
    
                if (!init) {
    
    
                    for (int j =  k/2 - 1; j >=0; j--) {
    
    
                        minHeap(data, 0, k);
                    }
                    init = true;
                }
                if (num > data[0]) {
    
    
                    data[0] = num;
                    minHeap(data, 0, k);
                }
            }
        }
        System.out.println("耗时:" + (System.currentTimeMillis() - time) + "ms \n");
        for (int datum : data) {
    
    
            System.out.print(datum+", ");
        }
    }

    private void minHeap(int[] data, int start, int end) {
    
    
        int parent = start;
        int son = parent * 2 + 1;
        while (son < end) {
    
    
            int temp = son;
            if (son + 1 < end && data[son]  > data[son +1]) {
    
     //右节点比左节点大
                temp = son + 1; //根号右节点和父节点
            }
            //temp表示我们左右节点小的那一个
            if (data[parent] < data[temp]) {
    
    
                return;
            } else {
    
    
                int t = data[parent];
                data[temp] = t;
                parent = temp;
                son = parent * 2 + 1;
            }
        }
        return;
    }

    public static void main(String[] args) {
    
    
        TopK topK = new TopK();
        topK.topK();
    }
}

Guess you like

Origin blog.csdn.net/menxu_work/article/details/130341619