《資料結構與演算法C#語言描述》筆記12_二叉樹和二叉查詢樹
樹的定義
樹,由邊連線的一些列節點。樹是一種非線性的資料結構。
根節點,樹上最高的節點。
父節點,某個節點的上層節點。
子節點,某個節點的下層節點。
葉子,沒有任何子節點。
二叉樹
二叉樹,子節點的數量不超過兩個的樹。
父節點的兩個節點分別稱為左節點和有節點。
二叉查詢樹,是一種較小資料值儲存在左節點,較大資料值儲存在有節點的二叉樹。
一個節點Node類和一個二叉查詢樹BinarySearchTree類。
Node類包含資料成員:資料值、左節點、右節點。
BinarySearchTree類包含資料成員:根節點。和一個插入節點的方法。
public class Node
{
public
public Node Left;
public Node Right;
public voidDisplayNode()
{ Console.Write(Data +""); }
public Node(int v)
{Data = v; }
public Node()
{Data = 0; }
}
public class BinarySearchTree
{
public Node root;
public BinarySearchTree()
{root = null; }
public void Insert(int i)
{
Node newNode = new Node();
newNode.Data = i;
if (root == null)
{
root = newNode;
}
else
{
Node
Node parent;///下一次的父節點
while (true)
{
parent = current;
if(i < current.Data)
{
current = current.Left;///下一次的當前節點,遞迴
if (current ==null)
{
parent.Left = newNode;
break;
}
}
else
{
current =current.Right;///下一次的當前節點,遞迴
if (current ==null)
{
parent.Right =newNode;
break;
}
}
}
}
}
public void Insert1(int i)
{
Node newNode = new Node();
newNode.Data = i;
if (root == null)
{
root = newNode;
}
else
{
Node current = root;
while (true)
{
if(i < current.Data)
{
if (current.Left ==null)
{
current.Left =newNode;
break;
}
current = current.Left;///,遞迴
}
else
{
if (current.Right ==null)
{
current.Right = newNode;
break;
}
current =current.Right;///,遞迴
}
}
}
}
}
一個二叉查詢樹:
BinarySearchTree bst =new BinarySearchTree();
bst.Insert1(1);
bst.Insert1(3);
bst.Insert1(5);
bst.Insert1(4);
bst.Insert1(2);
//1
// |
// 3
// | |
// 2 5
// |
// 4
遍歷的概念
所謂遍歷(Traversal)是指沿著某條搜尋路線,依次對樹中每個結點均做一次且僅做一次訪問。訪問結點所做的操作依賴於具體的應用問題。
遍歷是二叉樹上最重要的運算之一,是二叉樹上進行其它運算之基礎。
遍歷方案
1.遍歷方案
從二叉樹的遞迴定義可知,一棵非空的二叉樹由根結點及左、右子樹這三個基本部分組成。因此,在任意給定結點上,可以按某種次序執行三個操作:
(1)訪問結點本身(N),
(2)遍歷該結點的左子樹(L),
(3)遍歷該結點的右子樹(R)。
以上三種操作有六種執行次序:
NLR、LNR、LRN、NRL、RNL、RLN。
注意:
前三種次序與後三種次序對稱,故只討論先左後右的前三種次序。
2.三種遍歷的命名
根據訪問結點操作發生位置命名:
① NLR:前序遍歷(PreorderTraversal亦稱(先序遍歷))
——訪問結點的操作發生在遍歷其左右子樹之前。
② LNR:中序遍歷(InorderTraversal)
——訪問結點的操作發生在遍歷其左右子樹之中(間)。
③ LRN:後序遍歷(PostorderTraversal)
——訪問結點的操作發生在遍歷其左右子樹之後。
注意:
由於被訪問的結點必是某子樹的根,所以N(Node)、L(Left subtlee)和R(Right subtree)又可解釋為根、根的左子樹和根的右子樹。NLR、LNR和LRN分別又稱為先根遍歷、中根遍歷和後根遍歷。
遍歷演算法
1.中序遍歷的遞迴演算法定義:
若二叉樹非空,則依次執行如下操作:
(1)遍歷左子樹;
(2)訪問根結點;
(3)遍歷右子樹。
2.先序遍歷的遞迴演算法定義:
若二叉樹非空,則依次執行如下操作:
(1) 訪問根結點;
(2) 遍歷左子樹;
(3) 遍歷右子樹。
3.後序遍歷得遞迴演算法定義:
若二叉樹非空,則依次執行如下操作:
(1)遍歷左子樹;
(2)遍歷右子樹;
(3)訪問根結點。
遍歷序列
1.遍歷二叉樹的執行蹤跡
三種遞迴遍歷演算法的搜尋路線相同(如下圖虛線所示)。
具體線路為:
從根結點出發,逆時針沿著二叉樹外緣移動,對每個結點均途徑三次,最後回到根結點。
A
B C
D E F
2.遍歷序列
(1) 中序序列
中序遍歷二叉樹時,對結點的訪問次序為中序序列
【例】中序遍歷上圖所示的二叉樹時,得到的中序序列為:
D B A E C F
(2) 先序序列
先序遍歷二叉樹時,對結點的訪問次序為先序序列
【例】先序遍歷上圖所示的二叉樹時,得到的先序序列為:
A B D C E F
(3) 後序序列
後序遍歷二叉樹時,對結點的訪問次序為後序序列
【例】後序遍歷上圖所示的二叉樹時,得到的後序序列為:
D B E F C A
注意:
(1) 在搜尋路線中,若訪問結點均是第一次經過結點時進行的,則是前序遍歷;若訪問結點均是在第二次(或第三次)經過結點時進行的,則是中序遍歷(或後序遍歷)。只要將搜尋路線上所有在第一次、第二次和第三次經過的結點分別列表,即可分別得到該二叉樹的前序序列、中序序列和後序序列。
(2) 上述三種序列都是線性序列,有且僅有一個開始結點和一個終端結點,其餘結點都有且僅有一個前趨結點和一個後繼結點。為了區別於樹形結構中前趨(即雙親)結點和後繼(即孩子)結點的概念,對上述三種線性序列,要在某結點的前趨和後繼之前冠以其遍歷次序名稱。
【例】上圖所示的二叉樹中結點C,其前序前趨結點是D,前序後繼結點是E;中序前趨結點是E,中序後繼結點是F;後序前趨結點是F,後序後繼結點是A。但是就該樹的邏輯結構而言,C的前趨結點是A,後繼結點是E和F。
中序遍歷
中序遍歷按照節點鍵值的升序順序訪問樹中所有的節點。先訪問根節點下的左子節點,再訪問根節點下的右子節點。
///<summary>
///中序遍歷
///</summary>
///<param name="theroot"></param>
public static void InOrder(Nodetheroot)
{
if (!(theroot == null))
{
InOrder(theroot.Left);
theroot.DisplayNode();
InOrder(theroot.Right);
}
}
呼叫:
Console.WriteLine("InOrder:");
BinarySearchTree.InOrder(bst.root);///1 2 3 4 5
先序遍歷
///<summary>
///先序遍歷
///</summary>
///<param name="theroot"></param>
public static void PreOrder(Nodetheroot)
{
if (!(theroot == null))
{
theroot.DisplayNode();
PreOrder(theroot.Left);
PreOrder(theroot.Right);
}
}
呼叫
Console.WriteLine("PreOrder:");
BinarySearchTree.PreOrder(bst.root);///1 3 2 5 4
後序遍歷
///<summary>
///後序遍歷
///</summary>
///<param name="theroot"></param>
public static void PostOrder(Nodetheroot)
{
if (!(theroot == null))
{
PostOrder(theroot.Left);
PostOrder(theroot.Right);
theroot.DisplayNode();
}
}
呼叫
Console.WriteLine("PostOrder:");
BinarySearchTree.PostOrder(bst.root);///2 4 5 3 1
把目標節點的父節點的每一個子節點設定為空。
50 50
25 75 => 25 75
1 49 51 99 1 49 51
=>
將被刪除節點的父節點,其左(右)節點指向被刪除節點的左(右)子節點。
50 50
25 75 => 25 60
10 30 60 10 30
_________________________________________________________
50 50
25 75 => 25 80
10 30 8010 30
_________________________________________________________
50 50
25 75 => 10 75
10 51 80 51 80
_________________________________________________________
50 50
25 75 => 30 75
30 51 80 51 80
刪除帶有兩個子節點的節點比較複雜。替換被刪除的節點的這個新節點,我們暫稱為後繼節點。要實現刪除,關鍵是尋找後繼節點和重新設定後繼節點的左右子節點。
後繼節點是被刪除節點的右子節點下的最小節點(中序遍歷很快能找到)。
後繼節點的左節點是原先被刪除節點的左子節點,後繼節點的右節點是原先被刪除節點的右子節點(內部需要去除掉後繼節點)。
例:
...............50
........25....................75
....10.......30........60.......80
...1..14..28..40...55.65..78..90
刪除75(是一個右子節點)
...............50
........25...............78
....10......30.......60.....80
...1..14..28..40...55.65......90
刪除25(是一個左子節點)
...............50
........28.................75
....10......30.......60.....80
...1..14......40...55.65..78..90
例:
...............50
........25....................75
....10......30..........60......80
...1..14..28..40...55.65......90
刪除75
...............50
........25..............80
....10......30.......60.....90
...1..14..28..40...55.65........