樹(Tree)是 n(n≥0)個相同類型的數據元素的有限集合。樹中的數據元素叫結點(Node)。n=0 的樹稱爲空樹(Empty Tree);對於 n>0 的任意非空樹 T 有: (1)有且僅有一個特殊的結點稱爲樹的根(Root)結點,根沒有前驅結點; (2)若n>1,則除根結點外,其他結點被分紅了m(m>0)個互不相交的集合T1,T2,…,Tm,其中每個集合Ti(1≤i≤m)自己又是一棵樹。樹T1,T2,…,Tm稱爲這棵樹的子樹(Subtree)node
二叉樹(Binary Tree)是 n(n≥0)個相同類型的結點的有限集合。n=0 的二叉樹稱爲空二叉樹(Empty Binary Tree);對於 n>0 的任意非空二叉樹有:
(1)有且僅有一個特殊的結點稱爲二叉樹的根(Root)結點,根沒有前驅結點;
(2)若n>1,則除根結點外,其他結點被分紅了 2 個互不相交的集合TL,TR,而TL、TR自己又是一棵二叉樹,分別稱爲這棵二叉樹的左子樹(Left Subtree)和右子樹(Right Subtree)。測試
1)滿二叉樹(Full Binary Tree):若是一棵二叉樹只有度爲 0 的結點和度爲 2的結點,而且度爲 0 的結點在同一層上,則這棵二叉樹爲滿二叉樹this
(2)徹底二叉樹(Complete Binary Tree):深度爲 k,有 n 個結點的二叉樹當且僅當其每個結點都與深度爲 k,有 n 個結點的滿二叉樹中編號從1到n的結點一一對應時,稱爲徹底二叉樹spa
二叉樹的存儲結構主要有三種:順序存儲結構、二叉鏈表存儲結構和三叉鏈表存儲結構3d
二叉鏈表存儲結構的類實現
二叉樹的二叉鏈表的結點類有3個成員字段:數據域字段data、左孩子引用域字段lChild和右孩子引用域字段rChild。code
public class Node<T> { private T data;//數據域 public T Data { get { return data; } set { data = value; } } private Node<T> lchild;//左孩子 public Node<T> Lchild { get { return lchild; } set { lchild = value; } } private Node<T> rchild;//右孩子 public Node<T> Rchild { get { return rchild; } set { rchild = value; } } public Node() { data = default(T); lchild = null; rchild = null; } public Node(T data ) { this.data = data; lchild = null; rchild = null; } public Node(T data, Node<T> lchild, Node<T> rchlid) { this.data = data; this.lchild = lchild; this.rchild = rchild; } }
不帶頭結點的二叉樹的二叉鏈表比帶頭結點的二叉樹的二叉鏈表的區別與不帶頭結點的單鏈表與帶頭結點的單鏈表的區別同樣。blog
下面只介紹不帶頭結點的二叉樹的二叉鏈表的類隊列
public class BinaryTree<T> { private Node<T> head; //頭引用 internal Node<T> Head { get { return head; } set { head = value; } } public BinaryTree() { head = null; } public BinaryTree(T data) { Node<T> p = new Node<T>(data); head = p; } public BinaryTree(T data, Node<T> lchild, Node<T> rchild) { Node<T> p = new Node<T>(data, lchild, rchild); head = p; } // 二叉樹是否爲空 public bool IsEmptyTree() { if (null != head) { return false; } else { return true; } } // 獲取根節點 public Node<T> GetRoot() { if (IsEmptyTree()) { return null; } return head; } //獲取結點的左孩子結點 public Node<T> GetLChild(Node<T> p) { return p.Lchild; } // right child public Node<T> GetRChild(Node<T> p) { return p.Rchild; } //將結點p的左子樹插入值爲val的新結點, //原來的左子樹成爲新結點的左子樹 public void InsertL(T val, Node<T> p) { Node<T> temp = new Node<T>(val); temp.Lchild = p.Lchild; p.Lchild = temp; } //將結點p的右子樹插入值爲val的新結點, //原來的右子樹成爲新結點的右子樹 public void InsertR(T val, Node<T> p) { Node<T> tmp = new Node<T>(val); tmp.Rchild = p.Rchild; p.Rchild = tmp; } //若p非空,刪除p的左子樹 public Node<T> DeleteL(Node<T> p) { if (p == null || p.Lchild == null) { return null; } Node<T> temp = p.Lchild; p.Lchild = null; return temp; } //若p非空,刪除p的右子樹 public Node<T> DeleteR(Node<T> p) { if (p == null || p.Rchild == null) { return null; } Node<T> temp = p.Rchild; p.Rchild = null; return temp; } //判斷是不是葉子結點 public bool IsLeaf(Node<T> p) { if ( p != null && p.Lchild == null && p.Rchild == null) { return true; } return false; } // 銷燬二叉樹 public void DestroyTree( Node<T> node ) { if ( node != null ) { if (node.Lchild != null) { node.Lchild = null; } if (node.Rchild != null) { node.Rchild = null; } node = null; } } // 一、先序遍歷(DLR) public void PreOredr(Node<T> root) { if ( root == null ) { //Console.WriteLine("樹爲空,沒法遍歷"); return; } //處理根結點 Console.WriteLine("{0}", root.Data); //先序遍歷左子樹 PreOredr(root.Lchild); //再遍歷右子樹 PreOredr(root.Rchild); } /* 二、中序遍歷(LDR) 中序遍歷的基本思想是:首先中序遍歷根結點的左子樹,而後訪問根結點, 最後中序遍歷其右子樹 */ public void InOrder(Node<T> root) { if (root.Lchild != null) { InOrder(root.Lchild); } Console.WriteLine( root.Data ); if (root.Rchild != null) { InOrder(root.Rchild); } } /* 三、後序遍歷(LRD) 後序遍歷的基本思想是:首前後序遍歷根結點的左子樹,而後後序遍歷根結 點的右子樹,最後訪問根結點 */ public void PostOrder(Node<T> root) { if (root.Lchild != null) { PostOrder(root.Lchild); } if (root.Rchild != null) { PostOrder(root.Rchild); } Console.WriteLine(root.Data); } /* 層序遍歷的基本思想是:因爲層序遍歷結點的順序是先遇到的結點先訪問, 與隊列操做的順序相同。因此,在進行層序遍歷時,設置一個隊列,將根結點引 用入隊,當隊列非空時,循環執行如下三步: (1) 從隊列中取出一個結點引用,並訪問該結點; (2) 若該結點的左子樹非空,將該結點的左子樹引用入隊; (3) 若該結點的右子樹非空,將該結點的右子樹引用入隊 */ public void LevelOrder(Node<T> root) { if ( root == null ) { return; } Queue<Node<T>> queue = new Queue<Node<T>>(); queue.Enqueue(root); while ( queue.Count > 0 ) { //結點出隊 Node<T> temp = queue.Dequeue(); //處理當前結點 Console.WriteLine("結點出隊{0}", temp.Data); //將當前結點的左孩子結點入隊 if (temp.Lchild != null) { queue.Enqueue(temp.Lchild); } if (temp.Rchild != null) { queue.Enqueue(temp.Rchild); } } } }
// 測試get
BinaryTree<char> tree = new BinaryTree<char>(); Node<char> p = new Node<char>('A'); Node<char> p1 = new Node<char>('B'); tree.InsertL('B', p); tree.InsertL('D', p); tree.InsertR('E', p1); tree.InsertR('C', p); tree.InsertR('F', p); tree.InsertR('G', p); Console.WriteLine("先序遍歷*******************************"); tree.PreOredr(p); //tree.PreOredr(p1); Console.WriteLine("中序遍歷*******************************"); tree.InOrder(p); Console.WriteLine("後序遍歷*******************************"); tree.PostOrder(p); Console.WriteLine("層序遍歷*******************************"); tree.LevelOrder(p);