二叉樹和樹的根本區別
- 二叉樹的每個元素都恰好有兩棵子樹(其中一個或兩個可能爲空)。而樹的每個元素可有任意數量的子樹。
- 在二叉樹中,每個元素的子樹都是有序的。也就是說,有左子樹和右子樹之分。而樹的子樹是無序的.
二叉樹的特性
特性1 一棵二叉樹有n個元素,n>=0,它有n-1條邊。
證明 二叉樹的每個元素(除了根節點)有且僅有一個父節點。在子節點與父節點間有且只有一條邊,因此邊數爲n-1。
特性2 一棵二叉樹的高度爲h,h>=0,它最少有h個元素,最多有2^h-1個元素。
證明 二叉樹每一級最少有1個元素,因此元素的個數最少爲h。 每個元素最多有2個子節點,則第i層元素最多爲2^(i-1)個,i>0.則2^0+2^1+2^2+……+2^h=2^h-1.所以h層元素的總數最大爲2^h-1.
特性3 一棵二叉樹有n個元素,n>0,它的高度最大爲n,最小高度爲log2(n+1)向上取整。
證明 因爲每層至少有一個元素,因此高度不不會超過n。高度爲h的二叉樹最多有2^h-1個元素。因爲n<=2^h-1,所以,h>=log2(n+1)。由於h是整數,所以log2(n+1)要向上取整。及最小高度爲log2(n+1)向上取整。
當高度爲h的二叉樹恰好有2^h-1個元素時,稱其爲滿二叉樹。
對高度爲h的滿二叉樹的元素,從第一層到最後一層,在每一次中從左至右,順序編號,從1到2^h-1.假設從滿二叉樹中刪除k個其編號爲2^h-i元素,1<=i<=k<2^h,所得到的二叉樹被稱爲完全二叉樹。
滿二叉樹是完全二叉樹的一個特例。
特性4 設完全二叉樹的某一元素編號爲i,1<=i<=n。有以下關係成立:
- 如果i=1,則該元素爲二叉樹的根。若i>1,則其父親節點的編號爲i/2向下取整。
- 如果2i>n,則該元素無左孩子。否則,其左孩子的編號爲2i
- 如果2i+1>n,則該元素無右孩子。否則,其右孩子的編號爲2i+1.
二叉樹的常用操作
二叉樹的常用操作有:
- 確定高度
- 確定元素數目
- 複製
- 顯示或打印二叉樹
- 確定兩棵二叉樹是否一樣
- 刪除整棵樹
所有的這些操作都可以通過二叉樹的遍歷來完成。在二叉樹的遍歷中,每個元素僅被訪問一次。
二叉樹的遍歷
二叉樹的遍歷常用的有如下四種方法:
- 前序遍歷
- 中序遍歷
- 後序遍歷
- 層次遍歷
**二叉樹的中序遍歷和先序遍歷、中序遍歷和後序遍歷可以唯一確定一棵二叉樹。**除此之外,任意兩種遍歷方式結合都無法唯一確定一棵樹。
先序遍歷、中序遍歷、後序遍歷可以方便的使用棧來實現,所以可以使用遞歸實現。但是,層次遍歷的本質其實是隊列,所以用遞歸實現會比較困難。
二叉樹的描述
二叉樹可以用兩種方式來描述:數組和鏈表
數組描述 將二叉樹中的元素從上到下、從左到右順序編號。
一個有n個元素的二叉樹可能最多需要2^n-1個空間來存儲。最壞的情況是,每層只有一個元素,n個元素的二叉樹就有n層。n層二叉樹存儲需要的空間個數即爲滿二叉樹的元素個數。 用數組來表示二叉樹的時候,空節點也是要存儲在數組中的,因爲數組不僅要存儲元素,還要存儲元素的相對關係。 二叉樹的數組表示如下:
鏈表描述 二叉樹最常用的表示方法是指針。每個元素用一個節點表示,節點有兩個指針域,分別稱爲leftChild(左孩子)和rightChild(右孩子).除此之外,還有一個用來element域,用來存儲當前節點的值。 n個節點,每個節點有2個指針,則一棵二叉樹共有2n個指針。父節點的每一個指向孩子的指針表示一條邊,則n個元素的二叉樹共有n-1條邊。所以,二叉樹一共會有2n-(n-1)=n+1個指針域沒有值,它們被置爲NULL。
如下圖,二叉樹的鏈表表示:
從根節點開始,沿着leftChild和rightChild指針域,可以訪問二叉樹的所有節點。二叉樹的鏈式表示沒有指向父節點的指針,因爲大部分函數不需要這樣的指針。若某些應用需要這種指針,可以在每個節點再添加一個指針域。
下面是二叉樹的具體代碼實現:
/* * 二叉樹遍歷操作 *binaryTreeTraversals.cpp */ #include<iostream> #include"arrayqueue.h" #include"binarytreenode.h" #include"myexceptions.h" using namespace std; //訪問二叉樹節點的元素 template <class T> void visit(binaryTreeNode<T> *x) { cout << x->element <<' '; } //先序遍歷 template <class T> void preOrder(binaryTreeNode<T> *t) { if( t != NULL) { visit(t); preOrder(t->leftChild); preOrder(t->rightChild); } } //中序遍歷 template <class T> void inOrder(binaryTreeNode<T> *t) { if(t != NULL) { inOrder(t->leftChild); visit(t); inOrder(t->rightChild); } } //後序遍歷 template <class T> void postOrder(binaryTreeNode<T>* t) { if(t != NULL) { postOrder(t->leftChild); postOrder(t->rightChild); visit(t); } } //層次遍歷 /* * 層次遍歷跟前面三種遍歷不同,用遞歸比較難實現,適合用隊列來實現 */ template <class T> void levelOrder(binaryTreeNode<T>* t) { arrayQueue<binaryTreeNode<T>*> q; while( t != NULL) { visit(t); if(t->leftChild != NULL)//坐孩子入隊 q.push(t->leftChild); if(t->rightChild != NULL)//右孩子入隊 q.push((t->rightChild)); try{ t = q.front();}//出隊操作 catch(queueEmpty){return;} q.pop(); } } int main(void) { binaryTreeNode<int> *x,*y,*z; y = new binaryTreeNode<int> (2); z = new binaryTreeNode<int> (3); x = new binaryTreeNode<int> (1,y,z); //中序遍歷的輸出結果 cout<<"Inorder sequence is "; inOrder(x); cout <<endl; //先序遍歷的結果 cout<<"Preorder sequence is"; preOrder(x); cout <<endl; //後序遍歷 cout<<"Postorder sequence is"; postOrder(x); cout<<endl; //層次遍歷 cout<<"Level order sequence is"; levelOrder(x); cout<<endl; cout <<"Hello World!"<<endl; return 0; }
/* * 二叉樹節點定義 * binaryTreeNode.h */ #ifndef BINARYTREENODE_H #define BINARYTREENODE_H #include<iostream> using namespace std; template <class T> struct binaryTreeNode { T element; binaryTreeNode<T> *leftChild, *rightChild; binaryTreeNode() { leftChild = rightChild = NULL; } binaryTreeNode(const T& theElement):element(theElement) { leftChild = rightChild = NULL; } binaryTreeNode(const T& theElement, binaryTreeNode *theLeftChild, binaryTreeNode *theRightChild):element(theElement) { leftChild = theLeftChild; rightChild = theRightChild; } }; #endif // BINARYTREENODE_H
/* * 二叉樹的定義 * 二叉樹主要的操作是遍歷,這個在搜索、排序算法中很常用 * binaryTree.h * */ #ifndef BINARYTREE_H #define BINARYTREE_H #include<functional> using namespace std; template <class T> class binaryTree { public: virtual ~binaryTree() {} virtual bool empty() const = 0; virtual int size() const = 0; virtual void preOrder(void(*) (T *)) = 0;//先序遍歷 virtual void inOrder(void(*)(T *)) = 0;//中序遍歷 virtual void postOrder(void(*)(T *)) = 0;//後序遍歷 virtual void levelOrder(void(*)(T *)) = 0;//層次遍歷 }; #endif // BINARYTREE_H
/* * 隊列的定義 * queue.h */ #ifndef QUEUE_H #define QUEUE_H #include<iostream> using namespace std; template <class T> class queue { public: virtual ~queue(){} virtual bool empty() const = 0; virtual int size() const = 0; virtual T& front() = 0;//隊首 virtual T& back() = 0;//隊尾 virtual void pop() = 0;//出隊 virtual void push(const T& theElement) = 0;//入隊 }; #endif // QUEUE_H
/* * 用數組表示隊列 * arrayQueue.h */ #ifndef ARRAYQUEUE_H #define ARRAYQUEUE_H #include"queue.h" #include"myexceptions.h" #include<sstream> using namespace std; template <class T> class arrayQueue:public queue<T> { public: arrayQueue(int initialCapacity = 10); ~arrayQueue(){delete [] queue;} bool empty() const{return theFront == theBack;}//當隊首和隊尾相等時,表示隊空 int size() const { return (theBack - theFront + arrayLength)%arrayLength;// } T& front()//隊首元素 { if(theFront == theBack) throw queueEmpty();//判斷隊空 return queue[(theFront+1)%arrayLength]; } T& back()//隊尾元素 { if(theFront == theBack) throw queueEmpty(); return queue[theBack]; } void pop()//出隊 { if(theFront == theBack) throw queueEmpty(); theFront = (theFront+1)%arrayLength; queue[theFront].~T(); } //入隊的實現比較複雜一些,考慮的情況比較多 void push(const T& theELement)//入隊 { //如果隊滿的時候,隊列的容量擴充一倍 if((theBack + 1) % arrayLength == theFront) { T* newQueue = new T[2*arrayLength]; int start = (theFront + 1)%arrayLength; if(start < 2) copy(queue+start,queue+arrayLength,newQueue); else { copy(queue + start,queue + arrayLength,newQueue); copy(queue,queue+theBack+1,newQueue+arrayLength - start); } theFront = 2 * arrayLength - 1; theBack = arrayLength - 2; arrayLength *=2; queue = newQueue; } theBack = (theBack + 1)%arrayLength; queue[theBack] = theELement; } private: int theFront;//隊首 int theBack;//隊尾 int arrayLength;//數組長度 T* queue;//存儲元素的數組 }; template <class T> arrayQueue<T>::arrayQueue(int inititalCapacity) { if(inititalCapacity < 1) { ostringstream s; s <<"Initial capacty = "<<inititalCapacity<<"Must be > 0"; throw illegalParameterValue(s.str()); } arrayLength = inititalCapacity; queue = new T[arrayLength]; theFront = 0; theBack = 0; } #endif // ARRAYQUEUE_H
/* * 異常類的定義 * myExceptions.h */ #ifndef MYEXCEPTIONS_H #define MYEXCEPTIONS_H #include<string> #include<iostream> using namespace std; class illegalParameterValue { public: illegalParameterValue(string theMessage = "Illegal parameter value") { message = theMessage; } void outputMessage() { cout <<message<<endl; } private: string message; }; class queueEmpty { public: queueEmpty(string theMesssage = "Invalid operation on empty queue") { message = theMesssage; } void outpuMessaget() { cout<< message<<endl; } private: string message; }; #endif // MYEXCEPTIONS_H