二叉樹相關概念
一. 二叉樹基本概念
在電腦科學中,二叉樹是每個結點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。二叉樹常被用於實現二叉查詢樹和二叉堆。二叉樹是每個結點最多有兩個子樹的樹結構。它有五種基本形態:二叉樹可以是空集;根可以有空的左子樹或右子樹;或者左、右子樹皆為空。
1. 二叉樹的分類
- (1)完全二叉樹——若設二叉樹的高度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第h層有葉子結點,並且葉子結點都是從左到右依次排布,這就是完全二叉樹。或者說只有最下面兩層結點的度可以小於2,並且最下層的 葉結點幾種在靠左的若干位置上。
- (2)滿二叉樹——除了葉結點外每一個結點都有左右子葉且葉子結點都處在最底層的二叉樹。
- (3)平衡二叉樹——平衡二叉樹又被稱為AVL樹(區別於AVL演算法),它是一棵二叉排序樹,且具有以下性質:它是一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。
- (4)
舉例:
1 面試題:如果一個完全二叉樹的結點總數為768個,求葉子結點的個數。 2 由二叉樹的性質知:n0=n2+1,將之帶入768=n0+n1+n2中得:768=n1+2n2+1,因為完全二叉樹度為1的結點個數要麼為0,要麼為1,那麼就把n1=0或者1都代入公式中,很容易發現n1=1才符合條件。所以算出來n2=383,所以葉子結點個數n0=n2+1=384。View Code3 4 總結規律:如果一棵完全二叉樹的結點總數為n,那麼葉子結點等於n/2(當n為偶數時)或者(n+1)/2(當n為奇數時)
2. 二叉樹的性質
性質1:二叉樹第i層上的結點數目最多為2i-1(i>=1)
性質2:深度為k的二叉樹至多有2k-1個結點(k>=1)
性質3:包含n個結點的二叉樹的高度至少為(log2n)+1
性質4:在任意一棵二叉樹中,若終端結點的個數為n0,度為2的結點數為n2,則n0=n2+1
3. 二叉樹的用途
(1)可以用作二叉查詢樹(也叫二叉搜尋樹):設x為二叉查詢樹中的一個結點,x結點包含關鍵字key,結點x的key值計為key[x]。如果y是x的左子樹中的一個結點,則key[y]<=key[x];如果y是x的右子樹的一個結點,則key[y]>=key[x]
note:在二叉查詢樹中有以下性質:
- (1)若任意結點的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值。
- (2)任意結點的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值。
- (3)任意結點的左、右子樹也分別為二叉查詢樹。
- (4)沒有鍵值相等的結點。
二. 相關術語
結點的度:結點擁有的子樹的數目. 與圖論中的“度”不同,樹的度是如下定義的:有根樹T中,結點x的子女數目稱為x的度。也就是:在樹中,結點有幾個分叉,度就是幾
一個有用的小公式:樹中結點數 = 總分叉數 +1。(這裡的分叉數就是所有結點的度之和)
葉子結點:度為0的結點
分支結點:度不為0的結點
樹的度:樹中結點的最大的度
層次:根結點的層次為1,其餘結點的層次等於該結點的雙親結點的層次加1-----(同深度)
樹的高度:樹中結點的最大層次。從下往上數,最下面是高度為1
樹的深度:從上往下數,最上面根部分是深度為1
森林:0個或多個不相交的樹組成。對森林加上一個根,森林即成為樹;刪去根,樹即成為森林。
舉例:
1 設樹T的度為4,其中度為1,2,3,4的節點個數分別為4,2,1,1,則T中的葉子數為? 2 3 解答: 4 葉子的度數為0;那麼設葉子數為x,則此樹的總分叉數為1*4+2*2+3*1+4*1=15;此樹的節點個數為16(此處涉及到一個公式;節點數=分叉數+1,由圖形便可以觀察出來)。又根據題目可以知道頂點數目還可以列出一個式子:4+2+1+1+x便可以得到等式:4+2+1+1+x=16;x=8為葉子數。 5 因為此題是資料結構中的問題:一般情況下都是有向樹,所以葉子節點的度數為0,要區分於離散數學中的無向樹葉子節點度為一。在資料結構中一般常用的公式為:二叉樹:度為0的節點數=度為2的節點數+1(n0=n2+1)此公式可由上述計算思想推導(一般在二叉樹那裡的公式多一些,樹中只要你明確定義,畫出圖來,便可以根據圖形尋找出規律來)View Code
三. 二叉樹的遍歷方式
- 先序遍歷:遍歷順序規則為【根左右】(DFS思想)
- 中序遍歷:遍歷順序規則為【左根右】(DFS思想)
- 後序遍歷:遍歷順序規則為【左右根】(DFS思想)
- 層次遍歷:實際上用的是廣度優先演算法BFS(Breadth-First Search)
比如:上面的二叉樹三種方式遍歷後的情況是:
先序遍歷:ABCDEFGHK
中序遍歷:BDCAEHGKF
後序遍歷:DCBHKGFEA
前三種遍歷方式的遞迴程式碼:
1 //後序遍歷,左=》右=》根 2 public void postorder_Traversal(TreeNode root) 3 { 4 if(root==null)return; 5 postorder_Traversal(root.left); 6 postorder_Traversal(root.right); 7 8 //訪問節點的邏輯程式碼塊 9 System.out.print(root.val+" "); 10 } 11 //前序遍歷,根=》左=》右 12 public void preorder_Traversal(TreeNode root) 13 { 14 if(root==null)return; 15 16 //訪問節點的邏輯程式碼塊 17 System.out.print(root.val+" "); 18 19 preorder_Traversal(root.left); 20 preorder_Traversal(root.right); 21 } 22 //中序遍歷,左=》根=》右 23 public void inorder_Traversal(TreeNode root) 24 { 25 if(root==null)return; 26 inorder_Traversal(root.left); 27 28 //訪問節點的邏輯程式碼塊 29 System.out.print(root.val+" "); 30 31 inorder_Traversal(root.right); 32 }View Code
層次遍歷思想:
根據層次遍歷的順序,每一層都是從左到右的遍歷輸出,藉助於一個佇列。
先將根節點入隊,當前節點是隊頭節點,將其出隊並訪問,如果當前節點的左節點不為空將左節點入隊,如果當前節點的右節點不為空將其入隊。所以出隊順序也是從左到右依次出隊。
1 import java.util.LinkedList; 2 3 public class LevelOrder 4 { 5 public void levelIterator(BiTree root) 6 { 7 if(root == null) 8 { 9 return ; 10 } 11 LinkedList<BiTree> queue = new LinkedList<BiTree>(); 12 BiTree current = null; 13 queue.offer(root);//將根節點入隊 14 while(!queue.isEmpty()) 15 { 16 current = queue.poll();//出隊隊頭元素並訪問 17 System.out.print(current.val +"-->"); 18 if(current.left != null)//如果當前節點的左節點不為空入隊 19 { 20 queue.offer(current.left); 21 } 22 if(current.right != null)//如果當前節點的右節點不為空,把右節點入隊 23 { 24 queue.offer(current.right); 25 } 26 } 27 28 } 29 30 }View Code
note:上面的root應該代表了整棵樹
或者:
1 public static void levelRead(TreeNode root) 2 { 3 if(root == null) return; 4 Queue<TreeNode> queue = new LinkedList<TreeNode>() ; 5 queue.add(root); 6 while(queue.size() != 0) 7 { 8 int len = queue.size(); 9 for(int i=0;i <len; i++) 10 { 11 TreeNode temp = queue.poll(); 12 System.out.print(temp.val+" "); 13 if(temp.left != null) queue.add(temp.left); 14 if(temp.right != null) queue.add(temp.right); 15 } 16 } 17 }View Code
note:poll是取出並且移除佇列的頭結點;peek是取出但是並不移除佇列的頭節點;offer將元素加入到佇列的末尾;
四. 二叉樹的建立