二叉树 1.0 -- 创建二叉树、遍历二叉树、二叉树常见问题求解
樹的結(jié)構(gòu)主要是為了查找,這個(gè)主要是為了搜索,樹的結(jié)構(gòu)關(guān)注的不是增刪查改
樹
廣義上面的樹的結(jié)構(gòu)我們不知道樹的一個(gè)節(jié)點(diǎn)是有幾個(gè)子節(jié)點(diǎn)的,所以這個(gè)時(shí)候我們需要定義的一種結(jié)構(gòu)就是,一個(gè)節(jié)點(diǎn)的孩子是可以動(dòng)態(tài)的增加的,這個(gè)時(shí)候我們就可以把一個(gè)一個(gè)的節(jié)點(diǎn)放置在vector中,這個(gè)時(shí)候某一個(gè)節(jié)點(diǎn)的孩子節(jié)點(diǎn)是可以動(dòng)態(tài)的增加的
還有一種結(jié)構(gòu)就是,我們可以把一個(gè)節(jié)點(diǎn)中定義兩個(gè)節(jié)點(diǎn)指針,一個(gè)指向的是當(dāng)前節(jié)點(diǎn)的兄弟,一個(gè)指向的是當(dāng)前節(jié)點(diǎn)的第一個(gè)孩子節(jié)點(diǎn),那么它的其他的孩子節(jié)點(diǎn)就由第一個(gè)孩子節(jié)點(diǎn)去標(biāo)記了
struct TreeNode
{int _data;TreeNode* _firstchild;TreeNode* _nextSibling;
}
樹的應(yīng)用:文件管理
樹的存儲(chǔ)結(jié)構(gòu)
在樹的存儲(chǔ)結(jié)構(gòu)中主要介紹三種存儲(chǔ)結(jié)構(gòu):雙親表示法,孩子表示法,孩子兄弟表示法
雙親表示法
除了我們的根節(jié)點(diǎn)之外,我們其他噶所有的節(jié)點(diǎn)都是有雙親的,我們?cè)诙x樹的存儲(chǔ)結(jié)構(gòu)的時(shí)候可以在每個(gè)節(jié)點(diǎn)中附設(shè)一個(gè)指示器,用來(lái)標(biāo)識(shí)當(dāng)前節(jié)點(diǎn)的雙親節(jié)點(diǎn),然后我們可以在一個(gè)數(shù)組中開辟連續(xù)的空間用來(lái)存儲(chǔ)我們樹的結(jié)構(gòu)。
template<class T>
struct TreeNode
{T _data;int _parent; //數(shù)組中的下標(biāo)
}
template<class T>
class Tree
{typedef TreeNode<T> Node;private:Node _nodes[10]; int _root,_n; //用來(lái)標(biāo)記根的位置和樹的節(jié)點(diǎn)的個(gè)數(shù)
}
上面的存儲(chǔ)結(jié)構(gòu)中,因?yàn)槲覀兊母?jié)點(diǎn)是沒有雙親的,我們這里可以不妨設(shè)雙親節(jié)點(diǎn)的標(biāo)記雙親的那個(gè)參數(shù)的大小為-1.
這個(gè)時(shí)候就存在了一個(gè)問題就是,我們只能夠通過(guò)一個(gè)節(jié)點(diǎn)找到它的雙親,但是不能雙親找到孩子節(jié)點(diǎn),這里要考慮把每個(gè)節(jié)點(diǎn)中在加上一個(gè)標(biāo)識(shí)位,用來(lái)標(biāo)識(shí)孩子的位置,比如設(shè)計(jì)成下面的這種形式的
template<class T>
struct TreeNode
{T _data;int _parent; //數(shù)組中的下標(biāo)int _fiestchild; //我們成這個(gè)域?yàn)殚L(zhǎng)子域
}
如果我們的使用場(chǎng)景中還特別的關(guān)注一個(gè)節(jié)點(diǎn)和他兄弟之間的關(guān)系,我們還可以在節(jié)點(diǎn)域里面加上leftsib和rightsib等,就是根據(jù)我們的需求設(shè)計(jì)我們的節(jié)點(diǎn)結(jié)構(gòu)就可以了。
下面還留了其他的一些思考的內(nèi)容
還需要思考的另外的一個(gè)問題就是,每當(dāng)我們?cè)黾右粋€(gè)節(jié)點(diǎn)的時(shí)候如何找到知道他們雙親節(jié)點(diǎn)呢,就是比如說(shuō)我們知道一個(gè)節(jié)點(diǎn)的值是5,然后同時(shí)還有另外一個(gè)節(jié)點(diǎn)的值也是5,然后我們的第一個(gè)5這個(gè)節(jié)點(diǎn)有一個(gè)孩子是1,那么當(dāng)我們的這個(gè)1添加進(jìn)去的時(shí)候我們?nèi)绾闻袛噙@個(gè)節(jié)點(diǎn)的父親到底是第一個(gè)5呢,還是第二個(gè)5呢
還有就是刪除一個(gè)節(jié)點(diǎn)的時(shí)候,我們需要判斷的就是,此時(shí)我們是需要遍歷的,我們想要?jiǎng)h除一個(gè)節(jié)點(diǎn)的孩子節(jié)點(diǎn),這個(gè)時(shí)候我們是需要遍歷的,然后找到這個(gè)節(jié)點(diǎn)的父親是不是我們想要?jiǎng)h除的那一個(gè),這個(gè)時(shí)候在進(jìn)行刪除,那么這種遍歷是不是很麻煩呢
還有就是其他的一些問題的時(shí)候,如果我們是需要遍歷的話,我想要一個(gè)遍歷方式是層序的遍歷是不是也會(huì)很麻煩呢,這個(gè)時(shí)候會(huì)伴隨著大量的遍歷
孩子表示法
在講孩子表示法之前,我們先講一個(gè)與孩子表示法類似的方法,就是多重鏈表表示法,就是我們的節(jié)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)中,有一個(gè)數(shù)據(jù)域,然后剩下的是指向孩子節(jié)點(diǎn)的指針,類似于下圖的表示
但是上面的存儲(chǔ)結(jié)構(gòu)還是存在問題的,就是我們的指針域指向孩子的那些空間,因?yàn)楹⒆硬淮_定,可能會(huì)造成空間的浪費(fèi),于是我們又出現(xiàn)了下面的一種存儲(chǔ)結(jié)構(gòu)。
我們可以在節(jié)點(diǎn)結(jié)構(gòu)中加上一個(gè)孩子個(gè)數(shù),有幾個(gè)孩子我們這類就開辟出幾個(gè)孩子指針節(jié)點(diǎn),但是這種方式也是存在一些問題的,就是我們的維護(hù)的代價(jià)太大了,所以我們有了下面的一種方式,既能節(jié)省存儲(chǔ)的空間,還能很好的維護(hù),這就是孩子表示法,我們可以在設(shè)計(jì)節(jié)點(diǎn)的時(shí)候,包含兩部分內(nèi)存,一個(gè)是數(shù)據(jù)域,還有一個(gè)是指向孩子的鏈表指針,就是下圖的方式進(jìn)行存儲(chǔ)
這里二叉樹的結(jié)構(gòu)的設(shè)計(jì)如下
//指針節(jié)點(diǎn)
template<class T>
struct PointNode
{int _child; //標(biāo)記孩子的下標(biāo)TreeNode* _next; //指向下一個(gè)孩子
}
//孩子節(jié)點(diǎn)
template<class T>
struct TreeNode
{T _data;PointNode<T>* _firstchild;
}
template<class T>
class Tree
{TreeNode<T> _nodes[10];int _root,_n;
}
其實(shí)為了解決多重鏈表表示法造成的空間浪費(fèi)的問題,我們還可以利用STL中的vector,我們指向節(jié)點(diǎn)的指針域可以不用放置一個(gè)一個(gè)的鏈表,我們這里可以直接放置一個(gè)vector,我們可以直接把孩子節(jié)點(diǎn)放在vector中。就是類似于下面的存儲(chǔ)結(jié)構(gòu)
tempalte<class T>
struct TreeNode
{T _data;vector<TreeNode<T>> _child;
}
孩子兄弟表示法
孩子兄弟表示法的方法是我認(rèn)為比較巧妙的一種方法,就是我們的每個(gè)節(jié)點(diǎn)它如果有都會(huì)有唯一一個(gè)長(zhǎng)子,每個(gè)節(jié)點(diǎn)如果有,他都會(huì)有唯一一個(gè)右孩子,于是我們?cè)O(shè)計(jì)出下面的存儲(chǔ)結(jié)構(gòu)
template<class T>
struct TreeNode
{T _data;TreeNode* _fistchild;TreeNode* _rightchild;
}
二叉樹的結(jié)構(gòu)
上面的廣義上的樹不是我們學(xué)習(xí)的重點(diǎn),我們學(xué)習(xí)的重點(diǎn)的是二叉樹,包括現(xiàn)實(shí)中的使用最多的也是二叉樹,以及一些成熟的數(shù)據(jù)結(jié)構(gòu)也是用二叉樹的一些包裝。
二叉樹的結(jié)構(gòu)
- 我們可以把一個(gè)數(shù)組看成是一個(gè)二叉樹,這個(gè)在以后的堆里面會(huì)有很好的運(yùn)用,雖然數(shù)組在實(shí)際物理上面是連續(xù)存儲(chǔ)著的,但是我們?cè)谠L問一些數(shù)據(jù),以及對(duì)數(shù)據(jù)的一些操作的時(shí)候,是按照樹的結(jié)構(gòu)進(jìn)行處理的,比如下面的一種情況,首先看一個(gè)圖
上圖中,實(shí)際在內(nèi)存中存儲(chǔ)數(shù)據(jù)的時(shí)候,是按照數(shù)組的方式存儲(chǔ)的,但是我們對(duì)數(shù)據(jù)訪問操作的時(shí)候是按照樹的結(jié)構(gòu)去訪問的,比如要訪問一個(gè)節(jié)點(diǎn)的子節(jié)點(diǎn),而當(dāng)前節(jié)點(diǎn)的下標(biāo)是n,那么它的左孩子節(jié)點(diǎn)的下標(biāo)就是n * 2+1,右孩子節(jié)點(diǎn)就是n * 2+2,同樣的,一個(gè)的父親節(jié)點(diǎn)的下標(biāo)是(n-1)/2
左孩子 n * 2 + 1
右孩子 n * 2 + 2
父親節(jié) (n - 1)/2
分析:如果我們的數(shù)據(jù)很集中,比如滿二叉樹或者是完全二叉樹的時(shí)候可以使用上面的結(jié)構(gòu),但是如果我們的數(shù)據(jù)不是連續(xù)的,甚至是中間有很多空閑的位置的時(shí)候,就不建議使用數(shù)組這樣的結(jié)構(gòu),這回造成空間的很大的浪費(fèi)。
- 常用的一種結(jié)構(gòu)是一個(gè)節(jié)點(diǎn)有兩個(gè)指向孩子的指針。
這種結(jié)構(gòu)叫做二叉鏈
template<class T>
struct TreeNode
{T _data;TreeNode<T>* _left;TreeNode<T>* _right;
}
- 還有一種結(jié)構(gòu)是有一個(gè)指向父親的節(jié)點(diǎn),有兩個(gè)指向孩子的節(jié)點(diǎn)
這種結(jié)構(gòu)是三叉鏈
template<class T>
struct TreeNode
{T _data;TreeNode<T>* _father;TreeNode<T>* _left;TreeNode<T>* _right;
}
創(chuàng)建一個(gè)樹
傳入一個(gè)數(shù)組,然后使用前序(其他順序),構(gòu)建一棵樹,傳參的時(shí)候,需要傳入的時(shí)候是數(shù)組,數(shù)組大小還有一個(gè)就是非法值
從數(shù)組里面一個(gè)一個(gè)取數(shù)據(jù),然后構(gòu)建到樹里面直到,遇到非法值或者是遇到了結(jié)尾的時(shí)候,這個(gè)時(shí)候偶就讓當(dāng)前節(jié)點(diǎn)的根為NULL
創(chuàng)建一個(gè)樹,在構(gòu)造函數(shù)中需要調(diào)用的函數(shù)是creatTree,然后參數(shù)是數(shù)組,數(shù)組大小,非法值,數(shù)組下標(biāo),返回值是node*
把這個(gè)創(chuàng)建樹的代碼背下來(lái)
{1,2,3,0,0,4,0,0,5,6}
這里我們的非法值是0,首先看下面的代碼
#include<iostream>
using namespace std;// {}template<class T>
struct TreeNode
{T _data;TreeNode<T>* _left;TreeNode<T>* _right;TreeNode(T data):_data(data), _left(NULL), _right(NULL){}
};template<class T>
class BinaryTree
{typedef TreeNode<T> Node;
public:BinaryTree():_root(NULL){}BinaryTree(const T* arr,const T& inval,const size_t& n){size_t index = 0;_root = _CreatTree(arr,n,index,inval);}private:Node* _CreatTree(const T* arr,const size_t& n,size_t& index,const T& inval){Node* root = NULL; if (arr[index] != inval && index < n){root = new Node(arr[index]);root->_left = _CreatTree(arr,n,++index,inval);root->_right = _CreatTree(arr,n,++index,inval);}return root;}private:Node* _root;};void TestTree()
{int arr[] = {1,2,3,0,0,4,0,0,5,6};BinaryTree<int> tree(arr,0,10);
}
下面是對(duì)上面代碼的一個(gè)分析,首先應(yīng)該注意的一個(gè)就是,我們需要在構(gòu)造函數(shù)里面調(diào)用一個(gè)_CreatTree()的函數(shù)來(lái)進(jìn)行樹的創(chuàng)建,因?yàn)樵?創(chuàng)建樹的時(shí)候,需要遞歸的調(diào)用,所以這里不能直接一直調(diào)用構(gòu)造函數(shù),這顯然是不合理的
創(chuàng)建樹的函數(shù) – _CreatTree()
接下來(lái)主要需要講述的就是創(chuàng)建樹的函數(shù),首先是函數(shù)的返回值,因?yàn)槲覀冃枰f歸的調(diào)用,在調(diào)用的過(guò)程中的一個(gè)大的思路就是,首先,讓根節(jié)點(diǎn)等于函數(shù)的返回值,到函數(shù)的內(nèi)部創(chuàng)建一個(gè)root的節(jié)點(diǎn)指針,然后是如果數(shù)組中的數(shù)據(jù)符合要求就開辟一個(gè)節(jié)點(diǎn)的空間,然后就是root指向它,接著是是root的左和右分別的調(diào)用_CreatTree()函數(shù),最后返回root。通過(guò)這樣的操作之后,就可以首先是_root指向一個(gè)節(jié)點(diǎn),接下來(lái)是遞歸的使當(dāng)前節(jié)點(diǎn)的左右孩子節(jié)點(diǎn)調(diào)用,就可以使左右孩子節(jié)點(diǎn)也指向相應(yīng)的操作
==把上面的創(chuàng)建樹的代碼,包括下面的一些代碼,其實(shí)可以考慮背下來(lái),因?yàn)檫@些代碼是最最基礎(chǔ)的東西了,在理解的基礎(chǔ)之上背下來(lái),這些是以后深入學(xué)習(xí)的一個(gè)基礎(chǔ),不光光是要背,更加重要的是理解其中的一些==
參數(shù)index
參數(shù)index為什么使用的是引用呢,這里如果我們不使用引用的話,就會(huì)出現(xiàn)一些問題,首先還是看上面的我們給出的例子
int arr[] = {1,2,3,0,0,4,0,0,5,6};
首先第一層遞歸里面的時(shí)候,我們的index是0,然后創(chuàng)建一個(gè)節(jié)點(diǎn),把1放置進(jìn)去了,然后進(jìn)入到下一層遞歸,這個(gè)時(shí)候index是1,然后把數(shù)據(jù)’2’放置進(jìn)去了,大家記住這層遞歸里面的index是1,然后進(jìn)入到第三層遞歸的時(shí)候,index是2,這個(gè)時(shí)候把數(shù)據(jù)‘3’放置進(jìn)去了,接著往下走的時(shí)候,index是3,這個(gè)時(shí)候數(shù)據(jù)是0,這是個(gè)非法值,于是再次遞歸,這個(gè)時(shí)候index是4,也是一個(gè)非法值,這個(gè)時(shí)候,關(guān)于數(shù)據(jù)3這一層的遞歸函數(shù)已經(jīng)結(jié)束了,所以返回上一層遞歸,上一層遞歸的時(shí)候,index是1,就是又回到了數(shù)據(jù)2的那一層遞歸調(diào)用了,這個(gè)時(shí)候++index后,又變成了3,所以節(jié)點(diǎn)數(shù)據(jù)2的右孩子又是3,所以這里是錯(cuò)誤,這個(gè)是我們應(yīng)該注意的一個(gè)問題。
二叉樹的遍歷
前序遍歷,中序遍歷,后序遍歷,層序遍歷
看到一個(gè)二叉樹的時(shí)候,想象成三個(gè)部分,根節(jié)點(diǎn),左子樹,右子樹
前序:根左右
中序:左根右
后序:左右根
層序:一層一層
關(guān)于遍歷的思想特別的重要
==一定要深刻的理解各種遍歷的思想,因?yàn)橄旅娴暮枚鄦栴}其實(shí)都是遍歷思想的變形==
二叉樹的遍歷的這一點(diǎn)的知識(shí)很重要,因?yàn)楹竺娴暮芏嗟膬?nèi)容,很多的題目,很多的求解的方法都是使用了遍歷的思想的
前序遍歷
==思想==
根左右:訪問一個(gè)節(jié)點(diǎn)的時(shí)候先訪問數(shù)據(jù),然后訪問左孩子,然后訪問右孩子
public:
void PrevOrder(){_PrevOrder(_root);}
private:
void _PrevOrder(Node* root){if (root != NULL){cout << root->_data << " ";_PrevOrder(root->_left);_PrevOrder(root->_right);}}
前序遍歷和后面的很多的遍歷包括其他的問題是需要有兩個(gè)部分組成的,為什么一個(gè)設(shè)置成公有的,一個(gè)設(shè)置成私有的呢,因?yàn)槲覀兊谋闅v的時(shí)候是需要訪問 _ root的,但是我們肯定不能在外面用戶使用的時(shí)候直接讓他們調(diào)用前序遍歷的時(shí)候,將_root,作為參數(shù)傳遞進(jìn)去,這顯然是實(shí)現(xiàn)不了的,所以這里需要在寫一個(gè)私有的函數(shù),讓公有的函數(shù)去調(diào)用這個(gè)私有的函數(shù),然后讓這個(gè)私有的函數(shù)去實(shí)現(xiàn)遍歷的功能。
關(guān)于函數(shù)的實(shí)現(xiàn)部分就是按照拿到一個(gè)節(jié)點(diǎn)的時(shí)候首先要做的就是如果當(dāng)前節(jié)點(diǎn)不是NULL的話,就訪問數(shù)據(jù),如果是NULL的,就結(jié)束么,我上面寫代碼的時(shí)候,我把遞歸調(diào)用那里放置在了if判斷的外面了,這是錯(cuò)誤的,因?yàn)榉胖迷趇f判斷的外面的話,當(dāng)前節(jié)點(diǎn)已經(jīng)是NULL了,我們是無(wú)法訪問它的左右節(jié)點(diǎn)的。
中序遍歷
==思想==
左根右:訪問一個(gè)節(jié)點(diǎn)的時(shí)候,首先訪問它的左孩子,然后訪問數(shù)據(jù),然后在訪問訪問右孩子
public:
void InOrder(){_InOrder(_root);cout << endl;}
private:
void _InOrder(Node* root){if (root != NULL){_InOrder(root->_left);cout << root->_data << " ";_InOrder(root->_right);}}
沒什么好解釋的了,結(jié)合著前序遍歷就可以理解中序遍歷,然后就可以理解后序遍歷了
后序遍歷
==思想==
左右根:訪問一個(gè)節(jié)點(diǎn)的時(shí)候,首先訪問它的左孩子,然后在訪問訪問右孩子,然后訪問數(shù)據(jù)
public:
void EndOrder(){_EndOrder(_root);cout << endl;}
private:
void _EndOrder(Node* root){if (root != NULL){_EndOrder(root->_left);_EndOrder(root->_right);cout << root->_data << " ";}}
層序遍歷
==思想==
一層一層的遍歷
層序遍歷的代碼中我們使用一個(gè)隊(duì)列,首先我們把根節(jié)點(diǎn)放置在隊(duì)列中去,這個(gè)時(shí)候隊(duì)列里面至少有一個(gè)節(jié)點(diǎn)了 ,接下來(lái)要做的就是每次從隊(duì)列中拿取一個(gè)數(shù)據(jù),然后把這個(gè)節(jié)點(diǎn)的數(shù)據(jù)打印出來(lái),接著就是判斷這個(gè)節(jié)點(diǎn)的左右兩個(gè)孩子是否為NULL,如果不是的就把左右兩個(gè)節(jié)點(diǎn)指針放置在隊(duì)列中去。
public:
void LevelOrder(){queue<Node*> que;if (_root){que.push(_root);}while (!que.empty()){Node* cur = que.front();que.pop();cout << cur->_data << " ";if (cur->_left){que.push(cur->_left);}if (cur->_right){que.push(cur->_right);}}}
二叉樹的各種求解
二叉樹的各種求解中都基本上使用了二叉樹遍歷的一些思想
需要用到遞歸的思想:就是我想要求當(dāng)前節(jié)點(diǎn)的某個(gè)解,我需要求解它的左右子樹的某個(gè)解
求樹的所有節(jié)點(diǎn)的個(gè)數(shù)
==思想==
我需要求解以當(dāng)前節(jié)點(diǎn)為根的節(jié)點(diǎn)的個(gè)數(shù),我需要求解當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)的個(gè)數(shù)
返回條件就是,如果當(dāng)前節(jié)點(diǎn)為NULL的話,就直接返回0,如果當(dāng)前節(jié)點(diǎn)不是NULL,就返回當(dāng)前節(jié)點(diǎn)的孩子節(jié)點(diǎn)的個(gè)數(shù)+1,這個(gè)1表示的就是當(dāng)前節(jié)點(diǎn)的值。
這個(gè)求解的過(guò)程可以理解為一個(gè)后序遍歷的思想,就是我想要給當(dāng)前節(jié)點(diǎn)的所有節(jié)點(diǎn)的個(gè)數(shù),我首先應(yīng)該求解的是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)的個(gè)數(shù),然后才能給當(dāng)前節(jié)點(diǎn)的個(gè)數(shù)加上+1。
代碼很重要,背下來(lái),在這個(gè)代碼的基礎(chǔ)之上,思考其他的一些內(nèi)容
int NodeNum(){return _NodeNum(_root);}
private:
int _NodeNum(Node* root){if (root == NULL){return 0;}return _NodeNum(root->_left) + _NodeNum(root->_right) + 1;}
求樹的葉子節(jié)點(diǎn)的個(gè)數(shù)
==思想==
這個(gè)時(shí)候其實(shí)思想還是使用了后序遍歷的思想,就是我想要求解當(dāng)前節(jié)點(diǎn)的葉子節(jié)點(diǎn)的個(gè)數(shù),我需要求解的是當(dāng)前節(jié)點(diǎn)的孩子節(jié)點(diǎn)的葉子節(jié)點(diǎn)的個(gè)數(shù)
public:
int LeafNum(){return _LeafNum(_root);}
private:
int _LeafNum(Node* root){if (root == NULL){return 0;}if ((root->_left == NULL) && (root->_right == NULL)){return 1;}return _LeafNum(root->_left) + _LeafNum(root->_right);}
這個(gè)內(nèi)容我們需要和上面的求解節(jié)點(diǎn)的個(gè)數(shù)做一個(gè)詳細(xì)的比較,我們都應(yīng)該以當(dāng)前節(jié)點(diǎn)為一個(gè)判斷的依據(jù)。這個(gè)時(shí)候我們很容易糾結(jié)的一個(gè)問題就是,是不是需要單獨(dú)的判斷左右呢,單獨(dú)的判斷左右的時(shí)候,又如何進(jìn)行相加呢,這個(gè)時(shí)候就容易除了問題了。看我們上面的代碼就很好的解決了這個(gè)問題,如果當(dāng)前節(jié)點(diǎn)是NULL,,當(dāng)前節(jié)點(diǎn)很可能就是從上面的父節(jié)點(diǎn)傳遞過(guò)來(lái)的,所以很好的避免了我們需要單獨(dú)分開判斷的問題了。這個(gè)思想當(dāng)中我們需要注意的問題就是我們的返回條件是兩種的,一種就是返回0,一種是返回1。
求第K層的個(gè)數(shù)
==思想==
我們想要求第k層節(jié)點(diǎn)的個(gè)數(shù),需要求解的是第k-1層節(jié)點(diǎn)的個(gè)數(shù)
public:
int LevelNodeNum(int k){return _LevelNodeNum(_root,k);}
int _LevelNodeNum(Node* root,int k){if (root == NULL)return 0;if ((k == 1) && (NULL != root) )return 1;else{return _LevelNodeNum(root->_left, k - 1) + _LevelNodeNum(root->_right, k - 1);}}
上面的方式是一種很巧妙的方法,但是我們還是應(yīng)該理解其中的道理,這個(gè)問題和上面的問題一樣,我們還是需要考慮子問題還有幾十需要考慮的是返回值,這里的返回值也是有兩種返回條件,一種是返回0的時(shí)候,還有一種是返回1的狀態(tài)。
上面的問題讓我直接想可能還有點(diǎn)困難,但是讓我結(jié)合著代碼理解的話,問題是不大的
求二叉樹的高度(深度)
==思想==
這里還是子問題,如果我們想要求解當(dāng)前節(jié)點(diǎn)的深度,首先需要求解的是當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)的高度,然后+1,加的這個(gè)1就是當(dāng)前的一個(gè)節(jié)點(diǎn)。但是這里返回的時(shí)候我哦們需要放回的應(yīng)該是左右子樹最大的那一個(gè),但是如果用三目運(yùn)算符,或者是使用如下的方式的話,
if(_High(root->_left) > _High(root->_right))
{return _High(root->_left);
}
就可能造成了兩次的調(diào)用了遞歸函數(shù)。所以這里我們采用的是首先用一個(gè)數(shù)據(jù)保存起來(lái)的方式。
int High(){return _High(_root);}
private:
int _High(Node* root){if (root == NULL){return 0;}int l = _High(root->_left);int r = _High(root->_right);if (l > r)return l + 1;elsereturn r + 1;}
查找某一個(gè)節(jié)點(diǎn)
==思想==
這個(gè)問題和其他的問題有點(diǎn)出入的,我們需要從中間的某一個(gè)位置返回一個(gè)節(jié)點(diǎn)指針,但是這里返回就是一個(gè)問題,所以我們應(yīng)該注意到這個(gè)問題
private:
Node* Find(T data){return _Find(_root,data);}
Node* _Find(Node* root,T data){if (NULL == root){return NULL;}else{if (root->_data == data){return root;}Node* ret = _Find(root->_left,data);if (ret){return ret;}ret = _Find(root->_right,data);if(ret)return ret;return NULL;}}
上面的代碼背下來(lái),這是學(xué)習(xí)其他東西和解決問題的一些基礎(chǔ),一開始學(xué)習(xí)人家的代碼就是要后來(lái)更好的運(yùn)用
總結(jié)
以上是生活随笔為你收集整理的二叉树 1.0 -- 创建二叉树、遍历二叉树、二叉树常见问题求解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 排卵期输卵管疼是为什么
- 下一篇: 二叉树 2.0 -- 非递归遍历