鏈表能夠很方便的存儲數據,但是,數據的組織只能是線性的,不能有層次的組織數據,且查找元素需要線性查找,複雜度O(n)。
二叉查找樹是一種按照排序組織數據的有層次的方式
它的特點是:
(1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
(2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
即:對於任何一個子樹,左子樹所有結點值小於根結點,右子樹所有結點值小於根結點。
查找、插入刪除的最優複雜度爲log(n)
在極端情況下,如果按照排序好的順序給二叉查找樹數據,那麼樹會退化成一個鏈表,複雜的同鏈表一樣。
那麼:
在隨機情況下,二叉查找樹的查找效率有多高呢?
測試代碼:
BST tree = new BST();
Random rand = new Random();
double totalTime = 0;
for (int i=0;i<=3000000; i++)
tree.Insert(rand.nextInt(10000000));
//tree.tranformIntoPerfectTree();
for (int i=0;i<100;i++) {
long time = System.currentTimeMillis();
tree.Contains(new BSTNode(rand.nextInt(10000000)));
totalTime += System.currentTimeMillis()-time;
}
System.out.println("在1000萬數據中查找1000次 平均耗時: "+totalTime/100);
測試結果:
隨機生成300萬個數據(太多內存受不了),加入二叉查找樹,測試100次,平均時長爲:67.63ms
爲了提高查找效率,我們希望二叉樹儘可能像一個完全二叉樹
即整個樹只有最下面兩層不滿,其他層均滿。
爲此,人們提出了兩種算法
(1)DSW
當存入一定數據之後,按照一定規則將二叉查找樹拉伸成鏈表,然後重新組織數據,使數據接近完全二叉樹。以下代碼使用這種方法重建二叉搜索樹。因爲這種方法是所有數據一起重建一棵樹,所以又叫“全局重建”
(2)AVL
存入一個數據之後,該數據並非影響整棵樹的平衡,因爲數據總是插在最下方。所以可以只進行局部調整。
這種方法又叫“局部重建”(這個方法將在下一篇博文實現)
來看看:
平衡之後的二叉查找樹的效率
測試代碼及測試條件同上,加入了平衡函數tranformIntoPerfectTree()
改變不大,這是因爲我們隨機生成數據,如果數據有一定規律,那麼效率會提高很多。
對於二叉查找樹,有如下:
屬性:
root:樹的根,作爲樹的入口
size:數的大小,不必要,可以使用時遍歷求得
方法:
Insert:插入
remove:刪除,並返回被刪除的數據
isEmpty:判斷樹是否爲空
Contains:是否包含某數據
rotateRight/rotateLeft:將樹進行旋轉,這是DSW方法中使用的子方法
transformIntoBackbone:DSW方法的第一步,把樹轉化成鏈表
tranformIntoPerfectTree:使用DSW方法全局重構二叉樹
dfs:深度優先搜索,返回搜索路徑,二叉搜索樹的中序遍歷是數據的排序,使用遞歸,消耗資源
bfs:廣度優先遍歷,使用內存較大
下面是二叉查找樹的代碼:
package BST;
import java.util.ArrayList;
import java.util.Random;
public class BST {
int size;
BSTNode root;
public BST() {
root = null;
size = 0;
}
public boolean isEmpty() {
return root == null;
}
public void Insert(int value) {
Insert(new BSTNode(value));
}
public boolean Contains(BSTNode node) {
return Contains(node, root);
}
public boolean Remove(BSTNode node) {
BSTNode[] fatherNode = new BSTNode[1];
BSTNode toDelete = Search(node, fatherNode);
if (toDelete == null)
return false;
if (toDelete.left == null && toDelete.right == null)
return false;
if (toDelete.left == null && toDelete.right != null) {
fatherNode[0].right = toDelete.right;
}
else if (toDelete.left != null && toDelete.right == null) {
fatherNode[0].left = toDelete.left;
}
else if (toDelete.left != null && toDelete.right != null) {
if(toDelete.right.right != null) {
toDelete.right.right.left = toDelete.right.left;
toDelete.right.left = toDelete.left;
}
else {
if(toDelete.right.left != null)
Insert(toDelete.right.left);
toDelete.right.left = toDelete.left;
}
if(root != fatherNode[0])
if (BSTNode.equals(fatherNode[0].right, toDelete))
fatherNode[0].right = toDelete.right;
else
fatherNode[0].left = toDelete.right;
else {
root = toDelete.right;
}
}
size--;
return true;
}
public void tranformIntoPerfectTree() {
if (isEmpty() || size == 1) return ;
transformIntoBackbone(); //轉化爲骨架結構(向右)
//System.out.println("________________________________________");
//printSelf();
int n = this.size;
int m = (int) Math.pow(2, Math.floor(Math.log10(n+1)/Math.log10(2))) - 1;
int cnt = n-m;
if(cnt>0) {
rotateLeft(null, root);
cnt--;
}
//System.out.println("m:"+m+" cnt: "+cnt);
//printSelf();
//System.exit(0);
BSTNode grand = root;
BSTNode now = root.right;
if(cnt>0)
while(true) {
//System.out.println("___________In While_____________________________");
//printSelf();
rotateLeft(grand, now);
//System.out.println("___________In While_____________________________");
//printSelf();
cnt--;
if(cnt==0) break;
grand = grand.right;
now = grand.right;
}
while(m>1) {
m=m/2;
int tmp = m;
if (tmp>=1) {
rotateLeft(null, root);
tmp--;
}
grand = root;
now = root.right;
//System.out.println("Pre Rotating: " + root.value + " " + root.right.value);
while(tmp>0) {
rotateLeft(grand, now);
tmp--;
if(tmp==0) break;
grand = grand.right;
now = grand.right;
}
}
}
public void transformIntoBackbone() {
BSTNode grand = null;
BSTNode now = root;
while(now != null) {
//System.out.println((grand==null?"null":grand.value) + " " + now.value);
if (now.left != null) {
rotateRight(grand, now);
if(grand!=null)
now = grand.right;
else
now = root;
//System.out.println("exce 1");
//System.out.println("___________In While_____________________________");
//printSelf();
}
else {
grand = now;
now = now.right;
//System.out.println("exce 2");
}
}
}
public void transformIntoBackboneLeft() {
BSTNode grand = null;
BSTNode now = root;
while(now != null) {
if (now.right != null) {
rotateLeft(grand, now);
if(grand!=null)
now = grand.left;
else
now = root;
}
else {
grand = now;
now = now.left;
}
}
}
private void rotateRight(BSTNode grand, BSTNode node) {
if (node.left == null) return ;
BSTNode nodeLeft = node.left,
nodeRight = node.right,
child = node.left,
childLeft = node.left.left,
chileRight = node.left.right;
node.left = chileRight;
child.right = node;
if(grand == null) {
root = child;
}
else {
grand.right = child;
}
}
private void rotateLeft(BSTNode grand, BSTNode node) {
if (node == null || node.right == null) return ;
BSTNode nodeLeft = node.left,
nodeRight = node.right,
child = node.right,
childLeft = node.right.left,
chileRight = node.right.right;
node.right = childLeft;
child.left = node;
if(grand == null) {
root = child;
}
else {
grand.right = child;
}
}
private BSTNode Search(BSTNode node, BSTNode[] fatherNode) {
if(isEmpty())
return null;
fatherNode[0] = root;
BSTNode search = root;
while(true) {
if(search == null) return null;
if (BSTNode.equals(search, node)) {return search;}
fatherNode[0] = search;
if (node.value < search.value)
search = search.left;
else
search = search.right;
}
}
private void Insert(BSTNode node) {
//System.out.print("Value: " + node.value + " ");
if (isEmpty()) {
root = node;
size = 1;
return ;
}
BSTNode search = root;
while(true) {
//System.out.print("node value: "+ search.value + " ");
if (node.value < search.value)
if (search.left == null) {
search.left = node;
//System.out.println("add to left ");
break;
}
else {
search = search.left;
//System.out.println("add to left ");
}
else
if (search.right == null) {
search.right = node;
//System.out.println("add to right ");
break;
}
else {
search = search.right;
//System.out.println("add to right ");
}
}
size++;
}
private boolean Contains(BSTNode targetNode, BSTNode nowNode) {
if (isEmpty())
return false;
if (nowNode == null)
return false;
if (BSTNode.equals(targetNode, nowNode))
return true;
return Contains(targetNode, nowNode.left) || Contains(targetNode, nowNode.right);
}
public void dfs(int[] result, int[] cnt, BSTNode node) {
if (isEmpty())
return ;
//System.out.println("To Node:" + node.value);
if (node.left != null) {
//System.out.print("L"+node.value+" ");
dfs(result, cnt, node.left);
}
result[cnt[0]++] = node.value;
if (node.right != null) {
//System.out.print("R"+node.value+" ");
dfs(result, cnt, node.right);
}
return ;
}
public void bfs(int[] result, int[] cnt) {
if (isEmpty())
return ;
ArrayList<BSTNode> list = new ArrayList();
list.add(root);
while(!list.isEmpty()) {
BSTNode now = list.remove(0);
if (now.left != null)
list.add(now.left);
if (now.right != null)
list.add(now.right);
result[cnt[0]++] = now.value;
}
}
public void printSelf() {
int[] result = new int[this.size];
int[] cnt = new int[1];
this.dfs(result, cnt, this.root);
System.out.println("\nDFS: ");
for(int i=0; i<result.length; i++)
System.out.print(result[i] + " ");
System.out.println(" Size: "+ this.size);
cnt[0] = 0;
result = new int[this.size];
this.bfs(result, cnt);
System.out.println("BFS: ");
for(int i=0; i<result.length; i++)
System.out.print(result[i] + " ");
System.out.println();
}
static public void main(String[] argv) {
BST tree = new BST();
Random rand = new Random();
double totalTime = 0;
for (int i=0;i<=3000000; i++)
tree.Insert(rand.nextInt(10000000));
tree.tranformIntoPerfectTree();
for (int i=0;i<100;i++) {
long time = System.currentTimeMillis();
tree.Contains(new BSTNode(rand.nextInt(10000000)));
totalTime += System.currentTimeMillis()-time;
}
System.out.println("在1000萬數據中查找1000次 平均耗時: "+totalTime/100);
//tree.Insert(10);tree.Insert(5);tree.Insert(11);tree.Insert(6);tree.Insert(3);
//tree.printSelf();
//tree.Remove(new BSTNode(14));
//tree.Remove(new BSTNode(10));
//tree.printSelf();
//tree.rotateRight(null, tree.root);
//tree.transformIntoBackbone();
//tree.printSelf();
//tree.transformIntoBackboneLeft();
//tree.printSelf();
//tree.tranformIntoPerfectTree();
//tree.printSelf();
}
}
class BSTNode{
public int value;
public BSTNode left;
public BSTNode right;
static boolean equals(BSTNode a, BSTNode b) {
if(a==b && b==null) return true;
if(a==null || b==null) return false;
return a.value == b.value;
}
public BSTNode(int value) {
this.value = value;
}
public BSTNode(int value, BSTNode left, BSTNode right) {
this.value = value;
this.left = left;
this.right = right;
}
}