前面所討論的線性表元素之間都是一對一的關系,今天我們所看到的結構各元素之間卻是一對多的關系。樹在計算機中有著廣泛的應用,甚至在計算機的日常使用中,也可以看到樹形結構的身影,如下圖所示的windows資源管理器和應用程序的菜單都屬于樹形結構。樹形結構是一種典型的非線性結構,除了用于表示相鄰關系外,還可以表示層次關系。本文重點討論樹與二叉樹的基本結構和遍歷算法等內容。

一、好大一棵樹,綠色的祝福1.1 樹的基本概念

1.2 樹的基本術語
(1)不同的節點:根節點、內部節點、葉子節點以及節點的度

(2)節點的關系:雙親與孩子,爸爸回來了,爸爸去哪兒?
(3)節點的層次:結點的層次(Level)從根開始定義起,根為第一層,根的孩子為第二層。樹中結點的最大層次稱為樹的深度(Depth)或高度。

二、二叉樹又是個什么鬼2.1 從猜數字游戲引出二叉樹
回憶一下,當年某電視節目中會讓游戲參與者猜一個產品的價格,如果參與者在限定時間內猜對了,那么他就可以獲得這個產品。很多人都是一點點的提高數值來猜,但是這樣猜會很沒有效率。因此,很多聰明人都知道需要利用折半查找的思想去猜測。假定某個產品在100元的范圍內,那么可以在7次之內猜出結果來,如下圖所示:(由于是100以內的正整數,所以我們先猜50(100的一半),被告之“大了”,于是再猜25(50的一半),被告之“小了”,再猜37(25與50的中間數),小了,于是猜43,大了,40,大了,38,小了,39,完全正確。)

如上圖所示,對于這種在某個階段都是兩種結果的情形,比如開和關、0和1、真和假、上和下、對與錯,正面與反面等,都適合用樹狀結構來建模,而這種樹是一種很特殊的樹狀結構,叫做二叉樹。
2.2 二叉樹的順序存儲結構
二叉樹的順序存儲結構就是用一維數組存儲二叉樹中的結點。結點的存儲位置,也就是數組的下標要能體現結點之間的邏輯關系,比如雙親與孩子的關系,左右兄弟的關系等。

But,考慮一種極端的情況,一棵深度為k的右斜樹,它只有k個結點,卻需要分配2的k次方-1個存儲單元空間,這顯然是對存儲空間的浪費,所以,順序存儲結構一般只適用于完全二叉樹。
2.3 二叉樹的鏈式存儲結構
既然順序存儲適用性不強,我們就要考慮鏈式存儲結構。二叉樹每個結點最多有兩個孩子,所以為它設計一個數據域和兩個指針域是比較自然的想法,我們稱這樣的鏈表叫做二叉鏈表。其中data是數據域,lchild和rchild都是指針域,分別存放指向左孩子和右孩子的指針。

三、二叉樹的代碼實現3.1 二叉樹的C#代碼實現
(1)二叉樹節點的定義:
代碼語言:JavaScript代碼運行次數:0運行復制
/// <summary> /// 二叉樹的節點定義 /// </summary> /// <typeparam name="T">數據具體類型</typeparam> public class Node<t> { public T data { get; set; } public Node<t> lchild { get; set; } public Node<t> rchild { get; set; } public Node() { } public Node(T data) { this.data = data; } public Node(T data, Node<t> lchild, Node<t> rchild) { this.data = data; this.lchild = lchild; this.rchild = rchild; } }</t></t></t></t></t>
(2)二叉樹的創建實現:
代碼語言:javascript代碼運行次數:0運行復制
// Method01:判斷該二叉樹是否是空樹 public bool IsEmpty() { return this.root == null; } // Method02:在節點p下插入左孩子節點的data public void InsertLeft(Node<t> p, T data) { Node<t> tempNode = new Node<t>(data); tempNode.lchild = p.lchild; p.lchild = tempNode; } // Method03:在節點p下插入右孩子節點的data public void InsertRight(Node<t> p, T data) { Node<t> tempNode = new Node<t>(data); tempNode.rchild = p.rchild; p.rchild = tempNode; } // Method04:刪除節點p下的左子樹 public Node<t> RemoveLeft(Node<t> p) { if (p == null || p.lchild == null) { return null; } Node<t> tempNode = p.lchild; p.lchild = null; return tempNode; } // Method05:刪除節點p下的右子樹 public Node<t> RemoveRight(Node<t> p) { if (p == null || p.rchild == null) { return null; } Node<t> tempNode = p.rchild; p.rchild = null; return tempNode; }</t></t></t></t></t></t></t></t></t></t></t></t>
以上四個方法分別提供了新節點的插入以及移除的實現,我們可以針對某個節點進行插入左孩子有右孩子節點。
(3)二叉樹的遞歸遍歷:
首先我們通過幾張圖來看看二叉樹的三種基本遍歷:前序、中序以及后序遍歷;
①前序遍歷:若根節點不為空,則先訪問根節點,然后先序遍歷左子樹,最后先序遍歷右子樹;

②中序遍歷:若根節點不為空,則先中序遍歷左子樹,再訪問根節點,最后中序遍歷右子樹;

③后序遍歷:若根節點不為空,則首先后序遍歷左子樹,其次后序遍歷右子樹,最后訪問根節點;

代碼語言:javascript代碼運行次數:0運行復制
// Method01:前序遍歷 public void PreOrder(Node<t> node) { if (node != null) { // 根->左->右 Console.Write(node.data + " "); PreOrder(node.lchild); PreOrder(node.rchild); } } // Method02:中序遍歷 public void MidOrder(Node<t> node) { if (node != null) { // 左->根->右 MidOrder(node.lchild); Console.Write(node.data + " "); MidOrder(node.rchild); } } // Method03:后序遍歷 public void PostOrder(Node<t> node) { if (node != null) { // 左->右->根 PostOrder(node.lchild); PostOrder(node.rchild); Console.Write(node.data + " "); } } </t></t></t>
本次實現采用了遞歸的方式實現遍歷算法,主要是根據二叉樹三種遍歷(前序、中序以及后序遍歷)的要求,依次輸出各個節點的元素。至于非遞歸方式的遍歷算法以及層次遍歷算法會在下一篇中進行介紹。
3.2 測試二叉樹的遍歷方法
在上面的代碼中,我們實現了二叉樹的遞歸遍歷算法,這里我們通過一段簡單的測試代碼來構造一顆二叉樹,并進行遍歷。首先,通過下圖看看我們要創建的一顆二叉樹是什么鬼?

(1)測試代碼:
代碼語言:javascript代碼運行次數:0運行復制
static void MyBinaryTreeBasicTest() { // 構造一顆二叉樹,根節點為"A" MyBinaryTree<string> bTree = new MyBinaryTree<string>("A"); Node<string> rootNode = bTree.Root; // 向根節點"A"插入左孩子節點"B"和右孩子節點"C" bTree.InsertLeft(rootNode, "B"); bTree.InsertRight(rootNode, "C"); // 向節點"B"插入左孩子節點"D"和右孩子節點"E" Node<string> nodeB = rootNode.lchild; bTree.InsertLeft(nodeB, "D"); bTree.InsertRight(nodeB, "E"); // 向節點"C"插入右孩子節點"F" Node<string> nodeC = rootNode.rchild; bTree.InsertRight(nodeC, "F"); // 前序遍歷 Console.WriteLine("---------PreOrder---------"); bTree.PreOrder(bTree.Root); // 中序遍歷 Console.WriteLine(); Console.WriteLine("---------MidOrder---------"); bTree.MidOrder(bTree.Root); // 后序遍歷 Console.WriteLine(); Console.WriteLine("---------PostOrder---------"); bTree.PostOrder(bTree.Root); }</string></string></string></string></string>
(2)運行結果:

附件下載
本文實現的C#版二叉樹參考代碼下載:http://pan.baidu.com/s/1eQ1xmXs
參考資料
(1)程杰,《大話數據結構》
(2)陳廣,《數據結構(C#語言描述)》
(3)段恩澤,《數據結構(C#語言版)》
(4)楊俊明,《數據結構C#版筆記—樹與二叉樹》
(5)Frank Fan,《萬丈高樓平地起之C#實現二叉樹操作》
作者:周旭龍
出處:http://edisonchou.cnblogs.com
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。