TreeMap是基于红黑树结构实现的一种Map,要分析TreeMap的实现首先就要对红黑树有所了解。
要了解什么是红黑树,就要了解它的存在主要是为了解决什么问题,对比其他数据结构比如数组,链表,Hash表等树这种结构又有什么优点。
1.二叉查询树、红黑树介绍
以下为个人理解,有误请拍砖。。。
下面我尽可能用通俗易懂的语言,简单总结一下数组,链表,Hash表以及树的优缺点。
1.数组,优点:(1)随机访问效率高(根据下标查询),(2)搜索效率较高(可使用折半方法)。缺点:(1)内存连续且固定,存储效率低。(2)插入和删除效率低(可能会进行数组拷贝或扩容)。
2.链表,优点:(1)不要求连续内存,内存利用率高,(2)插入和删除效率高(只需要改变指针指向)。缺点:(1)不支持随机访问,(2)搜索效率低(需要遍历)。
3.Hash表:优点:(1)搜索效率高,(2)插入和删除效率较高,缺点:(1)内存利用率低(基于数组),(2)存在散列冲突。
上面说的话比较啰嗦,再精炼一下:数组查询好、插入和删除差且浪费内存;链表插入和删除好、查询差;Hash表查询好、插入和删除也不错但是浪费内存。
也就是说,查询好的插入和删除就差,插入和删除好的查询就差,好不容易有一个查询、插入和删除都不错的,但是却又浪费内存。哎,好苦恼啊,怎么办呢,愁死我啦。能不能做到查询、插入、删除效率都很高,又不浪费内存呢。答案当然是不能!哎,还是好愁人,烦死啦。但是可以做到查询、插入、删除效率比较高,又不浪费内存。哇塞,这是什么东东,这么牛掰,这就是二叉查询树(又叫二叉排序树,又叫二叉搜索树)。你说二叉树这么好,那有什么缺点吗,有!就是算法复杂。
那么什么是二叉查找树呢,它又有哪些特点呢?(以下见于百度百科)
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉查找树;
(4)没有键值相等的节点。
按照二叉查找树存储的数据,对元素的搜索效率是非常高的,比如上图中如果要查找值为48的节点,只需要遍历4个节点就能完成。理论上,一颗平衡的二叉查找树的任意节点平均查找效率为树的高度h,即O(lgn)。但是如果二叉查找树的失去平衡(元素全在一侧),搜索效率就退化为O(n),因此二叉查找树的平衡是搜索效率的关键所在。而红黑树就是靠红黑规则来维持二叉查找树的平衡性。
(一颗失去平衡的二叉树)
简单用代码描述上面的二叉查找树,试试看:
1 public class BinaryTree { 2 3 // 二叉树的根节点 4 public TreeNode rootNode ; 5 // 记录搜索深度 6 public int count; 7 8 /** 9 * 利用传入一个数组来建立二叉树 10 */ 11 public BinaryTree(int[] data) { 12 for (int i = 0; i < data. length; i++) { 13 addNodeToTree(data[i]); 14 } 15 } 16 17 /** 18 * 将指定的值加入到二叉树中适当的节点 19 */ 20 private void addNodeToTree(int value) { 21 TreeNode currentNode = rootNode; 22 // 建立树根 23 if (rootNode == null) { 24 rootNode = new TreeNode(value); 25 return; 26 } 27 28 // 建立二叉树 29 while (true) { 30 // 新增的value比节点的value小,则在左子树 31 if (value < currentNode.value ) { 32 if (currentNode.leftNode == null) { 33 currentNode. leftNode = new TreeNode(value); 34 return; 35 } else { 36 currentNode = currentNode. leftNode; 37 } 38 } else { // 新增的value比节点的value大,在右子树 39 if (currentNode.rightNode == null) { 40 currentNode. rightNode = new TreeNode(value); 41 return; 42 } else { 43 currentNode = currentNode. rightNode; 44 } 45 } 46 } 47 } 48 49 /** 50 * 中序遍历(左子树 -树根- 右子树) 51 */ 52 public void inOrder(TreeNode node) { 53 if (node != null) { 54 inOrder(node. leftNode); 55 System. out.print("[" + node.value + "]"); 56 inOrder(node. rightNode); 57 } 58 } 59 60 /** 61 * 前序遍历(树根 -左子树- 右子树) 62 */ 63 public void preOrder(TreeNode node) { 64 if (node != null) { 65 System. out.print("[" + node.value + "]"); 66 preOrder(node. leftNode); 67 preOrder(node. rightNode); 68 } 69 } 70 71 /** 72 * 后序遍历(左子树 -右子树- 树根) 73 */ 74 public void postOrder(TreeNode node) { 75 if (node != null) { 76 postOrder(node. leftNode); 77 postOrder(node. rightNode); 78 System. out.print("[" + node.value + "]"); 79 } 80 } 81 82 /** 83 * 从二叉树中查找指定value 84 */ 85 public boolean findTree(TreeNode node, int value) { 86 if (node == null) { 87 System. out.println("共搜索" + count + "次"); 88 return false; 89 } else if (node.value == value) { 90 System. out.println("共搜索" + count + "次"); 91 return true; 92 } else if (value < node.value) { 93 count++; 94 return findTree(node.leftNode , value); 95 } else { 96 count++; 97 return findTree(node.rightNode , value); 98 } 99 } 100 101 /** 102 * 利用中序遍历进行排序 103 */ 104 public void sort() { 105 this.inOrder(rootNode ); 106 } 107 108 class TreeNode { 109 int value ; 110 TreeNode leftNode; 111 TreeNode rightNode; 112 113 public TreeNode(int value) { 114 this.value = value; 115 this.leftNode = null; 116 this.rightNode = null; 117 } 118 } 119 120 public static void main(String[] args) { 121 int[] content = { 50, 35, 27, 45, 40, 48, 78, 56, 90 }; 122 123 BinaryTree tree = new BinaryTree(content); 124 System. out.println("前序遍历:" ); 125 tree.preOrder(tree. rootNode); 126 System. out.println("\n中序遍历:" ); 127 tree.inOrder(tree. rootNode); 128 System. out.println("\n后序遍历:" ); 129 tree.postOrder(tree. rootNode); 130 131 System. out.println("\n\n开始搜索:" ); 132 boolean isFind = tree.findTree(tree.rootNode, 48); 133 System. out.println("是否搜索到" + 48 + ":" + isFind); 134 135 System. out.println("\n进行排序:" ); 136 tree.sort(); 137 } 138 }
看下运行结果:
1 前序遍历: 2 [50][35][27][45][40][48][78][56][90] 3 中序遍历: 4 [27][35][40][45][48][50][56][78][90] 5 后序遍历: 6 [27][40][48][45][35][56][90][78][50] 7 8 开始搜索: 9 共搜索3次 10 是否搜索到48:true 11 12 进行排序: 13 [27][35][40][45][48][50][56][78][90]