算法基础 树专题 二叉树

二叉树

每个节点最多含有两个子树的树称为二叉树。

二叉树的性质

  • 在非空二叉树中,第i层的节点数不超过2 i-1i>=1;
  • 深度为h的二叉树最多有2 i -1个节点,最少有h个节点,h>=1;
  • 对于任意一颗二叉树,如果其叶子节点数为N,而度数为2的节点总数为M,则N=M+1;

其中又分为满二叉树和完全二叉树

  • 满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。也可以这样理解,除叶子结点外的所有结点均有两个子结点。节点数达到最大值,所有叶子结点必须在同一层上。
  • 完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~(h-1)层) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。


    image.png

    完全二叉树是效率很高的数据结构,堆是一种完全二叉树或近似完全二叉树。

/**
 * 二叉树
 */
public class BinaryTree<E> {

    public BinaryTree(){}

    /**
     * 循环构建二叉树
     */
    private Node<E> buildBinaryTree(E[] array) {
        List<Node<E>> nodes = new ArrayList<>(array.length);
        for (E value:array){
            nodes.add(new Node<>(value));
        }
        for (int i = 0; i < array.length/2; i++) {
            //左孩子
            if ((2*i+1)<array.length){
                nodes.get(i).setLeftChild(nodes.get(2*i+1));
            }
            //右孩子
            if ((2*i+1)<array.length){
                nodes.get(i).setRightChild(nodes.get(2*i+2));
            }
        }
        return nodes.get(0);
    }

    /**
     * 递归构造二叉树
     */
    public Node<E> buildBinaryTree(E[] array,int index){
        if(index>=array.length){
            return null;
        }
        Node<E> node = new Node<>(array[index]);
        if(index*2+1<array.length){
            node.setLeftChild(buildBinaryTree(array,index*2+1));
        }
        if (index*2+2<array.length){
            node.setRightChild(buildBinaryTree(array,index*2+2));
        }
        return node;
    }


    /**
     * 先序遍历(先根节点->遍历左子树->遍历右子树)
     * [1,2,4,8,9,5,3,6,7]
     */
    public void preOrderTraverse(Node<E> node){
        if (node==null){
            return;
        }
        System.out.print(node.getValue()+",");
        preOrderTraverse(node.getLeftChild());
        preOrderTraverse(node.getRightChild());
    }


    /**
     * 中序遍历(遍历左子树->根节点->遍历右子树)
     * [8,4,9,2,5,1,6,3,7]
     */
    public void inOrderTraverse(Node<E> node){
        if (node==null){
            return;
        }
        inOrderTraverse(node.getLeftChild());
        System.out.print(node.getValue()+",");
        inOrderTraverse(node.getRightChild());
    }

    /**
     * 后序遍历(遍历左子树->遍历右子树->根节点)
     * [7,6,3,5,9,8,4,2,1]
     */
    public void postOrderTraverse(Node<E> node){
        if (node==null){
            return;
        }
        postOrderTraverse(node.getRightChild());
        postOrderTraverse(node.getLeftChild());
        System.out.print(node.getValue()+",");
    }


    /**
     * 深度优先搜索
     * 简称DFS,其原则是,沿着一条路径一直找到最深的那个节点,
     * 当没有子节点的时候,返回上一级节点,寻找其另外的子节点,
     * 继续向下遍历,没有就向上返回一级,直到所有的节点都被遍历到,
     * 每个节点只能访问一次。
     * [1,2,4,8,9,5,3,6,7]
     * 非递归
     */
    public void depthOrderTraversal(Node<E> root){
        Stack<Node<E>> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            Node<E> node = stack.pop();
            System.out.print(node.getValue()+",");
            if (node.getRightChild()!=null){
                stack.push(node.getRightChild());
            }
            if (node.getLeftChild()!=null){
                stack.push(node.getLeftChild());
            }
        }
    }


    /**
     * 广度优先搜索
     * 简称BFS;广度优先遍历的原则就是对每一层的节点依次访问,
     * 一层访问结束后,进入下一层,直到最后一个节点,
     * 同样的,每个节点都只访问一次。
     * [1,2,3,4,5,6,7,8,9]
     * 非递归
     */
    public void levelOrderTraversal(Node<E> root){
        Queue<Node<E>> queue = new LinkedBlockingQueue();
        queue.add(root);
        while (!queue.isEmpty()){
            Node<E> node = queue.remove();
            System.out.print(node.getValue()+",");
            if (node.getLeftChild()!=null){
                queue.add(node.getLeftChild());
            }
            if (node.getRightChild()!=null){
                queue.add(node.getRightChild());
            }
        }
    }


    /**
     * 深度优先遍历
     * 递归
     */
    public void dfs(Node<E> root){
        if (root==null){
            return;
        }
        System.out.print(root.getValue()+",");
        dfs(root.leftChild);
        dfs(root.rightChild);
    }



    /**
     * 获取树的深度
     */
    public int getDepth(Node node){
        if (node==null){
            return 0;
        }
        return 1+(getDepth(node.getLeftChild())>getDepth(node.getRightChild())?getDepth(node.getLeftChild()):getDepth(node.getRightChild()));
    }


    static class Node<E>{
        private E value;
        private Node<E> leftChild;
        private Node<E> rightChild;

        public Node(E e){
            value = e;
        }

        public E getValue() {
            return value;
        }

        public void setValue(E value) {
            this.value = value;
        }

        public Node<E> getLeftChild() {
            return leftChild;
        }

        public void setLeftChild(Node<E> leftChild) {
            this.leftChild = leftChild;
        }

        public Node<E> getRightChild() {
            return rightChild;
        }

        public void setRightChild(Node<E> rightChild) {
            this.rightChild = rightChild;
        }
    }
}

排序二叉树

二叉查找树(英语:Binary Search Tree),也称为二叉搜索树、有序二叉树(ordered binary tree)或排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:

  • 若任意节点的左子树不空,则左子树上所有节点的值均小于它根节点的值
  • 若任意节点的右子树不空,则右子树上所有节点的值均大于它根节点的值
  • 没有键值相等的节点
  • 对二叉查找树进行中序遍历,即可得到有序的数列

二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低为 O ( log ⁡ n )

/**
 * 二叉查找树(排序二叉树)
 */
public class BinarySearchTree {

    Node root;

    public Node getRoot(){
        return root;
    }


    /**
     * 通过数组构造二叉排序树
     */
    public void buildBinarySearchTree(int[] array){
        root = new Node(array[0]);
        for (int i = 1; i < array.length; i++) {
            Node child = new Node(array[i]);
            buildBinarySearchTree(root,child);
        }
    }

    /**
     * 向排序二叉树中插入一个节点
     */
    public void buildBinarySearchTree(int value){
        Node node = new Node(value);
        buildBinarySearchTree(node);
    }


    /**
     * 递归构造二叉排序树
     */
    private void buildBinarySearchTree(Node parent,Node child){
        //右子树
        if (child.getValue()>parent.getValue()){
            if (parent.getRight()==null){
                parent.setRight(child);
                return;
            }else{
                buildBinarySearchTree(parent.getRight(),child);
            }
        }
        //左子树
        if(child.getValue()<parent.getValue()){
            if (parent.getLeft()==null){
                parent.setLeft(child);
                return;
            }else{
                buildBinarySearchTree(parent.getLeft(),child);
            }
        }
    }

    /**
     * 非递归构造排序二叉树
     */
    private void buildBinarySearchTree(Node node){
        if (root==null){
            root = node;
            return;
        }
        Node parent = root;
        while (true){
            //右子树
            if (node.getValue()>parent.getValue()){
                if (parent.getRight()==null){
                    parent.setRight(node);
                    return;
                }else{
                    parent = parent.getRight();
                    continue;
                }
            }
            //左子树
            if (node.getValue()<parent.getValue()){
                if (parent.getLeft()==null){
                    parent.setLeft(node);
                    return;
                }else{
                    parent = parent.getLeft();
                    continue;
                }
            }
        }
    }

    /**
     * 中序遍历
     */
    public void inOrderTraverse(Node root){
        if (root==null){
            return;
        }
        inOrderTraverse(root.getLeft());
        System.out.print(root.getValue()+",");
        inOrderTraverse(root.getRight());
    }

    /**
     * 获取树的深度
     */
    public int getDepth(Node root){
        if (root==null){
            return 0;
        }
        return 1+(getDepth(root.getRight())>getDepth(root.getLeft())?getDepth(root.getRight()):getDepth(root.getLeft()));
    }


    /**
     * 查找值
     */
    public boolean find(int value){
        Node parent = root;
        while (parent!=null){
            if (value==parent.getValue()){
                return true;
            }else if (value>parent.getValue()){
                parent = parent.getRight();
            }else if (value<parent.getValue()){
                parent = parent.getLeft();
            }
        }
        return false;
    }


    /**
     * 二叉树节点
     */
    static class Node{
        int value;
        Node left;
        Node right;

        public Node(int value){
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public Node getLeft() {
            return left;
        }

        public void setLeft(Node left) {
            this.left = left;
        }

        public Node getRight() {
            return right;
        }

        public void setRight(Node right) {
            this.right = right;
        }
    }
}

平衡二叉树

当且仅当任何节点的两棵子树的高度差不大于1的二叉树。其中AVL树是最先发明的自平衡二叉查找树,是最原始典型的平衡二叉树。

平衡二叉树是基于二叉查找树的改进。由于在某些极端的情况下(如在插入的序列是有序的时),二叉查找树将退化成近似链或链,此时,其操作的时间复杂度将退化成线性的,即O(n)。所以我们通过自平衡操作(即旋转)构建两个子树高度差不超过1的平衡二叉树。

/**
 * 平衡二叉树
 */
public class BalancedBinaryTree {

    Node root;

    public Node getRoot(){
        return root;
    }

    public void insert(int value){
        if (null==root){
            root = new Node(value);
        }else {
            insert(root,new Node(value));
        }
    }

    /**
     * 插入节点
     */
    private void insert(Node parent,Node child){
        //查找右节点
        if (child.getValue()>parent.getValue()){
            if (parent.getRight()==null){
                parent.setRight(child);
            }else {
                insert(parent.getRight(),child);
                //回溯的时候平衡树
                balance(parent);
            }
        }
        //查找做节点
        if (child.getValue()<parent.getValue()){
            if (parent.getLeft()==null){
                parent.setLeft(child);
            }else {
                insert(parent.getLeft(),child);
                //回溯的时候平衡树
                balance(parent);
            }
        }
    }

    /**
     * 平衡二叉查找树
     */
    private void balance(Node parent) {
        if (isBalance(parent)){
            return;
        }
        RotationModel model = judgeRotationModel(parent);
        switch (model){
            case LL:
                rightRotation(parent);
                break;
            case LR:
                leftRotation(parent);
                rightRotation(parent);
                break;
            case RL:
                rightRotation(parent);
                leftRotation(parent);
                break;
            case RR:
                rightRotation(parent);
                break;
            default:
                break;
        }
    }

    /**
     * 左旋转
     * (这里的balance指的是平衡后)
     */
    private void leftRotation(Node balance){
        Node leftChild = balance.getLeft();
        Node rightGrandSon = leftChild.getRight();
        leftChild.setRight(null);
        balance.setLeft(rightGrandSon);
        rightGrandSon.setLeft(leftChild);
    }

    /**
     * 右旋转
     * (这里的balance指的是平衡后)
     */
    private void rightRotation(Node balance){
        Node parent = searchParent(balance);
        Node leftChild = balance.getLeft();
        Node rightGrandSon = leftChild.getRight();
        if (parent==null){
            root = leftChild;
        }else {
            //将父节点的孩子替换为非平衡节点的左孩子
            if (parent.getRight()==balance){
                parent.setRight(leftChild);
            }else {
                parent.setLeft(leftChild);
            }
        }
        //将原本的非平衡节点设置为右节点
        leftChild.setRight(balance);
        //将右子孙节点设置为原来非平衡节点的左节点
        balance.setLeft(rightGrandSon);
    }

    /**
     * 判断节点是否平衡
     */
    private boolean isBalance(Node parent) {
        return getDepth(parent.getRight())-getDepth(parent.getLeft())<2
                &&getDepth(parent.getRight())-getDepth(parent.getLeft())>-2;
    }

    /**
     * 判断二叉树的旋转状态
     */
    private RotationModel judgeRotationModel(Node balance){
        if (getDepth(balance.getRight())>getDepth(balance.getLeft())){
            if (balance.getRight().getRight()==null){
                return RotationModel.RL;
            }else{
                return RotationModel.RR;
            }
        }else{
            if (balance.getLeft().getLeft()==null){
                return RotationModel.LR;
            }else {
                return RotationModel.LL;
            }
        }
    }

    /**
     * 查找节点的父亲节点
     */
    private Node findParent(Node child){
        if (child==root){
            return null;
        }
        return findParent(root,child);
    }

    /**
     * 递归查找子节点的父亲节点
     */
    private Node findParent(Node parent,Node child){
        Node target;
        if (parent==null){
            return null;
        }
        if (parent.getLeft()==child||parent.getRight()==child){
            target = parent;
        }else if (child.getValue()>parent.getValue()){
            target = findParent(parent.getRight(),child);
        }else {
            target = findParent(parent.getLeft(),child);
        }
        return target;
    }

    /**
     * 迭代查找子节点的父亲节点
     */
    private Node searchParent(Node child){
        if (child==root){
            return null;
        }
        Node parent = root;
        while (parent!=null){
            if (parent.getLeft()==child||parent.getRight()==child){
                return parent;
            } else if (child.getValue()>parent.getValue()){
                parent = parent.getRight();
            }else {
                parent = parent.getLeft();
            }
        }
        return null;
    }

    /**
     * 获取节点的深度
     */
    public int getDepth(Node node){
        if (node==null){
            return 0;
        }
        return 1+(getDepth(node.getLeft())>getDepth(node.getRight())
                ?getDepth(node.getLeft()):getDepth(node.getRight()));
    }

    /**
     * 中序遍历
     */
    public void inOrderTraverse(Node root){
        if (root==null){
            return;
        }
        inOrderTraverse(root.getLeft());
        System.out.print(root.getValue()+",");
        inOrderTraverse(root.getRight());
    }

    static class Node{
        int value;
        Node left;
        Node right;

        public Node(int value){
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }

        public Node getLeft() {
            return left;
        }

        public void setLeft(Node left) {
            this.left = left;
        }

        public Node getRight() {
            return right;
        }

        public void setRight(Node right) {
            this.right = right;
        }
    }

    enum RotationModel{
        LL,LR,RR,RL
    }
}

红黑树

红黑树也是一种弱平衡或者说自平衡(不是绝对的平衡)的二叉查找树,在平衡二叉树的基础上每个节点又增加了一个颜色的属性,节点的颜色只能是红色或黑色。红黑树的特点是:

  • 根节点必须是黑色
  • 所有叶子都是黑色
  • 每个红色结点的两个子结点一定都是黑色
  • 红黑树中所有的叶子节点后面再接上左右两个空节点,这样可以保持算法的一致性,而且所有的空节点都是黑色。
  • 任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

AVL树是带有平衡条件的二叉查找树,一般是用平衡因子差值判断是否平衡并通过旋转来实现平衡,左右子树树高相差不超过1,和红黑树相比,AVL树是严格的平衡二叉树,平衡条件必须满足。不管我们是执行插入还是删除操作,只要不满足上面的条件,就要通过旋转来保持平衡,因而它的旋转是非常耗时的。

由于维护这种高度平衡所付出的代价比从中获得的效率收益还大,故而实际的应用不多,更多的地方是用追求局部而不是非常严格整体平衡的红黑树。

红黑树一种二叉查找树,但在每个节点增加一个存储位表示节点的颜色,可以是红或黑。通过对任何一条从根到叶子的路径上各个节点着色的方式的限制,红黑树确保没有一条路径会比其它路径长出两倍,因此,红黑树是一种弱平衡二叉树。由于是弱平衡,可以看到,在相同的节点情况下,AVL树的高度低于红黑树。相对于要求严格的AVL树来说,它的旋转次数少,所以对于搜索,插入,删除操作较多的情况下,我们就用红黑树。

红黑是用非严格的平衡来换取增删节点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决,而AVL是严格平衡树,因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多。所以红黑树的插入效率更高。

红黑树有两大操作保证平衡:
- recolor
- rotation

image.png

插入节点的场景


image.png
/**
 * 红黑树
 */
public class RedBlackTree {

    Node root;

    public Node getRoot(){
        return this.root;
    }

    /**
     * 向树中插入节点
     */
    public void insert(int value){
        if (root==null){
            Node node = new Node(NodeColor.BLACK,value);
            root = node;
        }else {
            Node node = new Node(NodeColor.RED,value);
            insert(root,node);
        }
    }

    /**
     * 插入节点
     */
    private void insert(Node parent,Node node){
        //右子树
        if (node.getValue()>parent.getValue()){
            if (parent.getRight()==null){
                parent.setRight(node);
                selfBalance(node);
                return;
            }else {
                insert(parent.getRight(),node);
            }
        }
        //左子树
        if (node.getValue()<parent.getValue()){
            if (parent.getLeft()==null){
                parent.setLeft(node);
                selfBalance(node);
                return;
            }else {
                insert(parent.getLeft(),node);
            }
        }
    }

    /**
     * 红黑树的自平衡
     */
    private void selfBalance(Node node) {
        if (node==null){
            return;
        }
        if (node==root){
            if (node.getColor().equals(NodeColor.RED)){
                node.setColor(NodeColor.BLACK);
            }
            return;
        }
        Node parent = findParent(node);
        if (parent==root){
            if (parent.getColor().equals(NodeColor.RED)){
                parent.setColor(NodeColor.BLACK);
            }
            return;
        }
        Node grandParent = findParent(parent);
        Node uncle;
        if (grandParent.getLeft()==parent){
            uncle = grandParent.getRight();
        }else {
            uncle = grandParent.getLeft();
        }
        selfBalance(node,parent,uncle,grandParent);
    }

    /**
     * 递归自平衡
     */
    private void selfBalance(Node node, Node parent, Node uncle, Node grandParent) {
        CaseType type = judgeCaseType(node, parent, uncle, grandParent);
        switch (type){
            case PB://父亲是黑色节点直接插入,不用处理
                break;
            case UR://父亲节点和叔叔节点都是红色
                parent.setColor(NodeColor.BLACK);
                uncle.setColor(NodeColor.BLACK);
                grandParent.setColor(NodeColor.RED);
                selfBalance(grandParent);
                break;
            case PR_UB_LL://父亲节点是红色,叔叔节点是黑色或者不存在
                parent.setColor(NodeColor.BLACK);
                grandParent.setColor(NodeColor.RED);
                rightRotation(grandParent);
                break;
            case PR_UB_LR:
                leftRotation(parent);
                selfBalance(parent);
                break;
            case PR_UB_RR:
                parent.setColor(NodeColor.BLACK);
                grandParent.setColor(NodeColor.RED);
                leftRotation(grandParent);
                break;
            case PR_UB_RL:
                rightRotation(parent);
                selfBalance(parent);
                break;
        }
    }

    /**
     * 对当前节点进行左旋
     */
    private void leftRotation(Node balance){
        Node parent = findParent(balance);
        Node rightChild = balance.getRight();
        Node leftGrandChild = rightChild.getLeft();
        if (parent==root){
            root = rightChild;
        }else {
            if (balance==parent.getLeft()){
                parent.setLeft(rightChild);
            }else {
                parent.setRight(rightChild);
            }
        }
        rightChild.setLeft(balance);
        balance.setRight(leftGrandChild);
    }


    /**
     * 对当前节点进行右旋
     */
    private void rightRotation(Node balance){
        Node parent = findParent(balance);
        Node leftChild = balance.getLeft();
        Node rightGrandChild = leftChild.getRight();
        if (parent==root){
            root = leftChild;
        }else {
            if (balance==parent.getLeft()){
                parent.setLeft(leftChild);
            }else {
                parent.setRight(leftChild);
            }
        }
        leftChild.setRight(balance);
        balance.setLeft(rightGrandChild);
    }



    /**
     * 寻找当前节点的父亲节点
     */
    private Node findParent(Node current){
        if (current==root){
            return null;
        }
        Node parent = root;
        while (parent!=null){
            if (parent.getLeft()==current||parent.getRight()==current){
                return parent;
            }else if (current.getValue()>parent.getValue()){
                parent = parent.getRight();
            }else {
                parent = parent.getLeft();
            }
        }
        return null;
    }

    /**
     * 判断节点的场景
     */
    public CaseType judgeCaseType(Node node,Node parent,Node uncle,Node grandParent){
        if (parent.getColor().equals(NodeColor.BLACK)){
            return CaseType.PB;
        }else if (uncle!=null&&uncle.getColor().equals(NodeColor.RED)){
            return CaseType.UR;
        }else if (parent==grandParent.getLeft()&&node==parent.getLeft()){
            return CaseType.PR_UB_LL;
        }else if (parent==grandParent.getRight()&&node==parent.getLeft()){
            return CaseType.PR_UB_RL;
        }else if (parent==grandParent.getLeft()&&node==parent.getRight()){
            return CaseType.PR_UB_LR;
        }else if (parent==grandParent.getRight()&&node==parent.getRight()){
            return CaseType.PR_UB_RR;
        }else {
            throw new RuntimeException("未考虑当前场景,存在错误");
        }
    }


    public void inOrderTraverse(Node root){
        if (root==null){
            return;
        }
        inOrderTraverse(root.getLeft());
        System.out.print(root.getValue()+"["+root.getColor()+"]   ");
        inOrderTraverse(root.getRight());
    }

    /**
     * 红黑树节点
     */
    @Setter
    @Getter
    static class Node{
        public Node(NodeColor color,int value){
            this.color = color;
            this.value = value;
        }
        NodeColor color;
        int value;
        Node left;
        Node right;
    }

    /**
     * 节点颜色
     */
    enum NodeColor{
        BLACK,RED
    }

    /**
     * 场景类型
     * PB——>ParentBlack
     * PR——>ParentRed
     * UB——>UncleBlack
     * UR——>UncleRed
     * */
    enum CaseType{
        PB,//父亲节点是黑色
        UR,//父亲节点是红色,叔叔节点也是红色

        /*      A  *
         *    B    *
         *  C      *
         *         */
        PR_UB_LL,//父亲节点是红色,叔叔节点是黑色或者不存在,当前节点和父亲节点是左左结构

        /*    A    *
         *  B      *
         *    C    *
         *         */
        PR_UB_LR,//父亲节点是红色,叔叔节点是黑色或者不存在,当前节点和父亲节点是左右结构

        /*  A    *
         *    B  *
         *  C    *
         *       */
        PR_UB_RL,//父亲节点是红色,叔叔节点是黑色或者不存在,当前节点和父亲节点是右左结构

        /*  A       *
         *    B     *
         *      C   *
         *          */
        PR_UB_RR,//父亲节点是红色,叔叔节点是黑色或者不存在,当前节点和父亲节点是右右结构
        ;
    }
}

Huffman树

哈夫曼树是一种带权路径长度最短的二叉树,也称为最优二叉树。

在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则,那就是:权重越大的结点离树根越近。

路径
在一棵树中,一个结点到另一个结点之间的通路,称为路径

路径长度
在一条路径中,每经过一个结点,路径长度都要加 1 。例如在一棵树中,规定根结点所在层数为1层,那么从根结点到第 i 层结点的路径长度为 i - 1

结点的权
给每一个结点赋予一个新的数值,被称为这个结点的权。

结点的带权路径长度
指的是从根结点到该结点之间的路径长度与该结点的权的乘积。

WPL
树的带权路径长度为树中所有叶子结点的带权路径长度之和。通常记作 “WPL”

构建哈夫曼树的过程

  • 在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;
  • 在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;
  • 重复 1 和 2 ,直到所以的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树。


    image.png
/**
 * 哈夫曼树
 */
public class HuffmanTree {

    Node root;

    public Node getRoot(){
        return root;
    }


    private void buildHuffmanTree(List<Node> nodes) {
        if (nodes.size()<=0){
            return;
        }
        if (nodes.size()<2){
            root = nodes.get(0);
            return;
        }
        Node[] array;
        while (nodes.size()>2){
            Node left = nodes.get(0);
            Node right = nodes.get(1);
            Node parent = new Node(null,left.getWeight()+right.getWeight());
            parent.setLeft(left);
            parent.setRight(right);
            nodes.remove(left);
            nodes.remove(right);
            nodes.add(parent);
            array = new Node[nodes.size()];
            nodes.toArray(array);
            sort(array);
        }
        root = new Node(null,nodes.get(0).getWeight()+nodes.get(1).getWeight());
        root.setLeft(nodes.get(0));
        root.setRight(nodes.get(1));
    }


    public void sort(Node[] array) {
        bubbleSort(array);
    }

    /**
     * 比较相邻的两个元素之间的值
     */
    private void bubbleSort(Node[] array){
        int loop=0,swap=0;
        for (int i = 0; i < array.length; i++) {
            for (int j = 0; j < array.length-1; j++) {
                loop++;
                if(array[j].getWeight()<array[j+1].getWeight()){
                    swap++;
                    Node temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                }
            }
        }
    }


    @Data
    static class Node{

        public Node(String name,int weight){
            this.name = name;
            this.weight = weight;
        }

        private String name;//节点
        private int weight;//节点权值
        private Node left;
        private Node right;
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容