笔记6- 二叉树之 Haffman树、AVL树、红黑树

Haffman树

1.概念和构造:

我们来看一个案例:
image.png
image.png

重点理解一下路径长度和带权的路径长度的概念:(权重就是结点到结点之间的数字,代表重复了多少次)


image.png
下面我们来看一下Haffman树的构造:
image.png
image.png
image.png
Haffman树编码:
image.png
image.png

2.附上Haffman树的代码实现:

package com.xx.tree;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Stack;

public class HaffmanTree {

    public static void main(String[] arg0) {
        HaffmanTree haffmanTree = new HaffmanTree();
        ArrayList<TreeNode> list = new ArrayList<TreeNode>();
        list.add(new TreeNode("good", 50));
        TreeNode node = new TreeNode("nice", 10);
        list.add(node);
        list.add(new TreeNode("greate", 20));
        list.add(new TreeNode("hello", 110));
        list.add(new TreeNode("hi", 200));
        haffmanTree.showHaffman(haffmanTree.createHaffmanTree(list));
        haffmanTree.getCode(node);
    }

    TreeNode root;

    // 创建哈夫曼树
    public TreeNode createHaffmanTree(ArrayList<TreeNode> list) {
        // 排序
        while (list.size() > 1) {
            // 可以根据输入的数组不同用不同的方法进行排序
            Collections.sort(list);
            TreeNode left = list.get(list.size() - 1);
            TreeNode right = list.get(list.size() - 2);
            TreeNode parent = new TreeNode("tree", left.weight + right.weight);

            parent.leftChild = left;
            parent.rightChild = right;

            left.parent = parent;
            right.parent = parent;

            list.remove(right);
            list.remove(left);

            list.add(parent);
        }
        root = list.get(0);
        return root;
    }

    // 从上到小 从左往右依次显示
    public void showHaffman(TreeNode root) {
        LinkedList<TreeNode> list = new LinkedList<TreeNode>();
        list.offer(root);
        while (!list.isEmpty()) {
            // 先进先出
            TreeNode node = list.pop();
            System.out.println(node.data);
            if (node.leftChild != null) {
                list.offer(node.leftChild);
            }
            if (node.rightChild != null) {
                list.offer(node.rightChild);
            }
        }
    }
    // 获取编码
    public void getCode(TreeNode node) {
        TreeNode tNode = node;
        Stack<String> stack = new Stack<String>();
        while (tNode != null && tNode.parent != null) {
            if (tNode.parent.leftChild == tNode) {
                // node是左节点
                stack.push("0");
            } else if (tNode.parent.rightChild == tNode) {
                // node是右节点
                stack.push("1");
            }
            tNode = tNode.parent;
        }
        System.out.println();
        while (!stack.isEmpty()) {
            System.out.print(stack.pop());
        }
    }

    public static class TreeNode<T> implements Comparable<TreeNode<T>> {

        T data;
        int weight;// 权重
        TreeNode leftChild;
        TreeNode rightChild;
        TreeNode parent;

        public TreeNode(T data, int weight) {
            this.data = data;
            this.weight = weight;
            leftChild = null;
            rightChild = null;
            parent = null;
        }

        // 倒序 从大到小
        public int compareTo(TreeNode<T> o) {
            // TODO Auto-generated method stub
            if (this.weight > o.weight) {
                return -1;
            } else if (this.weight < o.weight) {
                return 1;
            }
            return 0;
        }

    }
}

AVL树(平衡二叉树)

概念:

1.平衡二叉树,是一种二叉排序树,其中每一个节点的左子树和右子树的高度差最多等于1.
2.平衡因子:二叉树上节点的左子树深度减去右子树深度的值称为平衡因子BF(Balance Factor),上面的概念又可以说成是 每个节点的平衡因子<=1的二叉排序树。
3.最小不平衡子树:距离插入结点最近的,且平衡因子的绝对值大于1的节点为根的子树,我们成为最小不平衡子树。

构建平衡二叉树:

image.png

image.png

image.png
image.png
image.png

左旋:
image.png

右旋:
image.png

插入规则:

1.左平衡操作:结点t的不平衡是因为左子树过深


image.png

2.右平衡操作:结点t的不平衡是因为右子树过深


image.png

实现代码:

package com.xx.tree;

import java.util.LinkedList;

/**
 * AVL树(二叉平衡树)
 * 
 */
public class AVLTree<E extends Comparable<E>> {
    Node<E> root;
    int size = 0;
    private static final int LH = 1;
    private static final int RH = -1;
    private static final int EH = 0;
    /**
     * 左旋转
     * @param x
     */
    public void left_rotate(Node<E> x) {
        if (x != null) {
            Node<E> y = x.right;// 先取到Y结点
            // 1。把贝塔作为X的右孩子
            x.right = y.left;
            if (y.left != null) {
                y.left.parent = x;
            }
            // 2。把Y移到原来X的位置
            y.parent = x.parent;
            if (x.parent == null) {
                root = y;
            } else {
                if (x.parent.left == x) {
                    x.parent.left = y;

                } else if (x.parent.right == x) {
                    x.parent.right = y;
                }
            }
            // 3。X作为Y的左孩子
            y.left = x;
            x.parent = y;
        }
    }
    /**
     * 右旋转
     * @param y
     */
    public void right_rotate(Node<E> y) {
        if (y != null) {
            Node<E> yl = y.left;

            // step1
            y.left = yl.right;
            if (yl.right != null) {
                yl.right.parent = y;
            }

            // step2
            yl.parent = y.parent;
            if (y.parent == null) {
                root = yl;
            } else {
                if (y.parent.left == y) {
                    y.parent.left = yl;
                } else if (y.parent.right == y) {
                    y.parent.right = yl;
                }
            }
            // step3
            yl.right = y;
            y.parent = yl;
        }
    }
    /**
     * 右平衡操作,即结点t的不平衡是因为右子树过深
    */
    public void rightBalance(Node<E> t) {
        Node<E> tr = t.right;
        switch (tr.balance) {
        case RH:// 新的结点插入到t的右孩子的右子树中
            left_rotate(t);
            t.balance = EH;
            tr.balance = EH;
            break;
        case LH:// 新的结点插入到t的右孩子的左子树中
            Node<E> trl = tr.left;
            switch (trl.balance) {
            case RH:
                t.balance = LH;
                tr.balance = EH;
                trl.balance = EH;
                break;
            case LH:
                t.balance = EH;
                tr.balance = RH;
                trl.balance = EH;
                break;
            case EH:
                tr.balance = EH;
                trl.balance = EH;
                t.balance = EH;
                break;

            }
            right_rotate(t.right);
            left_rotate(t);
            break;

        }
    }
    /**
     * 左平衡操作,即结点t的不平衡是因为左子树过深
    */
    public void leftBalance(Node<E> t) {
        Node<E> tl = t.left;
        switch (tl.balance) {
        case LH:
            right_rotate(t);
            tl.balance = EH;
            t.balance = EH;
            break;
        case RH:
            Node<E> tlr = tl.right;
            switch (tlr.balance) {
            case LH:
                t.balance = RH;
                tl.balance = EH;
                tlr.balance = EH;
                break;
            case RH:
                t.balance = EH;
                tl.balance = LH;
                tlr.balance = EH;
                break;
            case EH:
                t.balance = EH;
                tl.balance = EH;
                tlr.balance = EH;
                break;

            default:
                break;
            }
            left_rotate(t.left);
            right_rotate(t);
            break;

        }
    }
    //插入
    public boolean insertElement(E element) {
        Node<E> t = root;
        if (t == null) {
            root = new Node<E>(element, null);
            size = 1;
            root.balance = 0;
            return true;
        } else {
            // 开始找到要插入的位置
            int cmp = 0;
            Node<E> parent;
            Comparable<? super E> e = (Comparable<? super E>) element;
            do {
                parent = t;
                cmp = e.compareTo(t.elemet);
                if (cmp < 0) {
                    t = t.left;
                } else if (cmp > 0) {
                    t = t.right;
                } else {
                    return false;
                }
            } while (t != null);
            // 开始插入数据
            Node<E> child = new Node<E>(element, parent);
            if (cmp < 0) {
                parent.left = child;
            } else {
                parent.right = child;
            }
            // 节点已经放到了树上
            // 检查平衡,回溯查找
            while (parent != null) {
                cmp = e.compareTo(parent.elemet);
                if (cmp < 0) {
                    parent.balance++;
                } else {
                    parent.balance--;
                }
                if (parent.balance == 0) {// 如果插入后还是平衡树,不用调整
                    break;
                }
                if (Math.abs(parent.balance) == 2) {
                    // 出现了平衡的问题,需要修正
                    fixAfterInsertion(parent);
                    break;
                } else {
                    parent = parent.parent;
                }
            }
        }
        size++;
        return true;
    }

    public void showAVL(Node root) {
        LinkedList<Node> list = new LinkedList<Node>();
        list.offer(root);// 队列放入
        while (!list.isEmpty()) {
            Node node = list.pop();// 队列的取出
            System.out.println(node.elemet);
            if (node.left != null) {
                list.offer(node.left);
            }
            if (node.right != null) {
                list.offer(node.right);
            }
        }
    }

    private void fixAfterInsertion(Node<E> parent) {
        if (parent.balance == 2) {
            leftBalance(parent);
        }
        if (parent.balance == -2) {
            rightBalance(parent);
        }
    }

    public static class Node<E extends Comparable<E>> {
        E elemet;
        int balance = 0;// 平衡因子
        Node<E> left;
        Node<E> right;
        Node<E> parent;

        public Node(E elem, Node<E> pare) {
            this.elemet = elem;
            this.parent = pare;
        }

        public E getElemet() {
            return elemet;
        }

        public void setElemet(E elemet) {
            this.elemet = elemet;
        }

        public int getBalance() {
            return balance;
        }

        public void setBalance(int balance) {
            this.balance = balance;
        }

        public Node<E> getLeft() {
            return left;
        }

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

        public Node<E> getRight() {
            return right;
        }

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

        public Node<E> getParent() {
            return parent;
        }

        public void setParent(Node<E> parent) {
            this.parent = parent;
        }

    }

    public static void main(String[] arg0){
        Integer[] nums={5,8,2,0,1,-2};
            AVLTree<Integer> tree=new AVLTree<Integer>();
            for(int i=0;i<nums.length;i++){
                tree.insertElement(nums[i]);
            }
            tree.showAVL((Node) tree.root);
    }
}

红黑树

上次我们讲到AVL树。AVL树的性能(添加,删除)还是比较大的,为了解决这些操作上的性能的问题,我们会用到红黑树。(查询的效率和AVL是差不多的)
红黑树是对平衡树的改进,任意一个节点,他的左右子树的层次最多不超过一倍。

红黑树定义

image.png

插入节点

插入节点:先按照二叉排序树的方式插入一个节点,再查找最小不平衡子树,以最小不平衡子树进行下面的操作

1. 插入的是根节点
    直接将节点涂黑
2. 插入的节点的父节点是黑色
    不违背任何性质,不用调整
3. 插入节点的父节点是红色
    3.1 父节点是祖父节点的左孩子
        3.1.1 情况1:祖父节点的另一个子节点(叔叔节点)是红色
        将当前节点的父节点和叔叔节点涂黑,祖父节点涂红,把当前节点指向祖父节点,从新的当前节点开始算法
        3.1.2 情况2:叔叔节点是黑色
            3.1.2.1 当前节点是其父节点的右孩子
            当前节点的父节点做为新的当前节点,以新当前节点为支点左旋。
            3.1.2.2 当前节点是其父节点的左孩子
            父节点变为黑色,祖父节点变红色,再祖父节点为支点进行右旋
    3.2 父节点是祖父节点的右孩子
          参考3.1 将左全部变成右即可

删除节点

删除节点:先进行二叉排序树的删除操作,然后已替换节点作为当前节点进行后面的平衡操作

1.当前节点是红色
    直接把当前节点染成黑色,结束。
2.当前节点是黑色
    2.1  被删除节点是父节点的左孩子
         2.1.1 当前节点是根节点
               什么都不做
         2.1.2 当前节点x的兄弟节点是红色(此时父节点和兄弟节点的子节点分为黑)
               把父节点染成红色,兄弟节点染成黑色,对父节点进行左旋,重新设置x的兄弟节点
         2.1.3 当前节点x 的兄弟节点是黑色
               2.1.3.1 兄弟节点的两个孩子都是黑色
               将x的兄弟节点设为红色,设置x的父节点为新的x节点
               2.1.3.2 兄弟的右孩子是黑色,左孩子是红色
               将x兄弟节点的左孩子设为黑色,将x兄弟节点设置红色,将x的兄弟节点右旋,右旋后,重新设置x的兄弟节点。
               2.1.3.3 兄弟节点的右孩子是红色
               把兄弟节点染成当前节点父节点颜色,把当前节点父节点染成黑色,兄弟节点右孩子染成黑色,再以当前节点的父节点为支点进行左旋,算法结算
    2.2  被删除节点是父节点的右孩子
          参照2.1 把左设置为右

红黑树的应用:

Hashtable
TreeSet
TreeMap

计算机科学中的树

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

推荐阅读更多精彩内容

  • 目录 0.树0.1 一般树的定义0.2 二叉树的定义 1.查找树ADT 2.查找树的实现2.1 二叉查找树2.2 ...
    王侦阅读 7,112评论 0 3
  • 目录 1、什么是树 2、相关术语 3、二叉树 3.1、二叉树的类型 3.2、二叉树的性质 3.3、二叉树的结构 3...
    我哈啊哈啊哈阅读 2,536评论 0 10
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,654评论 0 13
  • 树的概述 树是一种非常常用的数据结构,树与前面介绍的线性表,栈,队列等线性结构不同,树是一种非线性结构 1.树的定...
    Jack921阅读 4,432评论 1 31
  • 突然想说一说需求,我将需求分成了强需求和弱需求,强需求可以强到早上起床做的第一件事情就是拿起手机上某APP,反之弱...
    六水君阅读 1,897评论 0 2