二叉树,红黑树

什么是红黑树,二叉树能解决什么样的问题?

Posted by Jack on March 20, 2018

二叉树

什么是二叉树?

在计算机科学中,二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。

二叉树是一个连通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根结点的度不大于2。有了根结点之后,每个顶点定义了唯一的父结点,和最多2个子结点。然而,没有足够的信息来区分左结点和右结点。如果不考虑连通性,允许图中有多个连通分量,这样的结构叫做森林。

  • 前序遍历
    访问根节点
    按前序遍历左子树
    按前序遍历右子树
  • 中序遍历
    按中序遍历左子树
    访问根节点
    按中序遍历右子树
  • 后序遍历
    按后序遍历左子树
    按后序遍历右子树
    访问根节点

image
先序遍历:ABCDEFGHK

中序遍历:BDCAEHGKF

后序遍历:DCBHKGFEA

二叉查找树

二叉查找树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;

下图中这棵树,就是一颗典型的二叉查找树:

image

比如,我有一个任务,需要输入10万个数据,然后有两个操作:
1.添加(删除)一个整数。
2.询问第x大的数据。
比如,我给你 1,8,13,10(等等一堆数据)…….然后我询问第3大的数据,然后我插入18然后我询问第4大的数据我再插入9我再询问第2大的数据

image

1.查看根节点9: image

2.由于10 > 9,因此查看右孩子13: image

3.由于10 < 13,因此查看左孩子11: image

4.由于10 < 11,因此查看左孩子10,发现10正是要查找的节点: image image

二叉树翻转

class TreeNode{
    int val;
    //左孩子
    TreeNode left;
    //右孩子
    TreeNode right;
}

TreeNode mirrorTreeNode(TreeNode root){
        if(root == null){
            return null;
        }
        TreeNode left = mirrorTreeNode(root.left);
        TreeNode right = mirrorTreeNode(root.right);
        root.left = right;
        root.right = left;
        return root;
    }

二叉树的前序遍历

迭代解法

ArrayList<Integer> preOrder(TreeNode root){
        Stack<TreeNode> stack = new Stack<TreeNode>();
        ArrayList<Integer> list = new ArrayList<Integer>();
        if(root == null){
            return list;
        }
        stack.push(root);
        while(!stack.empty()){
            TreeNode node = stack.pop();
            list.add(node.val);
            if(node.right!=null){
                stack.push(node.right);
            }
            if(node.left != null){
                stack.push(node.left);
            }
            
        }
        return list;
    }

递归解法

ArrayList<Integer> preOrderReverse(TreeNode root){
        ArrayList<Integer> result = new ArrayList<Integer>();
        preOrder2(root,result);
        return result;
        
    }
    void preOrder2(TreeNode root,ArrayList<Integer> result){
        if(root == null){
            return;
        }
        result.add(root.val);
        preOrder2(root.left,result);
        preOrder2(root.right,result);
    }

二叉树的中序遍历

ArrayList<Integer> inOrder(TreeNode root){
        ArrayList<Integer> list = new ArrayList<<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode current = root;
        while(current != null|| !stack.empty()){
            while(current != null){
                stack.add(current);
                current = current.left;
            }
            current = stack.peek();
            stack.pop();
            list.add(current.val);
            current = current.right;
            
        }
        return list;
        
    }

二叉树的后序遍历

ArrayList<Integer> postOrder(TreeNode root){
        ArrayList<Integer> list = new ArrayList<Integer>();
        if(root == null){
            return list;
        }
        list.addAll(postOrder(root.left));
        list.addAll(postOrder(root.right));
        list.add(root.val);
        return list;
    }

在二叉树中插入节点

TreeNode insertNode(TreeNode root,TreeNode node){
        if(root == node){
            return node;
        }
        TreeNode tmp = new TreeNode();
        tmp = root;
        TreeNode last = null;
        while(tmp!=null){
            last = tmp;
            if(tmp.val>node.val){
                tmp = tmp.left;
            }else{
                tmp = tmp.right;
            }
        }
        if(last!=null){
            if(last.val>node.val){
                last.left = node;
            }else{
                last.right = node;
            }
        }
        return root;
    }

红黑树

image 1.节点是红色或黑色。
2.根节点是黑色。
3.每个叶子节点都是黑色的空节点(NIL节点)。
4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

下图中这棵树,就是一颗典型的红黑树: image

1.向原红黑树插入值为14的新节点: image 由于父节点15是黑色节点,因此这种情况并不会破坏红黑树的规则,无需做任何调整.

2.向原红黑树插入值为21的新节点: image 由于父节点22是红色节点,因此这种情况打破了红黑树的规则4(每个红色节点的两个子节点都是黑色),必须进行调整,使之重新符合红黑树的规则。

image

变色:

为了重新符合红黑树的规则,尝试把红色节点变为黑色,或者把黑色节点变为红色。

下图所表示的是红黑树的一部分,需要注意节点25并非根节点。因为节点21和节点22连续出现了红色,不符合规则4,所以把节点22从红色变成黑色:

image

但这样并不算完,因为凭空多出的黑色节点打破了规则5,所以发生连锁反应,需要继续把节点25从黑色变成红色: image

此时仍然没有结束,因为节点25和节点27又形成了两个连续的红色节点,需要继续把节点27从红色变成黑色: image

左旋转:

逆时针旋转红黑树的两个节点,使得父节点被自己的右孩子取代,而自己成为自己的左孩子。说起来很怪异,大家看下图: image 图中,身为右孩子的Y取代了X的位置,而X变成了自己的左孩子。此为左旋转。

右旋转:

顺时针旋转红黑树的两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子。大家看下图: image 图中,身为左孩子的Y取代了X的位置,而X变成了自己的右孩子。此为右旋转。

我们以刚才插入节点21的情况为例: image

首先,我们需要做的是变色,把节点25及其下方的节点变色: image

此时节点17和节点25是连续的两个红色节点,为了维持红黑树的规则,我们把节点8和节点17进行变色,由红色节点编程黑色节点。这样以来就形成了新的红黑树:

image

image

参考链接:
https://zhuanlan.zhihu.com/p/31805309
https://www.jianshu.com/p/0190985635eb