二叉樹是每個結點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。二叉樹常被用於實現二叉查找樹和二叉堆。樹是數據結構中的重中之重,尤其以各類二叉樹爲學習的難點。
1、樹的相關概念
1.1、樹
樹是一種一對多的數據結構。樹又有很多子集,比如:二叉樹、二叉搜索樹、2-3樹、紅黑樹等等。樹的特徵:
1.沒有父結點的結點叫根,一個樹有且只有一個根;
2.每個結點有0個或多個子結點;
3.一顆樹裏也可擁有子樹,且子樹不能相交;
樹的示例:
圖中標紅的爲上面這個樹的子樹:
參考鏈接:每天一點算法-二叉樹 (Day8)
1.2、樹的相關術語
樹的結點(node):包含一個數據元素及若干指向子樹的分支;
孩子結點(child node):結點的子樹的根稱爲該結點的孩子;
雙親結點:B 結點是A 結點的孩子,則A結點是B 結點的雙親;
兄弟結點:同一雙親的孩子結點; 堂兄結點:同一層上結點;
祖先結點: 從根到該結點的所經分支上的所有結點
子孫結點:以某結點爲根的子樹中任一結點都稱爲該結點的子孫
結點層:根結點的層定義爲1;根的孩子爲第二層結點,依此類推;
樹的深度:樹中最大的結點層
結點的度:結點子樹的個數
樹的度: 樹中最大的結點度。
葉子結點:也叫終端結點,是度爲 0 的結點;
分枝結點:度不爲0的結點;
有序樹:子樹有序的樹,如:家族樹;
無序樹:不考慮子樹的順序;
2、二叉樹
2.1、二叉樹定義
二叉樹是n(n>=0)個結點的有限集合,該集合或者爲空集(稱爲空二叉樹),或者由一個根結點和兩棵互不相交的、分別稱爲根結點的左子樹和右子樹組成。
一棵普通二叉樹如下所示:
2.2、二叉樹的特性
- 一顆二叉樹有n個元素(n>0),它有n-1條邊
- 一棵二叉樹高度爲h(h>=0),它至少有h個元素,最多有2^h-1個元素
- 一顆二叉樹有n個元素(n>0),它的高度爲n,最小高度爲[log2(n+1)]
- 完全二叉樹的一元素編號爲i(1<=i<=n),有以下關係成立
- 如果i=1,該元素爲二叉樹的根。若i>1,則其父節點的編號爲i/2;
- 如果2i>n,則表示該元素無左孩子。否則,其左孩子的編號爲2i;
- 如果2i+1>n,則其元素無右孩子。否則其右孩子的編號爲2i+1。
2.3、二叉樹分類
- 二叉樹
二叉樹是每個節點最多有兩個子樹的樹結構。
- 滿二叉樹
一棵深度爲k,且有2^k-1個節點的樹是滿二叉樹。滿二叉樹如下圖:
性質:
1.如果一顆樹深度爲h,最大層數爲k,且深度與最大層數相同,即k=h;
2.它的葉子數是: 2^(h-1)
3.第k層的結點數是: 2^(k-1)
4.總結點數是: 2^k-1 (2的k次方減一)
5.總節點數一定是奇數。
6.樹高:h=log2(n+1)。
- 完全二叉樹
若設二叉樹的深度爲h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第h 層所有 的結點都連續集中在最左邊,這就是完全二叉樹。完全二叉樹如下圖:
特點:
1)葉子結點只能出現在最下層和次下層。
2)最下層的葉子結點集中在樹的左部。
3)倒數第二層若存在葉子結點,一定在右部連續位置。
4)如果結點度爲1,則該結點只有左孩子,即沒有右子樹。
5)同樣結點數目的二叉樹,完全二叉樹深度最小。
注:滿二叉樹一定是完全二叉樹,但反過來不一定成立。
參考鏈接:深入學習二叉樹(一) 二叉樹基礎
3、二叉樹的實現
3.1、數據存儲結構
- 順序存儲
二叉樹的順序存儲結構就是使用一維數組存儲二叉樹中的結點,並且結點的存儲位置,就是數組的下標索引。
上圖所示的一棵完全二叉樹採用順序存儲方式,如圖下表示:
由上圖可以看出,當二叉樹爲完全二叉樹時,結點數剛好填滿數組。
那麼當二叉樹不爲完全二叉樹時,採用順序存儲形式如何呢?例如:對於上圖描述的二叉樹:其中淺色結點表示結點不存在。那麼圖3.8所示的二叉樹的順序存儲結構如圖所示:
其中,∧表示數組中此位置沒有存儲結點。此時可以發現,順序存儲結構中已經出現了空間浪費的情況。
那麼對於右斜樹極端情況對應的順序存儲結構如圖所示:由圖可以看出,對於這種右斜樹極端情況,採用順序存儲的方式是十分浪費空間的。因此,順序存儲一般適用於完全二叉樹。
- 鏈表存儲
既然順序存儲不能滿足二叉樹的存儲需求,那麼考慮採用鏈式存儲。由二叉樹定義可知,二叉樹的每個結點最多有兩個孩子。因此,可以將結點數據結構定義爲一個數據和兩個指針域。
定義結點代碼:
//鏈表節點 template <class T> class ListNode { public: ListNode():value_(NULL),front(nullptr),next(nullptr){} ListNode(const T &value):value_(value),front(nullptr),next(nullptr){} ListNode(const T &value, ListNode<T> *left, ListNode<T> *right): value_(value), front(left), next(right){} ListNode(const ListNode<T> &theNode): value_(theNode.value_), front(nullptr), next(nullptr){} public: T value_; ListNode<T> *front; ListNode<T> *next; };
二叉樹可以採用下表示。
圖中採用一種鏈表結構存儲二叉樹,這種鏈表稱爲二叉鏈表。
作者:MrHorse1992
鏈接:https://www.jianshu.com/p/bf73c8d50dc2
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
3.2、二叉樹遍歷
二叉樹的根結點出發,按照某種次序依次訪問二叉樹中的所有結點,使得每個結點被訪問一次,且僅被訪問一次。
二叉樹的訪問次序可以分爲四種:前序遍歷、中序遍歷、後序遍歷和層序遍歷。
- 前序遍歷
前序遍歷通俗的說就是從二叉樹的根結點出發,當第一次到達結點時就輸出結點數據,按照先向左在向右的方向訪問。
二叉樹前序遍歷如下:
1)從根結點出發,則第一次到達結點A,故輸出A;
2)繼續向左訪問,第一次訪問結點B,故輸出B;
3)按照同樣規則,輸出D,輸出H;
4)當到達葉子結點H,返回到D,此時已經是第二次到達D,故不在輸出D,進而向D右子樹訪問,D右子樹不爲空,則訪問至I,第一次到達I,則輸出I;
5)I爲葉子結點,則返回到D,D左右子樹已經訪問完畢,則返回到B,進而到B右子樹,第一次到達E,故輸出E;
6)向E左子樹,故輸出J;
7)按照同樣的訪問規則,繼續輸出C、F、G;二叉樹的前序遍歷輸出爲:A->B->D->H->I->E->J->C->F->G。
- 中序遍歷
中序遍歷就是從二叉樹的根結點出發,當第二次到達結點時就輸出結點數據,按照先向左在向右的方向訪問。
二叉樹中序遍歷如下:
1)從根結點出發,則第一次到達結點A,不輸出A,繼續向左訪問,第一次訪問結點B,不輸出B;繼續到達D,H;
2)到達H,H左子樹爲空,則返回到H,此時第二次訪問H,故輸出H;
3)H右子樹爲空,則返回至D,此時第二次到達D,故輸出D;
4)由D返回至B,第二次到達B,故輸出B;
5)按照同樣規則繼續訪問,輸出J、E、A、F、C、G。二叉樹的中序遍歷輸出爲:H->D->I->B->J->E->A->F->C->G。
- 後序遍歷
後序遍歷就是從二叉樹的根結點出發,當第三次到達結點時就輸出結點數據,按照先向左在向右的方向訪問。
二叉樹後序遍歷如下:
1)從根結點出發,則第一次到達結點A,不輸出A,繼續向左訪問,第一次訪問結點B,不輸出B;繼續到達D,H;
2)到達H,H左子樹爲空,則返回到H,此時第二次訪問H,不輸出H;
3)H右子樹爲空,則返回至H,此時第三次到達H,故輸出H;
4)由H返回至D,第二次到達D,不輸出D;
5)繼續訪問至I,I左右子樹均爲空,故第三次訪問I時,輸出I;
6)返回至D,此時第三次到達D,故輸出D;
7)按照同樣規則繼續訪問,輸出J、E、B、F、G、C,A;二叉樹的中序遍歷輸出爲:H->I->D->J->E->B->F->G->C->A。
作者:MrHorse1992
鏈接:https://www.jianshu.com/p/bf73c8d50dc2
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
- 層序遍歷
層次遍歷就是按照樹的層次自上而下的遍歷二叉樹。
二叉樹層序遍歷如下:
1)把根節點A放入隊列,此時隊列爲:A,隊列頭指針指向A,也就是隊列第一個元素
2、把當前隊列頭指針所指元素的左右兒子放入隊列,即將B C放入隊列,此時隊列爲A B C ,隊列頭指針向下移一格,此時指向B
3、不斷重複2步驟。此時把B的左右兒子取出來放入隊尾,隊列變爲A B C D E,隊列頭指針後移,指向C,把C的左右兒子取出來放入隊尾,隊列變爲A B C D E F G;
4、依次類推,直到隊列頭指針和爲指針重合時,輸出最後一個元素,算法結束。隊列從頭到尾輸出一遍,就是按層遍歷,這個隊列是動態的,只要有子節點,子節點就會不停的加入隊尾,但總有子節點沒有的時候,所以,隊列尾指針肯定有不再移動的時候,而頭指針一直在一步一步向下移,總會有首尾指針重合的時候,即標誌着算法結束。
二叉樹的層次遍歷結果爲:A->B->C->D->E->F->G->H->I->J。
參考鏈接:二叉樹的按層遍歷法
3.3、二叉樹的算法實現
template <class T>
class BTree
{
public:
virtual ~BTree(){}
virtual bool empty() const =0;
virtual size_t size() const=0;
//前序遍歷
virtual void preOrder()=0;
//中序遍歷
virtual void inOrder()=0;
//後序遍歷
virtual void postOrder()=0;
//層次遍歷
virtual void levelOrder()=0;
};
/*******************************************
* 二叉樹的數組實現 *
/*******************************************/
template <class T>
class ArrayBTree
{
public:
ArrayBTree(const size_t &capacity=10);
~ArrayBTree(){}
//二叉樹元素的數量
size_t size() const{return size_;}
//二叉樹的容量
size_t max_size() const{return capacity_;}
//二叉樹是否爲空
bool empty() const{return size_==0;};
//向二叉樹中插入元素
void insert(const T &value);
//刪除二叉樹中的元素
void erase(const T &value);
//修改二叉樹中的元素
void set(const T &index,const T &value);
//查找二叉樹中的元素
T get(const int &index);
//二叉樹中搜索對應值位置的下標
int find(const T &value);
//打印二叉樹
void print();
//前序遍歷
void preOrder();
//中序遍歷
void inOrder();
//後序遍歷
void postOrder();
//層次遍歷
void levelOrder();
private:
void insert_(const T &value,int index); //插入元素
void erase_(const int &index); //刪除元素並重新調整二叉樹
int find_(const T &value,int index); //搜索二叉樹
void preOrder_(const int &index); //前序遍歷
void inOrder_(const int &index); //中序遍歷
void postOrder_(const int &index); //後序遍歷
void levelOrder_(const int &index); //層次遍歷
private:
shared_ptr<T> ptr_; //存儲二叉樹數的數組
size_t size_; //二叉樹元素的大小
size_t capacity_; //二叉樹的容量
};
template <class T>
ArrayBTree<T>::ArrayBTree(const size_t &capacity):capacity_(capacity),
size_(0)
{
ptr_=shared_ptr<T>(new T[capacity_],[](T *p){delete[] p;});
fill(ptr_.get(),ptr_.get()+capacity_,-1);
}
template <class T>
void ArrayBTree<T>::insert_(const T &value, int index)
{
if(index>=capacity_){
cerr<<"index out of capacity(in function insert_)"<<endl;
exit(0);
}
if(ptr_.get()[index]==-1){
ptr_.get()[index]=value;
} else{
if(ptr_.get()[index]>=value){
insert_(value,index*2+1);
}else{
insert_(value,index*2+2);
}
}
}
template <class T>
void ArrayBTree<T>::insert(const T &value)
{
if(size_+1>=capacity_){
cerr<<"tree capacity is out of max size"<<endl;
exit(0);
}
if(this->empty()){
*ptr_.get()=value;
}else{
insert_(value,0);
}
++size_;
}
template <class T>
void ArrayBTree<T>::erase_(const int &index)
{
if(index*2+1>=capacity_ || (ptr_.get()[index*2+1]==-1 &&
ptr_.get()[index*2+2]==-1)){
ptr_.get()[index]=-1;
return ;
}
if(ptr_.get()[index*2+1]!=-1 && (ptr_.get()[index*2+2]==-1 ||
ptr_.get()[index*2+2]>=capacity_)){
ptr_.get()[index]=-1;
T value=ptr_.get()[index*2+1];
ptr_.get()[index*2+1]=-1;
insert(value);
return ;
}
ptr_.get()[index]=ptr_.get()[index*2+2];
erase_(index*2+2);
}
template <class T>
void ArrayBTree<T>::erase(const T &value)
{
int index=find(value);
if(-1==index){
cerr<<"erase id fail,"<<value<<" don't in the tree"<<endl;
exit(0);
}else{
erase_(index);
--size_;
}
}
template <class T>
void ArrayBTree<T>::set(const T &index, const T &value)
{
if(index<0 || index>=capacity_){
cerr<<"index out of capacity(in function insert_)"<<endl;
exit(0);
}
if(ptr_.get()[index]==-1){
insert(value);
}else{
erase(ptr_.get()[index]);
insert(value);
}
}
template <class T>
T ArrayBTree<T>::get(const int &index)
{
if(index<0 || index>=capacity_){
cerr<<"index out of capacity(in function insert_)"<<endl;
exit(0);
}
if(ptr_.get()[index]==-1){
cerr<<"Don't find value in the tree"<<endl;
return -1;
} else{
return ptr_.get()[index];
}
}
template <class T>
int ArrayBTree<T>::find_(const T &value, int index)
{
if(ptr_.get()[index]==-1 || index>=capacity_){
cerr<<"Don't find "<<value<<" in the tree"<<endl;
return -1;
}
if(ptr_.get()[index]==value){
return index;
}else if(ptr_.get()[index] > value){
return find_(value,index*2+1);
} else{
return find_(value,index*2+2);
}
}
template <class T>
int ArrayBTree<T>::find(const T &value)
{
if(this->empty()){
cerr<<"tree is empty"<<endl;
exit(0);
}
return find_(value,0);
}
template <class T>
void ArrayBTree<T>::print()
{
for(auto i=0;i<capacity_;++i){
cout<<ptr_.get()[i]<<" ";
}
cout<<endl;
}
template <class T>
void ArrayBTree<T>::preOrder_(const int &index)
{
if(index>=capacity_ || ptr_.get()[index]==-1){
return;
}
cout<<ptr_.get()[index]<<" ";
preOrder_(index*2+1);
preOrder_(index*2+2);
}
template <class T>
void ArrayBTree<T>::preOrder()
{
preOrder_(0);
}
template <class T>
void ArrayBTree<T>::inOrder_(const int &index)
{
if(index>=capacity_ || ptr_.get()[index]==-1){
return;
}
inOrder_(index*2+1);
cout<<ptr_.get()[index]<<" ";
inOrder_(index*2+2);
}
template <class T>
void ArrayBTree<T>::inOrder()
{
inOrder_(0);
}
template <class T>
void ArrayBTree<T>::postOrder_(const int &index)
{
if(index>=capacity_ || ptr_.get()[index]==-1){
return;
}
postOrder_(index*2+1);
postOrder_(index*2+2);
cout<<ptr_.get()[index]<<" ";
}
template <class T>
void ArrayBTree<T>::postOrder()
{
postOrder_(0);
}
template <class T>
void ArrayBTree<T>::levelOrder_(const int &index)
{
if(index>=capacity_ || ptr_.get()[index]==-1){
return;
}
int left=0,right=0;
left=right=index;
while(left<capacity_ && right<capacity_){
for(int i=left;i<=right;++i){
T value=ptr_.get()[i];
if(value!=-1){
cout<<value<<" ";
}
}
cout<<endl;
left=left*2+1;
right=right*2+2;
}
}
template <class T>
void ArrayBTree<T>::levelOrder()
{
levelOrder_(0);
}
/*******************************************
* 二叉樹的鏈表實現 *
/*******************************************/
template <class T>
class ListBTree:public BTree<T>
{
public:
ListBTree():phead_(nullptr),size_(0){}
ListBTree(const ListBTree<T> &other); //拷貝構造函數,複製整棵樹
~ListBTree();
//獲取二叉樹的根
ListNode<T> *get_root(){return phead_;}
//判斷二叉樹是否爲空
bool empty() const{return size_==0;}
//返回二叉樹元素的個數
size_t size() const{return size_;}
size_t &set_size(){return size_;}
//插入節點
void insert(const T &value);
void insert(const ListNode<T> &theNode);
//刪除節點
void erase(const T &value);
//二叉樹中搜索對應值位置
ListNode<T> *find(const T &value);
//拷貝二叉樹
ListNode<T> *clone() const;
//前序遍歷
void preOrder();
//中序遍歷
void inOrder();
//後序遍歷
void postOrder();
//層次遍歷
void levelOrder();
private:
void insert_(ListNode<T> *node,ListNode<T> *&cur); //插入節點
void erase_(const T &value,ListNode<T> *&node); //刪除節點
void delete_(ListNode<T> *node); //刪除整個二叉樹
ListNode<T> *clone_(ListNode<T> *node) const; //拷貝二叉樹
void preOrder_(ListNode<T> *node); //前序遍歷
void inOrder_(ListNode<T> *node); //中序遍歷
void postOrder_(ListNode<T> *node); //後序遍歷
ListNode<T> *get_parents(ListNode<T> *node); //獲取當前節點的父節點
private:
ListNode<T> *phead_;
size_t size_;
};
template <class T>
ListBTree<T>::ListBTree(const ListBTree<T> &other)
{
if(other.empty()){
phead_= nullptr;
}else{
phead_=other.clone();
}
}
template <class T>
void ListBTree<T>::delete_(ListNode<T> *node)
{
if(node!= nullptr){
ListNode<T> *cur=node;
delete_(node->front);
delete_(node->next);
delete cur;
cur= nullptr;
}
}
template <class T>
ListBTree<T>::~ListBTree()
{
if(phead_!= nullptr){
delete_(phead_);
}
}
template <class T>
void ListBTree<T>::insert_(ListNode<T> *node,ListNode<T> *&cur)
{
if(cur== nullptr) {
cur = new ListNode<T>(node->value_);
return ;
}
if(cur->value_<=node->value_){
insert_(node,cur->next);
} else{
insert_(node,cur->front);
}
}
template <class T>
void ListBTree<T>::insert(const T &value)
{
ListNode<T> *node=new ListNode<T>(value);
if(phead_== nullptr){
phead_=node;
}else{
insert_(node,phead_);
}
++size_;
}
template <class T>
void ListBTree<T>::insert(const ListNode<T> &theNode)
{
ListNode<T> *node=new ListNode<T>(theNode);
if(phead_== nullptr){
phead_=node;
}else{
insert_(node,phead_);
}
++size_;
}
template <class T>
void ListBTree<T>::erase_(const T &value, ListNode<T> *&node)
{
if(node== nullptr){
return ;
}
if(value<node->value_){
erase_(value,node->front);
} else if(value>node->value_){
erase_(value,node->next);
}else if(node->front!= nullptr && node->next!= nullptr){
ListNode<T> *tmp=node->next;
while(tmp->front!= nullptr){
tmp=tmp->front;
}
node->value_=tmp->value_;
erase_(node->value_,node->next);
}else{
ListNode<T> *tmp=node;
node=(node->front!= nullptr)?node->front:node->next;
delete tmp;
tmp= nullptr;
}
}
template <class T>
void ListBTree<T>::erase(const T &value)
{
if(phead_== nullptr){
return ;
}
erase_(value,phead_);
--size_;
}
template <class T>
ListNode<T> *ListBTree<T>::find(const T &value)
{
if(phead_== nullptr){
return nullptr;
}
ListNode<T> *cur=phead_;
while(cur != nullptr){
if(cur->value_<value){
cur=cur->next;
} else if(cur->value_>value){
cur=cur->front;
}else{
return cur;
}
}
return nullptr;
}
template <class T>
ListNode<T> *ListBTree<T>::clone_(ListNode<T> *node) const
{
if(node== nullptr){
return nullptr;
}
return new ListNode<T>(node->value_,
clone_(node->front),
clone_(node->next));
}
template <class T>
ListNode<T> *ListBTree<T>::clone() const
{
if(phead_== nullptr){
return nullptr;
}
return clone_(phead_);
}
template <class T>
void ListBTree<T>::preOrder_(ListNode<T> *node)
{
if(node== nullptr){
return ;
}
cout<<node->value_<<" ";
preOrder_(node->front);
preOrder_(node->next);
}
template <class T>
void ListBTree<T>::preOrder()
{
if(phead_== nullptr){
return ;
}
preOrder_(phead_);
}
template <class T>
void ListBTree<T>::inOrder_(ListNode<T> *node)
{
if(node== nullptr){
return ;
}
inOrder_(node->front);
cout<<node->value_<<" ";
inOrder_(node->next);
}
template <class T>
void ListBTree<T>::inOrder()
{
if(phead_== nullptr){
return ;
}
inOrder_(phead_);
}
template <class T>
void ListBTree<T>::postOrder_(ListNode<T> *node)
{
if(node== nullptr){
return ;
}
postOrder_(node->front);
postOrder_(node->next);
cout<<node->value_<<" ";
}
template <class T>
void ListBTree<T>::postOrder()
{
if(phead_== nullptr){
return ;
}
postOrder_(phead_);
}
template <class T>
void ListBTree<T>::levelOrder()
{
if(phead_== nullptr){
return ;
}
ListQueue<ListNode<T>* > queue;
queue.push(phead_);
while(!queue.empty()){
ListNode<T> *cur=queue.pop();
cout<<cur->value_<<" ";
if(cur->front!= nullptr){
queue.push(cur->front);
}
if(cur->next!= nullptr){
queue.push(cur->next);
}
}
}
詳細代碼可以參考我的github代碼:https://github.com/kingqiuol/algorithm_practice/blob/master/assignment5/binary_tree.h