二叉樹的創建我們已經瞭解了,現在我們在這一個基礎上來拓展一些常出現的題
1、在二叉樹中尋找一個節點——>存在,返回節點,不存在返回NULL
2、求一個節點的雙親節點——>存在,返回節點,不存在返回NULL
3、求二叉樹的高度
4、求葉子節點的個數
5、求第K層的節點個數
6、求一個二叉樹的鏡像
……
不過在這這錢我們先來補充一下二叉樹的性質:
1、若規定根節點的層次爲0,則一個非空的二叉樹的第i個節點最多有2^i(i>=0)個節點。
2、若規定只有根節點的深度爲0,則深度爲K的二叉樹的最大節點數爲2^(K+1)-1(K>=-1).
3、對任何二叉樹,如果葉子節點爲n0,度爲n2的飛葉子節點的n2,則有n0=n2+1
證明過程如下:
4、具有n個節點的完全二叉樹的深度K爲log2(n+1)向上取整
懂得人都知道log後面的不是2*(n+1),而是“以2爲底(n+1)”。具體怎麼驗證這個性質,那麼不妨回顧一下完全二叉樹的定義吧。而且這個性質的中心位置就是完全二叉樹。
5、對具有N的節點的完全二叉樹,如果按照從上到下,從左到右,的順序對所有的節點進行排序,從0開始編號。則:i
①、i的雙親節點爲(i-1)/2
②、i的左孩子,爲2*i+1,右孩子爲2*i+2
③、i=0,則爲根節點,無雙親節點
④2*i+1>=N,2*i+2>=N,則沒有雙親節點
瞭解了這些之後,我們再來看一下這些編程題:
1、在二叉樹中尋找一個節點——>存在,返回節點,不存在返回NULL
如果用先序的遍歷方式,那麼用遞歸的話只需四步即可
非遞歸的話,也是可以參考先序遍歷的非遞歸的寫法,只需將對根節點的處理,改成和要查找的節點進行比較即可
Node* _Find(Node* pRoot, const T& value)
{
//遞歸:
//if (NULL == pRoot)//樹爲空,則返回空
// return NULL;
//if (pRoot->_value == value)//先確定根結點是不是所要查找的值
// return pRoot;
//_Find(pRoot->_pLeft, value);//再查找左右子樹
//_Find(pRoot->_pRight, value);
//非遞歸:
if (pRoot == NULL)
return NULL;
stack<Node*> s;
s.push(pRoot);
while (!s.empty())
{
Node *pCur = s.top();
if (pCur->_value == value)//注意:兩個value類型不一樣
return pCur;
s.pop();//時刻記住,訪問完後要出棧
if (pCur->_pRight)//注意,入棧順序是先右後左
s.push(pCur->_pRight);
if (pCur->_pLeft)
s.push(pCur->_pLeft);
}
}
2、求一個節點的雙親節點——>存在,返回節點,不存在返回NULL
首先我們得將參數檢驗一下:對根結點檢驗,判斷樹是不是空樹;對pCur檢驗,驗證它是不是存在;還有一根特殊的情況,根結點沒有雙親。之後其他的都是普通情況。再進行遞歸運算。
Node* _Parent(Node* pRoot, Node* pCur)
{
if (NULL == pRoot)//首先確定樹是不是空的
return NULL;
//參數驗證2:判斷pCur是不是爲NULL 或 根結點是不是pCur(根結點是沒有雙親結點的)
if (NULL == pCur || pCur == pRoot)
return NULL;
if (pRoot->_pLeft)//在左子樹中查找
return _Parent(pRoot->_pLeft, pCur);
return _Parent(pRoot->_pRight, pCur);//無論找沒找到,都返回(這一步可以拆分開)
3、求二叉樹的高度
一個二叉樹的高度是多少?如果說根結點的層數爲1的話,那最下面一層的層數就是二叉樹的高度。那麼用遞歸的話,它是左子樹的高度和右子樹高度的最大值再加1。
size_t _Height(Node* pRoot)
{
if (NULL == pRoot)//沒有結點,返回0
return 0;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
return 1;
//遍歷左右子樹,返回左右子樹最大值 +1(根結點)
return max(_Height(pRoot->_pLeft),_Height(pRoot->_pRight))+1;
}
4、求葉子節點的個數
首先想一下葉子結點的特點是啥,沒有左右子樹,所以用遞歸的話,這就是我們的一個遞歸出口,那麼葉子結點的個數就是:遞歸左子樹的個數加上遞歸右子樹的個數。
size_t _GetLeafCount(Node* pRoot)
{
if (NULL == pRoot)
return 0;
//葉子結點
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
return 1;
//遞歸:左子樹的葉子結點+右子樹的葉子結點
return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
}
5、求第K層的節點個數
第K層的結點個數,如果K=高度,那麼這種情況和葉子結點個數還是不一樣,葉子結點不一定都在同一層,所以,我們要求第K層的需要將左子樹的第K-1層的計算出來,再加上右子樹的第K-1層結點個數。
不過這裏涉及參數檢驗的討論,對於根結點的檢驗我們已經很熟悉了,判斷其是不是空樹就行了,不過這裏還有一個參數:K
對於K來說,有一個特殊的情況那就是K==1,那麼我們就返回1,至於對其進行參數檢驗,只要其不小於1就行了;那麼你或許會問了,爲什麼不對它進行上限處理呢?需要嗎?
/*假設我們進行了上限處理:K<Height(),我們這是遞歸,每一次進入這個函數走到這一步是不是都需要求一下高度,每次求高度還要將樹遍歷一下,那麼算下來我們求了幾次了,很多。而不加的時候會出錯嗎?我們假設K是5,(層數是3),那麼到第四層(所有節點都是NULL)直接會從判斷根結點爲空的地方返回,所以,這是不會影響結果的。*/
//第k層的結點個數
size_t _GetKLevelCount(Node* pRoot, size_t k)
{
if (NULL == pRoot || k < 1)//注意:參數檢驗
return 0;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
return 1;
return _GetKLevelCount(pRoot->_pLeft, k - 1) + _GetKLevelCount(pRoot->_pRight, k - 1);
}
6、求一個二叉樹的鏡像
如圖所示,我們看到,左邊的是我們一直使用的一個二叉樹,鏡像之後我們可以直觀上看到,兩個二叉樹的關係:關於中間的一條隱形的線(鏡子)對稱。那麼接下來我們就來根據這一個性質來求一個二叉樹的鏡像
首先我們處理的是二叉樹的特殊情況:
1、要是樹爲空,那麼就不用處理了,直接返回就行了
2、鏡像是將二叉樹中的左子樹和右子樹交換,那麼如果這個數只有一個節點,那麼就不用交換了。所以這一個情況是,二叉樹只有一個節點。
3.普通情況:這要是用遞歸的話,很簡單的幾句代碼就搞定了:首先是將根節點的兩個子樹交換,之後再進入左子樹進行遞歸,然後再進入右子樹進行遞歸。
這樣之後,就完了,不過非遞歸又該怎麼寫呢?
同樣我們來屢一下思路:
1、首先還式對樹進行判斷:樹空的話,返回NULL
2、其次創建一個數據結構(隊列),將根節點入隊列
3、取出對頭元素,判斷其左右子樹,存在,則入隊列
4、對頭元素的左右子樹。最後將對頭元素出隊列
void _BinaryMirror(Node* pRoot)
{
if (pRoot == NULL)
return;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)
return;
swap(pRoot->_pLeft, pRoot->_pRight);
if (pRoot->_pLeft!=NULL)
_BinaryMirror(pRoot->_pLeft);
if (pRoot->_pRight!=NULL)
_BinaryMirror(pRoot->_pRight);
}
void _BinaryMirror_Nor(Node* pRoot)
{
if (pRoot == NULL)
return;
queue<Node*> q;
q.push(pRoot);
while (!q.empty())
{
Node* pCur = q.front();//取隊頭元素
//如果左右孩子存在,將其保存在隊列中
if (pCur->_pLeft)
q.push(pCur->_pLeft);
if (pCur->_pRight)
q.push(pCur->_pRight);
//交換左右
swap(pCur->_pLeft, pCur->_pRight);
//隊頭元素出隊列
q.pop();
}
}
下面是完整的代碼,以及測試的語句:
#include<iostream>
using namespace std;
#include<queue>
#include<stack>
// 孩子表示法
template<class T>
struct BinaryTreeNode
{
BinaryTreeNode(const T& value)
: _value(value)
, _pLeft(NULL)
, _pRight(NULL)
{}
T _value;
BinaryTreeNode<T>* _pLeft; // 左孩子
BinaryTreeNode<T>* _pRight; // 右孩子
};
template<class T>
class BinaryTree
{
typedef BinaryTreeNode<T> Node;//換個名字Node
public:
//構造函數①
BinaryTree()
:_pRoot(NULL)
{}
//構造函數②
BinaryTree(const T array[], size_t size, const T& invalid)
{
size_t index = 0;
_CreateBinaryTree(_pRoot, array, size, index, invalid);
}
//拷貝構造函數
BinaryTree(const BinaryTree<T>& bt)
{
_pRoot = _CopyBirnaryTree(bt._pRoot);
}
//賦值運算符重載
BinaryTree<T>& operator=(const BinaryTree<T>& bt)
{
if (this != &bt)
{
_CopyBirnaryTree(bt._pRoot);
}
return this;
}
//析構函數
~BinaryTree()
{
_DestroyBinaryTree(_pRoot);
}
// 層序遍歷
void LevelOrder()
{
cout << "LevelOrder:" << endl;
_LevelOrder(_pRoot);
cout << endl;
}
//查找函數
Node* Find(const T& value)
{
return _Find(_pRoot, value);
}
// 找一個結點的雙親結點
Node* Parent(Node* pCur)
{
return _Parent(_pRoot, pCur);
}
//找一個結點的左孩子結點
Node* GetLeftChild(Node* pCur)
{
return (pCur == NULL) ? NULL : pCur->_pLeft;
}
//找一個結點的右孩子結點
Node* GetRightChild(Node* pCur)
{
return (pCur == NULL) ? NULL : pCur->_pRight;
}
//求二叉樹的高度
size_t Height()
{
return _Height(_pRoot);
}
//求二叉樹葉子結點的個數
size_t GetLeafCount()
{
return _GetLeafCount(_pRoot);
}
//求第K層結點的個數
size_t GetKLevelCount(size_t k)
{
return _GetKLevelCount(_pRoot, k);
}
//求二叉樹的鏡像 (非遞歸)
void BinaryMirror_Nor()
{
_BinaryMirror_Nor(_pRoot);
}
//求二叉樹的鏡像
void BinaryMirror()
{
_BinaryMirror(_pRoot);
}
private:
//創建二叉樹
void _CreateBinaryTree(Node* &pRoot, const T array[], size_t size, size_t& index, const T& invalid)
{
if (index < size&&array[index] != invalid)
{
pRoot = new Node(invalid);//注意new的時候要和結構體中寫的函數參數對應
pRoot->_value = array[index];
_CreateBinaryTree(pRoot->_pLeft, array, size, ++index, invalid);//注意:++index
_CreateBinaryTree(pRoot->_pRight, array, size, ++index, invalid);
}
}
// pRoot-->被拷貝樹的根節點
Node* _CopyBirnaryTree(Node* pRoot)
{
if (pRoot == NULL)
return NULL;
Node *NewRoot = new Node(pRoot->_value);
NewRoot->_pLeft = _CopyBirnaryTree(pRoot->_pLeft);
NewRoot->_pRight = _CopyBirnaryTree(pRoot->_pRight);
return NewRoot;
}
//銷燬二叉樹
void _DestroyBinaryTree(Node*& pRoot)
{
Node* temp = pRoot;
if (temp == NULL)
return;
_DestroyBinaryTree(temp->_pLeft);
_DestroyBinaryTree(temp->_pRight);
delete temp;
temp = NULL;
}
Node* _Find(Node* pRoot, const T& value)
{
//遞歸:
//if (NULL == pRoot)//樹爲空,則返回空
// return NULL;
//if (pRoot->_value == value)//先確定根結點是不是所要查找的值
// return pRoot;
//_Find(pRoot->_pLeft, value);//再查找左右子樹
//_Find(pRoot->_pRight, value);
//非遞歸:
if (pRoot == NULL)
return NULL;
stack<Node*> s;
s.push(pRoot);
while (!s.empty())
{
Node *pCur = s.top();
if (pCur->_value == value)//注意:兩個value類型不一樣
return pCur;
s.pop();//時刻記住,訪問完後要出棧
if (pCur->_pRight)//注意,入棧順序是先右後左
s.push(pCur->_pRight);
if (pCur->_pLeft)
s.push(pCur->_pLeft);
}
}
//查找pCur的雙親結點
Node* _Parent(Node* pRoot, Node* pCur)
{
if (NULL == pRoot)//首先確定樹是不是空的
return NULL;
//參數驗證2:判斷pCur是不是爲NULL 或 根結點是不是pCur(根結點是沒有雙親結點的)
if (NULL == pCur || pCur == pRoot)
return NULL;
if (pRoot->_pLeft)//在左子樹中查找
return _Parent(pRoot->_pLeft, pCur);
return _Parent(pRoot->_pRight, pCur);//無論找沒找到,都返回(這一步可以拆分開)
}
size_t _Height(Node* pRoot)
{
if (NULL == pRoot)//沒有結點,返回0
return 0;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
return 1;
//遍歷左右子樹,返回左右子樹最大值 +1(根結點)
return max(_Height(pRoot->_pLeft),_Height(pRoot->_pRight))+1;
}
size_t _GetLeafCount(Node* pRoot)
{
if (NULL == pRoot)
return 0;
//葉子結點
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
return 1;
//遞歸:左子樹的葉子結點+右子樹的葉子結點
return _GetLeafCount(pRoot->_pLeft) + _GetLeafCount(pRoot->_pRight);
}
//第k層的結點個數
size_t _GetKLevelCount(Node* pRoot, size_t k)
{
if (NULL == pRoot || k < 1)//注意:參數檢驗
return 0;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)//只有一個結點,返回1
return 1;
return _GetKLevelCount(pRoot->_pLeft, k - 1) + _GetKLevelCount(pRoot->_pRight, k - 1);
}
void _BinaryMirror(Node* pRoot)
{
if (pRoot == NULL)
return;
if (pRoot->_pLeft == NULL&&pRoot->_pRight == NULL)
return;
swap(pRoot->_pLeft, pRoot->_pRight);
if (pRoot->_pLeft!=NULL)
_BinaryMirror(pRoot->_pLeft);
if (pRoot->_pRight!=NULL)
_BinaryMirror(pRoot->_pRight);
}
void _BinaryMirror_Nor(Node* pRoot)
{
if (pRoot == NULL)
return;
queue<Node*> q;
q.push(pRoot);
while (!q.empty())
{
Node* pCur = q.front();//取隊頭元素
//如果左右孩子存在,將其保存在隊列中
if (pCur->_pLeft)
q.push(pCur->_pLeft);
if (pCur->_pRight)
q.push(pCur->_pRight);
//交換左右
swap(pCur->_pLeft, pCur->_pRight);
//隊頭元素出隊列
q.pop();
}
}
private:
Node* _pRoot; // 指向樹的根節點
};
void FunTest2()
{
char* pStr = "124###35##6";
BinaryTree<char> bt(pStr, strlen(pStr), '#');
//cout << bt.Find('5')->_value << endl;//注意:傳參的類型
////cout << bt.Parent(bt.Find('5'))->_value << endl;
//cout << bt.Height() << endl;//高度
//cout << bt.GetLeafCount() << endl;//葉子結點個數
//cout << bt.GetKLevelCount(3) << endl;
//bt.BinaryMirror(); //????
//bt.BinaryMirror_Nor();
}
int main()
{
FunTest2();
return 0;
}