红黑树-想说爱你不容易

前言:java

  记得在大一懵懵懂懂的时候就接触了红黑树的算法。但因为当时内功尚浅,没法将其内化,只是以为它很神奇,是个好算法,设计它的人很牛!现今重拾起这个算法,不得再也不次被它的精妙所折服!编写本文,是但愿以鄙人的理解将红黑树算法的精髓向博客园的园友陈述一番,也但愿对其有独特看法的朋友能不吝赐教。准备好了的话,咱们就开始吧~算法

--------------------------------------------this

Part I:BSTspa

  做为开始,咱们得先谈谈二叉树(Binary Search Tree)。设计

1.假设存在一个以下简单的键值字符表:code

Key Valueblog

A 2递归

C 1ip

B 6get

B 11

H 1

J 3

  要求你按照读入顺序创建这样一棵二叉查找树,建好以后要求可以进行对于的查询操做。

源于二分查找的思想,二叉查找树有这样一个特色:

  对于树上的任意一个结点,若是它有左右子结点的话,其结点大小一定大于其左子结点且小于其右子结点。

 

2.查找get(key)

 

因为单独创建一个二叉查找树起初很差分析,咱们就假设如今有一棵已经构造好二叉查找树。咱们仅须要思考如何在其上面进行查找操做。

根据二分查找的思想,咱们能够按照下面步骤进行查找:

Step1:将须要查找的key与二叉查找树的当前根节点的key做比较,获得比较结果后进行下面的step2;

Step2:若查找的key比根节点的key小,则递归从根节点的左子树进行一样的查找key操做;若比根节点的key大,则递归地从根节点的右子树进行一样的查找key操做;

若,查找的key恰好等于当前根节点的key,则返回当前key对应的value,结束!

 

3.插入put(key,value)

假设如今已经有了一个二叉查找树,咱们要插入一对键值(key-value)。源于查找过程的经验,咱们知道插入操做其实近似于查找操做。由于,咱们插入的时候一样是拿key跟当前根节点的key比较,以后再肯定是往左走仍是右走,或者是更新当前值(key=root.key时)。

 

Code:

 1 package com.gdufe.binarysearchtree;
 2 
 3 import java.io.File;
 4 import java.util.Scanner;
 5 
 6 public class BST<Key extends Comparable<Key>, Value> {
 7 
 8     Node root; // 维护根节点
 9 
10     class Node { // 二叉树的结点
11         Key key;
12         Value value;
13         Node left, right;
14 
15         public Node(Key key, Value value) { // 初始化结点
16             this.key = key;
17             this.value = value;
18         }
19     }
20 
21     public Value get(Key key) {
22         return get(root, key);
23     }
24 
25     //查找操做
26     public Value get(Node x, Key key) {
27         if (x == null)
28             return null;
29         int cmp = key.compareTo(x.key);
30         if (cmp < 0)
31             return get(x.left, key);
32         else if (cmp > 0)
33             return get(x.right, key);
34         else
35             return x.value;
36     }
37 
38     public void put(Key key, Value value) {
39         root = put(root, key, value);
40     }
41     //插入操做
42     public Node put(Node x, Key key, Value value) {
43         if (x == null)
44             return new Node(key, value);
45         int cmp = key.compareTo(x.key);
46         if (cmp < 0)
47             x.left = put(x.left, key, value);
48         else if (cmp > 0)
49             x.right = put(x.right, key, value);
50         else
51             x.value = value;
52         return x;
53     }
54 
55     public static void main(String[] args) throws Exception {
56         Scanner input = new Scanner(new File("data_BST.txt"));
57         BST<String, Integer> bst = new BST<String, Integer>();
58         while (input.hasNext()) {
59             String key = input.next();
60             int value = input.nextInt();
61             bst.put(key, value);
62         }
63         System.out.println(bst.get("H"));
64         System.out.println(bst.get("B"));
65     }
66 
67 }

 

输出结果:

1
11

 

 分析:

  插入或查找时,有可能最坏状况树不断恶意生长(垂直生长),此时的时间复杂度为:ON,平均的时间复杂度为:O(lgN)

----------------------------------------

Part II:RedBlackBST

1. 2-3

在二叉树的基础之上,咱们引入了平衡2-3树。简单地说,二叉树每一个结点至多只能有2个子结点(称为“2结点”),而如今咱们能够经过将2个结点“绑”在一块儿造成一个有3个子结点的“3结点”。见下图:

 

因为查找操做较简单,咱们重点讨论它的插入操做。一样基于上面所给的数据,见图:

 ------------------------------------------------

 2.红黑二叉查找树(简称“红黑树”)

  那么问题来了,咱们该如何实现这样一棵2-3树呢?正常的思惟固然是但愿在原先的Node结构中进行重构,再构造一个嵌套的BIGNode。但巧妙的地方就在这里,咱们能够以以前的二叉查找树为基础,把结点之间的连接分为“红连接”和“黑连接”。其中,红链接经过链接两个2结点组成3结点,黑链接是以前二叉查找树的普通链接。为了方便,咱们不妨把3结点统一表示为一条左斜的红色连接。如图:

 

  上面经过定义红黑树的规则实现咱们等价的2-3树结构,因而红黑树也就有了下面等价的定义。

含有红黑连接而且知足下列条件的二叉查找树:

1)红连接均为左连接

2)没有任何结点同时和2条红连接相连

3)任意空连接到根节点路径上的黑连接数相同

---------------------------------------------

既然从上面的阐述中,咱们得出 了“红黑树≈2-3树",咱们咱们紧接着用上面的数据构建咱们的红黑树,见图:

 

  其中,存在着3个关键操做:

左旋:当结点出现左子结点为黑,右子结点为红时,进行左旋转;

右旋:当结点出现左子结点以及左子结点的左结点均为红时,进行右旋转;

变色:当结点出现左右子结点均为红时,进行变色操做(2个子连接均变黑色,并将红连接向上传递!)

 具体,见下图:

Code:

  1 package com.gdufe.binarysearchtree;
  2 
  3 import java.io.File;
  4 import java.util.Scanner;
  5 
  6 public class RedBlackTree<Key extends Comparable<Key>, Value> {
  7 
  8     Node root; // 维护根节点
  9 
 10     final static boolean RED = true;
 11     final static boolean BLACK = false;
 12 
 13     class Node { // 二叉树的结点
 14         Key key;
 15         Value value;
 16         boolean color;
 17         Node left, right;
 18 
 19         public Node(Key key, Value value, boolean color) { // 初始化结点
 20             this.key = key;
 21             this.value = value;
 22             this.color = color;
 23         }
 24     }
 25 
 26     public Value get(Key key) {
 27         return get(root, key);
 28     }
 29 
 30     // 右旋
 31     public Node rotateRight(Node h) {
 32         Node x = h.left;
 33         h.left = x.right;
 34         x.right = h;
 35         x.color = h.color;
 36         h.color = RED;
 37         return x;
 38     }
 39 
 40     // 左旋
 41     public Node rotateLeft(Node h) {
 42         Node x = h.right;
 43         h.right = x.left;
 44         x.left = h;
 45         x.color = h.color;
 46         h.color = RED;
 47         return x;
 48     }
 49 
 50     // 变色处理
 51     public void flipColors(Node h) {
 52         h.left.color = BLACK;
 53         h.right.color = BLACK;
 54         h.color = RED;
 55     }
 56     public boolean isRed(Node x){
 57         if(x==null) return false;
 58         else return x.color;
 59     }
 60     public Value get(Node x, Key key) {
 61         if (x == null)
 62             return null;
 63         int cmp = key.compareTo(x.key);
 64         if (cmp < 0)
 65             return get(x.left, key);
 66         else if (cmp > 0)
 67             return get(x.right, key);
 68         else
 69             return x.value;
 70     }
 71 
 72     public void put(Key key, Value value) {
 73         root = put(root, key, value);
 74         root.color = BLACK;
 75     }
 76 
 77     public Node put(Node x, Key key, Value value) {
 78         if (x == null)
 79             return new Node(key, value, RED); // 添加的结点连接为红色
 80         int cmp = key.compareTo(x.key);
 81         if (cmp < 0)
 82             x.left = put(x.left, key, value);
 83         else if (cmp > 0)
 84             x.right = put(x.right, key, value);
 85         else {
 86             x.value = value;
 87         }
 88         // 判断是否须要左旋,右旋,变色操做
 89         if (x != null) {
 90             if (!isRed(x.left) && isRed(x.right))
 91                 x = rotateLeft(x); 
 92             if (isRed(x.left) && isRed(x.left.left))
 93                 x = rotateRight(x);
 94             if (isRed(x.left ) && isRed(x.right))
 95                 flipColors(x);
 96         }
 97 
 98         return x;
 99     }
100 
101     public static void main(String[] args) throws Exception {
102         Scanner input = new Scanner(new File("data_BST.txt"));
103         RedBlackTree<String, Integer> bst = new RedBlackTree<String, Integer>();
104         while (input.hasNext()) {
105             String key = input.next();
106             int value = input.nextInt();
107             bst.put(key, value);
108         }
109         System.out.println(bst.get("H"));
110         System.out.println(bst.get("B"));
111     }
112 
113 }

输出结果:

1
11

分析:

  有了上面3个关键操做以后,咱们保证了树的平衡性,即树不会再恶意生长。插入N个结点后,树的高度为:O(lgN)~O(2*lgN) (思考一下?)。因此,咱们获得插入和查找的总体时间复杂度均降为:O(lgN)。

--------------------------

结语:

不得不认可,红黑树算法堪称算法研究领域的非凡之做。在现今的汪洋信息时代,存在着上亿的数据。可是,当咱们用红黑树算法对其进行动态的增长和查找时,仅仅须要几十次操做便可完事儿,怎能不让人拍案叫绝!!

相关文章
相关标签/搜索