二叉樹的基本概念 在電腦中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。 二叉樹是一個連通的無環圖,並且每一個頂點的度不大於3。有根二叉樹還要滿足根結點的度不大於2。有了根節點之後,每個頂點定義了唯一的父節 ...
二叉樹的基本概念
在電腦中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。
二叉樹是一個連通的無環圖,並且每一個頂點的度不大於3。有根二叉樹還要滿足根結點的度不大於2。有了根節點之後,每個頂點定義了唯一的父節點,和最多2個子節點。然而,沒有足夠的信息來區分左節點和右節點。
從邏輯上講二叉樹有五種形態,如上圖:
二叉樹相關的術語
- 樹的節點(node):包含一個數據元素及若幹指向子樹的分支;
- 孩子節點(child node):節點的子樹的根稱為該節點的孩子;
- 雙親節點:B 節點是A 節點的孩子,則A結點是B 節點的雙親;
- 兄弟節點:同一雙親的孩子節點;
- 堂兄結點:同一層上節點;
- 祖先節點: 從根到該節點的所經分支上的所有節點
- 子孫節點:以某節點為根的子樹中任一節點都稱為該節點的子孫
- 節點層:根節點的層定義為1;根的孩子為第2層節點,依此類推;
- 樹的深度:樹中最大的節點層
- 節點的度:節點子樹的個數
- 樹的度: 樹中最大的節點度。
- 葉子結點:也叫終端節點,是度為 0 的節點;
- 分枝結點:度不為0的節點;
- 有序樹:子樹有序的樹,如:家族樹;
- 無序樹:不考慮子樹的順序;
二叉樹的特性
- 在非空二叉樹中,第i層的節點總數不超過 , i>=1;
- 深度為h的二叉樹最多有 個結點(h>=1),最少有h個節點;
- 對於任意一棵二叉樹,如果其葉節點數為N0,而度數為2的節點總數為N2,則N0=N2+1;
- 具有n個節點的完全二叉樹的深度為
- 有N個節點的完全二叉樹各節點如果用順序方式存儲,則節點之間有如下關係:若I為節點編號則 如果I>1,則其父節點的編號為I/2;如果2*I<=N,則其左兒子(即左子樹的根節點)的編號為2*I;若2*I>N,則無左兒子;如果2*I+1<=N,則其右兒子的節點編號為2*I+1;若2*I+1>N,則無右兒子。
- 給定N個節點,能構成h(N)種不同的二叉樹。h(N)為卡特蘭數的第N項。h(n)=C(2*n,n)/(n+1)。
- 設有i個枝點,I為所有枝點的道路長度總和,J為葉的道路長度總和J=I+2i
二叉樹的遍歷
遍歷是對樹的一種最基本的運算,所謂遍歷二叉樹,就是按一定的規則和順序走遍二叉樹的所有節點,使每一個節點都被訪問一次,而且只被訪問一次。由於二叉樹是非線性結構,因此,樹的遍歷實質上是將二叉樹的各個結點轉換成為一個線性序列來表示。
設L、D、R分別表示遍歷左子樹、訪問根節點和遍歷右子樹, 則對一棵二叉樹的遍歷有三種情況:DLR(稱為先序遍歷),LDR(稱為中序遍歷),LRD (稱為後序遍歷)
先序遍歷(DLR):
首先訪問根,再先序遍歷左子樹,最後先序遍歷右子樹。如上圖遍歷的結果是:ABCDE
中序遍歷(LDR):
首先中序遍歷左子樹,再訪問根,最後中序遍歷右子樹,如上圖遍歷的結果是:CBDAE
後序遍歷(LRD):
首先後序遍歷左子樹,再後序遍歷右子樹,最後訪問根。如上圖遍歷的結果是:CDBEA
傳統遞歸對二叉樹的遍歷
public class Node { public Node(int i) { Data = i; } public int Data { get; set; } public Node Left { get; set; } public Node Right { get; set; } public void Display(string lr) { Console.Write(lr + ": " + Data); }
先序遍歷
public void DLRDisplay(Node theNode) { if (theNode != null) { theNode.Display("InOrder"); InOrder(theNode.Left); InOrder(theNode.Right); } }
中序遍歷
public void LDRDisplay(Node theNode) { if (theNode != null) { InOrder(theNode.Left); theNode.Display("InOrder"); InOrder(theNode.Right); } }
後序遍歷
public void LRDDisplay(Node theNode) { if (theNode != null) { InOrder(theNode.Left); InOrder(theNode.Right); theNode.Display("InOrder"); } }
組合模式實現二叉樹的三種遍歷
代碼實現
public enum Order { DLR,//先序遍歷 LDR,//中序遍歷 LRD //後序遍歷 } public abstract class AbstractTreeNode { protected string name; public AbstractTreeNode(string name) { this.name = name; } public AbstractTreeNode Left { get; set; } public AbstractTreeNode Right { get; set; } public abstract void Display(Order order); } public class TreeNode : AbstractTreeNode { public TreeNode(string name) : base(name) { } public override void Display(Order order) { switch (order) { case Order.DLR:// D->L->R DLRDisplay(order); break; case Order.LDR://L->D->R LDRDisplay(order); break; case Order.LRD://L->R->D LRDDisplay(order); break; } } private void DLRDisplay(Order order) { Console.Write(name); if (Left != null) { Left.Display(order); } if (Right != null) { Right.Display(order); } } private void LDRDisplay(Order order) { if (Left != null) { Left.Display(order); } Console.Write(name); if (Right != null) { Right.Display(order); } } private void LRDDisplay(Order order) { if (Left != null) { Left.Display(order); } if (Right != null) { Right.Display(order); } Console.Write(name); } } public class LeafNode : AbstractTreeNode { public LeafNode(string name) : base(name) { } public override void Display(Order order) { Console.Write(name); } }
測試:
/// <summary> /// A /// / \ /// B E /// / \ /// C D /// </summary> /// <param name="args"></param> static void Main(string[] args) { AbstractTreeNode rootA, b, c, d, e; rootA = new TreeNode("A"); b = new TreeNode("B"); c = new LeafNode("C"); d = new LeafNode("D"); e = new LeafNode("E"); rootA.Left = b; rootA.Right = e; b.Left = c; b.Right = d; rootA.Display(Order.DLR);//output->ABCDE Console.WriteLine(); rootA.Display(Order.LDR);//output->CBDAE Console.WriteLine(); rootA.Display(Order.LRD);//output->CDBEA Console.ReadKey(); }
結果:
先序遍歷的結果:ABCDE
中序遍歷的結果:CBDAE
後序遍歷的結果:CDBEA
在這裡其實可以省略掉葉子節點類,只需要一個實現類TreeNode就可以了這樣就變得更簡單了:
代碼中可以刪掉LeafNode類,客戶端只針對TreeNode實現就可以了。因為這裡沒有針對複雜的樹枝節點的管理操作。因此不需要分別對待樹枝節點和葉子節點。
public enum Order { DLR,//先序遍歷 LDR,//中序遍歷 LRD //後序遍歷 } public abstract class AbstractTreeNode { protected string name; public AbstractTreeNode(string name) { this.name = name; } public AbstractTreeNode Left { get; set; } public AbstractTreeNode Right { get; set; } public abstract void Display(Order order); } public class TreeNode : AbstractTreeNode { public TreeNode(string name) : base(name) { } public override void Display(Order order) { switch (order) { case Order.DLR:// D->L->R DLRDisplay(order); break; case Order.LDR://L->D->R LDRDisplay(order); break; case Order.LRD://L->R->D LRDDisplay(order); break; } } private void DLRDisplay(Order order) { Console.Write(name); if (Left != null) { Left.Display(order); } if (Right != null) { Right.Display(order); } } private void LDRDisplay(Order order) { if (Left != null) { Left.Display(order); } Console.Write(name); if (Right != null) { Right.Display(order); } } private void LRDDisplay(Order order) { if (Left != null) { Left.Display(order); } if (Right != null) { Right.Display(order); } Console.Write(name); } }
測試:
/// <summary> /// A /// / \ /// B E /// / \ /// C D /// </summary> /// <param name="args"></param> static void Main(string[] args) { AbstractTreeNode rootA, b, c, d, e; rootA = new TreeNode("A"); b = new TreeNode("B"); c = new TreeNode("C"); d = new TreeNode("D"); e = new TreeNode("E"); rootA.Left = b; rootA.Right = e; b.Left = c; b.Right = d; rootA.Display(Order.DLR);//output->ABCDE Console.WriteLine(); rootA.Display(Order.LDR);//output->CBDAE Console.WriteLine(); rootA.Display(Order.LRD);//output->CDBEA Console.ReadKey(); }
結果:
先序遍歷的結果:ABCDE
中序遍歷的結果:CBDAE
後序遍歷的結果:CDBEA
兩次測試的結果是一樣的。