一、什么是二叉樹
二叉樹是每個結點最多有兩個子樹的樹結構,二叉樹是遞歸定義的,其結點有左右子樹之分,通常包含:滿二叉樹、完全二叉樹、霍夫曼樹、平衡二叉樹、紅黑樹等。
滿二叉樹:如果二叉樹中所有分支結點的度數都為2,並且葉子結點都在統一層次上,則二叉樹為滿二叉樹,從圖形形態上看,滿二叉樹外觀上是一個三角形;從數學上看,滿二叉樹的各個層的結點數形成一個首項為1,公比為2的等比數列。如圖:
完全二叉樹:完全二叉樹從根結點到倒數第二層滿足滿二叉樹,最后一層可以不完全填充,其葉子結點都靠左對齊。滿二叉樹一定是完全二叉樹,完全二叉樹不一定是滿二叉樹。
非完全二叉樹:
二、二叉樹常見的問題
本文包括:
2.1 二叉樹的創建
2.2 二叉樹的遍歷
2.3 求葉子節點的個數
2.4 求樹的深度
2.5 交換樹的左右子樹
2.6 判斷一個節點是否在一棵子樹中
2.7 求樹中總共的節點個數
2.8 判斷兩個樹是否相同
2.9 判斷一個樹是否為另一棵樹的子樹
2.1 二叉樹的創建
參考代碼:https://www.cnblogs.com/BeyondAnyTime/archive/2012/08/27/2659163.html
#include <QCoreApplication> #include <QDebug> #include <QTextStream> //二叉樹的節點 class BinTreeNode { private: int data; BinTreeNode *left,*right; public: //利用初始化列表完成data、left、right的初始化 BinTreeNode(const int &item,BinTreeNode *lPtr = NULL,BinTreeNode *rPtr = NULL):data(item) ,left(lPtr),right(rPtr){} void set_data(int item) { data = item; } int get_data()const { return data; } void set_left(BinTreeNode *l) { left = l; } BinTreeNode *get_left() const { return left; } void set_right(BinTreeNode *r) { right = r; } BinTreeNode *get_right() const { return right; } }; //二叉樹 class BinTree { private: BinTreeNode *root; public: BinTree(BinTreeNode *t = NULL):root(t){} ~BinTree(){delete root;} void set_root(BinTreeNode *t) { root = t; } BinTreeNode *get_root() const { return root; } //創建二叉樹 BinTreeNode * create_tree(); }; BinTreeNode *BinTree::create_tree() { QString item; BinTreeNode *t,*t_l,*t_r; QTextStream qin(stdin); qin>>item; int data = item.toInt(); if(item != "#") { BinTreeNode *pTmpNode = new BinTreeNode(data); t = pTmpNode; t_l = create_tree(); t->set_left(t_l); t_r = create_tree(); t->set_right(t_r); return t; } else { t = NULL; return t; } } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); BinTree tree; qDebug()<<"輸入二叉樹前序序列進行建樹,\"#\"代表空節點"<<endl; tree.set_root(tree.create_tree()); return a.exec(); }
由此,可以看到,其實構造一個二叉樹並不是一個十分困難的過程,當然我們采用的是先序創建。結合自己的想法很容易寫出二叉樹:
#include <QCoreApplication> #include <QDebug> #include <QTextStream> class binTreeTest { public: int data; binTreeTest *left,*right; void setData(int item) { data = item; } int getData() { return data; } void set_left(binTreeTest *l) { left = l; } binTreeTest * get_left()const { return left; } void set_right(binTreeTest *r) { right = r; } binTreeTest * get_right()const { return right; } }; void createBinTree(binTreeTest **r) { QString item; QTextStream qin(stdin); qin>>item; int data = item.toInt(); binTreeTest *T_l,*T_r; if(item != "#") { (*r)->setData(data); T_l = new binTreeTest; T_r = new binTreeTest; createBinTree(&T_l); (*r)->set_left(T_l); createBinTree(&T_r); (*r)->set_right(T_r); } else { *r = NULL; } } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); binTreeTest *tree = new binTreeTest; qDebug()<<"輸入二叉樹前序序列進行建樹,\"#\"代表空節點"<<endl; createBinTree(&tree); return a.exec(); }
在使用的過程中,注意C++的使用:https://www.cnblogs.com/pinking/p/9339201.html。
2.2 二叉樹的遍歷
二叉樹的遍歷分為兩種策略:深度優先和廣度優先,深度優先又包含:中序遍歷、前序遍歷、后序遍歷。記憶的時候就看中間那個是什么就是什么序的遍歷。
中序遍歷:左子樹---> 根結點 ---> 右子樹
前序遍歷:根結點 ---> 左子樹 ---> 右子樹
后序遍歷:左子樹 ---> 右子樹 ---> 根結點
層次遍歷:僅僅需按層次遍歷就可以
構造二叉樹如圖:
輸入構造二叉樹:
中序遍歷代碼:
void middle(binTreeTest *t) { if(t != NULL) { binTreeTest *l,*r; l = t->get_left(); middle(l); qDebug()<<t->getData(); r = t->get_right(); middle(r); } }
中序遍歷輸出結果:
同理,前序遍歷應該的結果為:1、2、3、4、5、6、7、8、9 。代碼如下:

1 void forward(binTreeTest *t) 2 { 3 if(t != NULL) 4 { 5 qDebug()<<t->getData(); 6 forward(t->get_left()); 7 forward(t->get_right()); 8 } 9 }
后序遍歷的結果應該為:4、5、3、2、8、9、7、6、1 。代碼如下:

1 void backward(binTreeTest *t) 2 { 3 if(t != NULL) 4 { 5 backward(t->get_left()); 6 backward(t->get_right()); 7 qDebug()<<t->getData(); 8 } 9 }
層次遍歷,通常采用隊列的形式,保持先進先出的方式,層次遍歷每一層,結果為:1、2、6、3、7、4、5、8、9 。代碼如下:

1 void level(binTreeTest *t) 2 { 3 if(t == NULL) 4 return; 5 QQueue<binTreeTest*> queue; 6 queue.enqueue(t); 7 while (!queue.empty()) 8 { 9 binTreeTest *ptem = queue.front(); 10 qDebug()<<ptem->getData(); 11 queue.pop_front(); 12 if(ptem->get_left() != NULL) 13 { 14 queue.push_back(ptem->get_left()); 15 } 16 if(ptem->get_right() != NULL) 17 { 18 queue.push_back(ptem->get_right()); 19 } 20 } 21 }
2.3 求葉子節點的個數
求葉子節點的個數還是比較好理解的,代碼如下:
int get_leaf_num(binTreeTest *t) { if(t == NULL) return 0; if(t->get_left() == NULL && t->get_right() == NULL) { return 1; } return get_leaf_num(t->get_left())+get_leaf_num(t->get_right()); }
2.4 求樹的深度
int getTreeHeight(binTreeTest *t) { if (t == NULL) return 0; if(t->get_left() == NULL && t->get_left() == NULL) { return 1; } int l_height = getTreeHeight(t->get_left()); int r_height = getTreeHeight(t->get_right()); return l_height>=r_height?l_height+1:r_height+1; }
2.5 交換樹的左右子樹
void swap_tree(binTreeTest *t) { if(t == NULL) return; binTreeTest *temp = new binTreeTest; temp = t->get_right(); t->set_right(t->get_left()); t->set_left(temp); swap_tree(t->get_left()); swap_tree(t->get_right()); }
2.6 判斷一個節點是否在一棵子樹中
其實思想和遍歷是一樣的,代碼如下:
bool isInTree(binTreeTest *tree,binTreeTest *nodeTree) { //查找的節點為NULL時,代表沒有查到 if(tree == NULL) return false; else if(tree->getData() == nodeTree->getData()) { return true; } else { bool has = false; if(tree->get_left() != NULL) { has = isInTree(tree->get_left(),nodeTree); } if((has == false) && (tree->get_right() != NULL)) { has = isInTree(tree->get_right(),nodeTree); } return has; } }
2.7 求樹中總共的節點個數
int getNumNode(binTreeTest *t) { if(t == NULL) return 0; return getNumNode(t->get_left())+getNumNode(t->get_right())+1; }
2.8 判斷兩個樹是否相同
bool compared(binTreeTest *tree,binTreeTest *tree2) { //兩個樹都是空 if(tree == NULL && tree2 == NULL) return true; if(tree == NULL || tree2 == NULL)//其中有一個為NULL就不用比了 return false; if(tree->getData() == tree2->getData()) { return compared(tree->get_left(),tree2->get_left())&&compared(tree->get_right(),tree2->get_right()); } else { return false; } }
2.9 判斷一個樹是否為另一棵樹的子樹
這個需要結合上面的判斷兩棵樹是否相同來判斷:
bool compared(binTreeTest *tree,binTreeTest *tree2) { //兩個樹都是空 if(tree == NULL && tree2 == NULL) return true; if(tree == NULL || tree2 == NULL)//其中有一個為NULL就不用比了 return false; if(tree->getData() == tree2->getData()) { return compared(tree->get_left(),tree2->get_left())&&compared(tree->get_right(),tree2->get_right()); } else { return false; } } bool judgeNode(binTreeTest *tree,binTreeTest *child) { // 兩個都是空樹 if(tree == NULL && child == NULL) return true; // 空樹是任意的子樹 if(child == NULL) return true; // 空樹沒有其他非空的子樹 if(tree == NULL) return false; //排除空樹的情況 if(tree->getData() == child->getData()) { return compared(tree,child); } else { bool result = judgeNode(tree->get_left(),child); if(result == false) return judgeNode(tree->get_right(),child); return result; } }