資料結構之自平衡二叉尋找樹(1)

來源:互聯網
上載者:User

今天開始,我們再來認識一個新的二叉樹,稱為自平衡二叉尋找樹。AVL樹是最先發明的自平衡二叉尋找樹。

AVL樹的特點是:對於樹中的任何節點,節點的左右子樹的高度差距最大為1,所以AVL樹也稱為高度平衡樹。AVL樹得名於它的發明者G.M. Adelson-Velsky和E.M.
Landis,他們在1962年的論文《An algorithm for the organization of information》中發表了它。

我們定義節點的平衡因子就是該節點的左子樹高度與右子樹高度之差,根據上面的描述我們可以知道,帶有+1,0,-1平衡因子的節點是平衡節點,其他的都為非平衡節點。當節點是非平衡的時候,我們需要對該節點進行平衡化。我們既可以將平衡因子儲存在節點的內部,也可以通過節點的高度計算出來。

高度為h的AVL樹,節點數N最多; 最少
( 其中 )。
最少節點數n如以費伯納西數列可以用數學歸納法證明:
= - 1 (是Fibonacci
polynomial)。
即:
= 0 (表示AVL Tree高度為0的節點總數)
= 1 (表示AVL Tree高度為1的節點總數)
= 2 (表示AVL Tree高度為2的節點總數)
= +
+ 1 (表示AVL Tree高度為h的節點總數)
換句話說,當節點數為N時,高度h最多為。

 

對於非平衡節點的平衡化一般有四種情況,現在列在下面:

這四種方式的代碼實現如下:

//單旋左子節點private AVLTreeNode rotateWithLeftChild(AVLTreeNode node){AVLTreeNode root=node.left;node.left=root.right;root.right=node;node.height=Math.max(height(node.left),height(node.right))+1;root.height=Math.max(height(root.left),height(root.right))+1;return root;}//單旋右子節點private AVLTreeNode rotateWithRightChild(AVLTreeNode node){AVLTreeNode root=node.right;node.right=root.left;root.left=node;node.height=Math.max(height(node.left),height(node.right))+1;root.height=Math.max(height(root.left),height(root.right))+1;return root;}//雙旋左子節點private AVLTreeNode doubleRotateLeftChild(AVLTreeNode node){node.left=rotateWithRightChild(node.left);return rotateWithLeftChild(node);}//雙旋右子節點private AVLTreeNode doubleRotateRightChild(AVLTreeNode node){node.left=rotateWithLeftChild(node.right);return rotateWithRightChild(node);}

 

AVL樹的操作包含插入,刪除和尋找三個基本操作,這三種情況在平均和最壞情況下的時間複雜度都為O(log n)。

插入:

向AVL樹插入可以通過如同它是未平衡的二叉尋找樹一樣把給定的值插入樹中,接著自底向上向根節點折回,於在插入期間成為不平衡的所有節點上進行旋轉來完成。因為折回到根節點的路途上最多有1.5乘logn個節點,而每次AVL旋轉都耗費恒定的時間,插入處理在整體上耗費O(logn)
時間。

刪除:

從AVL樹中刪除可以通過把要刪除的節點向下旋轉成一個葉子節點,接著直接剪除這個葉子節點來完成。因為在旋轉成葉子節點期間最多有log n個節點被旋轉,而每次AVL旋轉耗費恒定的時間,刪除處理在整體上耗費O(logn) 時間。

尋找:

可以像普通二叉尋找樹一樣的進行,所以耗費O(log n)時間,因為AVL樹總是保持平衡的。不需要特殊的準備,樹的結構不會由於尋找而改變。(這是與伸展樹尋找相對立的,它會因為尋找而變更樹結構。)

為了便於觀察自平衡二叉尋找樹和一般的二叉尋找樹的區別,我們下面將對同一個二叉樹給出遍曆的兩種不同結果,通過遍曆的結果我們可以知道二叉樹的結構情況。下面首先給出一般二叉尋找樹的程式:

package mianshiti;import java.util.*;public class BinarySearchTree {private TreeNode root=null;//根節點public static void main(String[] args) {// TODO Auto-generated method stubBinarySearchTree mytree=new BinarySearchTree();mytree.insert(4);mytree.insert(3);mytree.insert(2);mytree.insert(1);mytree.insert(5);System.out.println("遞迴方式實現前序走訪:");mytree.preorder(mytree.getRoot());System.out.println();System.out.println("遞迴方式實現中序遍曆:");mytree.inorder(mytree.getRoot());System.out.println();System.out.println("遞迴方式實現後序遍曆:");mytree.postorder(mytree.getRoot());System.out.println();System.out.println("非遞迴方式實現前序走訪:");mytree.nonRecursivePreorder(mytree.getRoot());System.out.println();System.out.println("非遞迴方式實現中序遍曆:");mytree.nonRecursiveInorder(mytree.getRoot());System.out.println();System.out.println("非遞迴方式實現後序遍曆:");mytree.nonRecursivePostorder(mytree.getRoot());System.out.println();}//前序走訪public void preorder(TreeNode node){if(node!=null){this.getNodeInfo(node);preorder(node.left);preorder(node.right);}}//中序遍曆public void inorder(TreeNode node){if(node!=null){inorder(node.left);this.getNodeInfo(node);inorder(node.right);}}//後序遍曆public void postorder(TreeNode node){if(node!=null){postorder(node.left);postorder(node.right);this.getNodeInfo(node);}}//非遞迴方式實現前序走訪public void nonRecursivePreorder(TreeNode node){Stack<TreeNode> mystack=new Stack<TreeNode>();if(node!=null){mystack.push(node);while(!mystack.isEmpty()){TreeNode temp=mystack.pop();this.getNodeInfo(temp);if(temp.right!=null){mystack.push(temp.right);}if(temp.left!=null){mystack.push(temp.left);}}}}//非遞迴方式實現中序遍曆public void nonRecursiveInorder(TreeNode node){Stack<TreeNode> mystack=new Stack<TreeNode>();while(node!=null){while(node!=null){if(node.right!=null){mystack.push(node.right);}mystack.push(node);node=node.left;}node=mystack.pop();while(!mystack.isEmpty()&&node.right==null){this.getNodeInfo(node);node=mystack.pop();}this.getNodeInfo(node);if(!mystack.isEmpty()){node=mystack.pop();}else{node=null;}}}//非遞迴方式實現後續遍曆public void nonRecursivePostorder(TreeNode node){Stack<TreeNode> mystack=new Stack<TreeNode>();TreeNode temp=node;while(node!=null){for(;node.left!=null;node=node.left){mystack.push(node);}while(node!=null&&(node.right==null||node.right==temp)){this.getNodeInfo(node);temp=node;if(mystack.isEmpty())return;node=mystack.pop();}mystack.push(node);node=node.right;}}//建構函式public BinarySearchTree(){this.root=null;}public TreeNode getRoot(){return this.root;}//添加一個節點public void insert(int value){TreeNode nextnode=new TreeNode(value);TreeNode temp=this.root;if(this.root==null){this.root=nextnode;nextnode.parent=null;return ;}while(temp!=null){if(value<temp.data){if(temp.left==null){temp.left=nextnode;nextnode.parent=temp;return;}else{temp=temp.left;}}else if(value>temp.data){if(temp.right==null){temp.right=nextnode;nextnode.parent=temp;return;}else{temp=temp.right;}}}}//擷取以begin為根節點的樹的最小關鍵字節點的引用private TreeNode minTreeNode(TreeNode begin){if(begin==null)return null;while(begin.left!=null){begin=begin.left;}return begin;}//擷取以begin為根節點的樹的最大關鍵字節點的引用private TreeNode maxTreeNode(TreeNode begin){if(begin==null)return null;while(begin.right!=null){begin=begin.right;}return begin;}//刪除一個節點public void delete(int value){if(this.root==null){System.out.println("樹為空白,無法刪除"+value);return;}TreeNode temp=this.root;while(temp!=null){if(value>temp.data){if(temp.right==null){System.out.println("樹中沒有要刪除的項");}else{temp=temp.right;}}else if(value<temp.data){if(temp.left==null){System.out.println("樹中沒有要刪除的項");}else{temp=temp.left;}}else{//找到了這個節點if(temp.left==null&&temp.right==null)//該節點是分葉節點{if(temp.parent.right==temp){temp.parent.right=null;}else{temp.parent.left=null;}}else if(temp.right!=null&&temp.left==null)//左子樹為空白,右子樹不為空白{if(temp.parent.right==temp){temp.parent.right=temp.right;temp.right.parent=temp.parent;}else{temp.parent.left=temp.right;temp.right.parent=temp.parent;}}else if(temp.left!=null&&temp.right==null)//左子樹不為空白,右子樹為空白{if(temp.parent.right==temp){temp.parent.right=temp.left;temp.left.parent=temp.parent;}else{temp.parent.left=temp.left;temp.left.parent=temp.parent;}}else//左右子樹都不為空白{TreeNode replacenode=minTreeNode(temp.right);delete(replacenode.data);temp.data=replacenode.data;}break;}}}public void getNodeInfo(TreeNode treenode){if(treenode!=null){System.out.print(treenode.data+" ");}}}class TreeNode{public int data;public TreeNode left;//左子節點public TreeNode right;//右子節點public TreeNode parent;//父節點//建構函式public TreeNode(int data){this(data,null,null,null);}public TreeNode(int data,TreeNode left,TreeNode right,TreeNode parent){this.data=data;this.left=left;this.right=right;this.parent=parent;}}

運行結果如下所示:

遞迴方式實現前序走訪:4 3 2 1 5 遞迴方式實現中序遍曆:1 2 3 4 5 遞迴方式實現後序遍曆:1 2 3 5 4 非遞迴方式實現前序走訪:4 3 2 1 5 非遞迴方式實現中序遍曆:1 2 3 4 5 非遞迴方式實現後序遍曆:1 2 3 5 4 

接下來,我們再給出自平衡二叉尋找樹的實現:

package mianshiti;import java.util.Stack;//AVL樹的實現public class AVLTree {AVLTreeNode rootnode=null;/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubAVLTree mytree=new AVLTree();mytree.insert(4);mytree.insert(3);mytree.insert(2);mytree.insert(1);mytree.insert(5);System.out.println("遞迴方式實現前序走訪:");mytree.preorder(mytree.getRoot());System.out.println();System.out.println("遞迴方式實現中序遍曆:");mytree.inorder(mytree.getRoot());System.out.println();System.out.println("遞迴方式實現後序遍曆:");mytree.postorder(mytree.getRoot());System.out.println();System.out.println("非遞迴方式實現前序走訪:");mytree.nonRecursivePreorder(mytree.getRoot());System.out.println();System.out.println("非遞迴方式實現中序遍曆:");mytree.nonRecursiveInorder(mytree.getRoot());System.out.println();System.out.println("非遞迴方式實現後序遍曆:");mytree.nonRecursivePostorder(mytree.getRoot());System.out.println();}public AVLTreeNode getRoot(){return rootnode;}//前序走訪public void preorder(AVLTreeNode node){if(node!=null){this.getNodeInfo(node);preorder(node.left);preorder(node.right);}}//中序遍曆public void inorder(AVLTreeNode node){if(node!=null){inorder(node.left);this.getNodeInfo(node);inorder(node.right);}}//後序遍曆public void postorder(AVLTreeNode node){if(node!=null){postorder(node.left);postorder(node.right);this.getNodeInfo(node);}}//非遞迴方式實現前序走訪public void nonRecursivePreorder(AVLTreeNode node){Stack<AVLTreeNode> mystack=new Stack<AVLTreeNode>();if(node!=null){mystack.push(node);while(!mystack.isEmpty()){AVLTreeNode temp=mystack.pop();this.getNodeInfo(temp);if(temp.right!=null){mystack.push(temp.right);}if(temp.left!=null){mystack.push(temp.left);}}}}//非遞迴方式實現中序遍曆public void nonRecursiveInorder(AVLTreeNode node){Stack<AVLTreeNode> mystack=new Stack<AVLTreeNode>();while(node!=null){while(node!=null){if(node.right!=null){mystack.push(node.right);}mystack.push(node);node=node.left;}node=mystack.pop();while(!mystack.isEmpty()&&node.right==null){this.getNodeInfo(node);node=mystack.pop();}this.getNodeInfo(node);if(!mystack.isEmpty()){node=mystack.pop();}else{node=null;}}}//非遞迴方式實現後續遍曆public void nonRecursivePostorder(AVLTreeNode node){Stack<AVLTreeNode> mystack=new Stack<AVLTreeNode>();AVLTreeNode temp=node;while(node!=null){for(;node.left!=null;node=node.left){mystack.push(node);}while(node!=null&&(node.right==null||node.right==temp)){this.getNodeInfo(node);temp=node;if(mystack.isEmpty())return;node=mystack.pop();}mystack.push(node);node=node.right;}}public void getNodeInfo(AVLTreeNode node){if(node!=null){System.out.print(node.data+" ");}}public void insert(int data){rootnode=insert(data,rootnode);}private AVLTreeNode insert(int data,AVLTreeNode root){if(root==null){AVLTreeNode node=new AVLTreeNode(data);return node;}if(data<root.data){root.left=insert(data,root.left);}else if(data>root.data){root.right=insert(data,root.right);}else{System.out.println("已存在"+data);}return balance(root);}//將二叉樹平衡化private AVLTreeNode balance(AVLTreeNode root){if(root==null)return root;if(height(root.left)-height(root.right)>1){if(height(root.left.left)>=height(root.left.right)){root=rotateWithLeftChild(root);}else{root=doubleRotateLeftChild(root);}}else if(height(root.right)-height(root.left)>1){if(height(root.right.right)>=height(root.right.left)){root=rotateWithRightChild(root);}else{root=doubleRotateRightChild(root);}}root.height=Math.max(height(root.left), height(root.right))+1;return root;}//單旋左子節點private AVLTreeNode rotateWithLeftChild(AVLTreeNode node){AVLTreeNode root=node.left;node.left=root.right;root.right=node;node.height=Math.max(height(node.left),height(node.right))+1;root.height=Math.max(height(root.left),height(root.right))+1;return root;}//單旋右子節點private AVLTreeNode rotateWithRightChild(AVLTreeNode node){AVLTreeNode root=node.right;node.right=root.left;root.left=node;node.height=Math.max(height(node.left),height(node.right))+1;root.height=Math.max(height(root.left),height(root.right))+1;return root;}//雙旋左子節點private AVLTreeNode doubleRotateLeftChild(AVLTreeNode node){node.left=rotateWithRightChild(node.left);return rotateWithLeftChild(node);}//雙旋右子節點private AVLTreeNode doubleRotateRightChild(AVLTreeNode node){node.left=rotateWithLeftChild(node.right);return rotateWithRightChild(node);}//返回一個節點的高度private int height(AVLTreeNode node){return node==null?-1:node.height;}}//定義AVL樹的節點class AVLTreeNode{int data;AVLTreeNode left;AVLTreeNode right;int height;//建構函式public AVLTreeNode(int data){this(data,null,null);}public AVLTreeNode(int data,AVLTreeNode left,AVLTreeNode right){this.data=data;this.left=left;this.right=right;this.height=0;}}

運行結果如下所示:

遞迴方式實現前序走訪:3 2 1 4 5 遞迴方式實現中序遍曆:1 2 3 4 5 遞迴方式實現後序遍曆:1 2 5 4 3 非遞迴方式實現前序走訪:3 2 1 4 5 非遞迴方式實現中序遍曆:1 2 3 4 5 非遞迴方式實現後序遍曆:1 2 5 4 3 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.