資料結構與演算法(C#)--樹和二叉樹
阿新 • • 發佈:2019-02-05
3.1、樹的表示方法 3.2、樹的基本術語 1、結點、結點的度和樹的度 結點:包含一個元素及若干指向子樹的分支 結點的度:結點所擁有的子樹數 樹的度:樹內各結點度的最大值 葉子結點:度為零的結點,也稱為終端結點 分支結點:度不為零的結點,也稱為非終端結點 2、孩子和雙親 孩子:一個結點子樹的根是該結點的孩子,也稱為兒子 雙親:一個結點是其子樹根的雙親,也稱為父親 兄弟:同一個雙親的兩個結點 3、層次和深度 層次:在一棵樹中,根結點的層次為 1,其他結點的層次等於其雙親結點的層次加 1 深度:樹中葉子結點的最大層次,也稱為高度 堂兄弟:雙親結點在同一層的結點 3.3、二叉樹 3.3.1、特殊形態的二叉樹 滿二叉樹:一棵深度為 k 且有 2k-1 個結點的二又樹稱為滿二叉樹。 滿二叉樹的特點: (1) 每一層上的結點數都達到最大值。即對給定的高度,它是具有最多結點數的二叉樹。 (2) 滿二叉樹中不存在度數為 1 的結點,每個分支結點均有兩棵高度相同的子樹,且樹葉都在最下一層 上。 完全二叉樹:若一棵二叉樹至多隻有最下面的兩層上結點的度數可以小於 2,並且最下一層上的結點都集 中在該層最左邊的若干位置上,則此二叉樹稱為完全二叉樹。 完全二叉樹的特點: (1) 滿二叉樹是完全二叉樹,完全二叉樹不一定是滿二叉樹。 (2) 在滿二叉樹的最下一層上,從最右邊開始連續刪去若干結點後得到的二叉樹仍然是一棵完全二叉樹。 (3) 在完全二叉樹中,若某個結點沒有左孩子,則它一定沒有右孩子,即該結點必是葉結點。 3.3.2、二叉樹的基本性質性質 1 二叉樹第 i 層上的結點數目最多為 2i-1(i≥1)。 性質 2 深度為 k 的二叉樹至多有 2k-1 個結點(k≥1)。 性質 3 在任意-棵二叉樹中,若終端結點的個數為 n0,度為 2 的結點數為 n2,則 no=n2+1。 性質 4 具有 n 個結點的完全二叉樹的深度為 3.3.3、二叉樹的儲存表示 3.3.4、遍歷二叉樹 1.中序遍歷的遞迴演算法定義: (1)遍歷左子樹; (2)訪問根結點; (3)遍歷右子樹。 2.先序遍歷的遞迴演算法定義: (1) 訪問根結點; (2) 遍歷左子樹; (3) 遍歷右子樹。 3.後序遍歷得遞迴演算法定義: (1)遍歷左子樹; (2)遍歷右子樹; (3)訪問根結點。 3.3.5、線索二叉樹3.3.6、二叉查詢樹 二叉排序樹又稱二叉查詢樹。 具有下列性質: (1)若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值; (2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; (3)左、右子樹也分別為二叉排序樹; namespace 二叉查詢樹 { class Program { static void Main(string[] args) { BinarySearchTree nums = new BinarySearchTree(); nums.Insert(100); nums.Insert(25); nums.Insert(125); nums.Insert(99); nums.Insert(50); nums.Insert(70); nums.Insert(160); nums.Insert(115); Console.WriteLine("--------中序遍歷-----------"); nums.InOrder(nums.root); Console.WriteLine(); Console.WriteLine("--------查詢最小值---------"); Console.WriteLine(nums.FindMin()); Console.WriteLine("--------查詢最大值---------"); Console.WriteLine(nums.FindMax()); nums.Delete(99); Console.WriteLine("--------刪除結點後-------"); nums.InOrder(nums.root); Console.ReadLine(); } } public class Node { public int Data; public Node Left; public Node Right; public void DisplayNode() { Console.Write(Data+" "); } } 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 current = root; 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 InOrder(Node theRoot) //中序遍歷 { if (!(theRoot == null)) { InOrder(theRoot.Left); theRoot.DisplayNode(); InOrder(theRoot.Right); } } public void PreOrder(Node theRoot) //先序遍歷 { if (!(theRoot == null)) { theRoot.DisplayNode(); PreOrder(theRoot.Left); PreOrder(theRoot.Right); } } public void PostOrder(Node theRoot) //後序遍歷 { if (!(theRoot==null)) { PostOrder(theRoot.Left); PostOrder(theRoot.Right); theRoot.DisplayNode(); } } public int FindMin() //查詢最小值 { Node current = root; while (!(current.Left == null)) current = current.Left; return current.Data; } public int FindMax() //查詢最大值 { Node current = root; while (!(current.Right == null)) current = current.Right; return current.Data; } public Node Find(int key) //查詢特定結點 { Node current = root; while (current.Data != key) { if (key < current.Data) { current = current.Left; } else { current = current.Right; } if (current == null) return null; } return current; } public bool Delete(int key) { Node current = root; Node parent = root; bool isLeftChild = true; //是否為左結點 while (current.Data != key) //判斷當前結點是否為左結點 { parent = current; if (key < current.Data) { isLeftChild = true; current = current.Left; } else { isLeftChild = false; current = current.Right; } if ((current == null)) return false; } if ((current.Left == null) & (current.Right == null)) //當前結點的左右結點為空 { if (current == root) root = null; else if (isLeftChild) parent.Left = null; else parent.Right = null; } else if (current.Right == null) //當前結點的右結點為空 { if (current == root) root = current.Left; else if (isLeftChild) parent.Left = current.Left; else parent.Right = current.Left; } else if (current.Left == null) //當前結點的左結點為空 { if (current == root) root = current.Right; else if (isLeftChild) parent.Left = current.Right; else parent.Right = current.Right; } else { Node successor = GetSuccessor(current); if (current == root) root = successor; else if (isLeftChild) parent.Left = successor; else parent.Right = successor; successor.Left = current.Left; } return true; } public Node GetSuccessor(Node delNode) //查詢要刪除結點的後繼結點 { Node successorParent = delNode; Node successor = delNode; Node current = delNode.Right; while (!(current == null)) { successorParent = current; successor = current; current = current.Left; } if (!(successor == delNode.Right)) { successorParent.Left = successor.Right; successor.Right = delNode.Right; } return successor; } } } 3.4、哈夫曼樹 在權為 wl,w2,„,wn 的 n 個葉子所構成的所有二叉樹中,帶權路徑長度最小(即代價最小)的二叉樹稱為最 優二叉樹或哈夫曼樹(WPL 為最小的二叉樹) 。 樹的帶權路徑長度(Weighted Path Length of Tree,簡記為 WPL) 結點的權:在一些應用中,賦予樹中結點的一個有某種意義的實數。 結點的帶權路徑長度:結點到樹根之間的路徑長度與該結點上權的乘積。 樹的帶權路徑長度:定義為樹中所有葉結點的帶權路徑長度之和,通常記為: 其中: n 表示葉子結點的數目 wi 和 li 分別表示葉結點 ki 的權值和根到結點 ki 之間的路徑長度。 樹的帶權路徑長度亦稱為樹的代價。 注意: 1 葉子上的權值均相同時,完全二叉樹一定是最優二叉樹,否則完全二叉樹不一定是最優二叉樹。 2 最優二叉樹中,權越大的葉子離根越近。 3 最優二叉樹的形態不唯一,WPL 最小 哈夫曼編碼:從哈夫曼樹根結點開始,對左子樹分配程式碼“0”,右子樹分配程式碼“1”,一直到達葉子結 點為止,然後將從樹根沿每條路徑到達葉子結點的程式碼排列起來,便得到了哈夫曼編碼。 設各字母的使用頻度為 {E,M,C,A,D}={1,2,3,3,4}。用頻度為權值生成哈夫曼樹,並在葉子上標註對應的 字母,樹枝分配程式碼“0”或“1”: 3.5、樹