二叉樹非遞迴實現
二叉樹非遞迴實現會比較難理解一點,不過只要理解了非遞迴的,那麼遞迴的就非常好理解了。接下來進行圖文詳解。
C程式碼下載
C++程式碼下載
java程式碼下載
( 備用地址下載)
導航
1.建立二叉樹
2.前序遍歷二叉樹
3.中序遍歷二叉樹
4.後序遍歷二叉樹
5.層次遍歷二叉樹
6.計算二叉樹深度
7.計算二叉樹雙分支節點數
8.計算二叉樹單分支節點數
9.計算二叉樹葉子節點數
10.新增節點
11.查詢二叉樹中的節點
注:有一些重複的程式碼且多的就不重複貼出來了,需要的可以點上面的連結去下載。
一、建立二叉樹
按照前序遍歷來建立,給定一個串,其規則是空格代表空節點
例如給定串:ABC D EF G ;
建立步驟如下:
1.C程式碼
/*
* function 建立二叉樹(前序建立)
* param char* s 根據給定字串建立
* return 返回二叉樹的根節點
*/
PBTree CreateBTree(char* s)
{
if (!s || s[0] == '\0') //如果s為空 則樹也為空
return NULL;
Stack stack = CreateStack(); //建立一個棧
int i = 0;
// 1.先建立根節點
PBTree root = CreateNode(s[i++]);
PBTree btree = root; //用一個臨時的指標代替root,因為最後要返回root指標,所以它不能改變
Push(&stack,root); //將根節點壓入棧中
int len = _csize(s);
while (i < len)
{
// 2.如果當前讀到的字元不為空,並且當前節點的左孩子不存在,則建立它
if (s[i] != ' ' && btree->bLCExits == false)
{
btree->left = CreateNode(s[i]); //建立左孩子
btree->bLCExits = true; //左孩子存在
btree = btree->left;
Push(&stack, btree); //入棧
++i;
}
// 3.如果當前讀到的字元不為空,並且當前節點的右孩子不存在,則建立它
else if (s[i] != ' ' && btree->bRCExits == false)
{
btree->right = CreateNode(s[i]);//建立右孩子
btree->bRCExits = true; //右孩子存在
btree = btree->right;
Push(&stack, btree); //入棧
++i;
}
// 4.如果當前讀到的字元為空,並且當前節點的左孩子不存在,則將當前節點的左孩子置為存在(空也算孩子)
else if (s[i] == ' ' && btree->bLCExits == false) //空節點
btree->bLCExits = true, ++i; //左孩子存在
// 5.如果當前讀到的字元為空,並且當前節點的右孩子不存在,則將當前節點的右孩子置為存在(空也算孩子)
else if (s[i] == ' ' && btree->bRCExits == false)
btree->bRCExits = true, ++i; //右孩子存在
// 6.回溯回去,當該節點的度為2的時候(就是左右孩子都存在的情況)
if (btree->bLCExits && btree->bRCExits)
Pop(&stack),btree = Top(&stack);
}
DestroyStack(&stack); //最後銷燬棧
return root;
}
2.C++程式碼
/*
* function 建立二叉樹(前序建立)
* param char* s 根據給定串建立
* return 無
*/
template<typename T>
void NonRecursionBTree<T>::Create(string s)
{
if (s.empty()) //如果s為空 則樹也為空
return ;
stack<Node*> st; //建立一個棧
int i = 0;
// 1.先建立根節點
m_root = new Node(s[i++]);
Node* btree = m_root; //用一個臨時的指標代替root,因為最後要返回root指標,所以它不能改變
st.push(btree); //將根節點壓入棧中
int len = s.size();
while (i < len)
{
// 2.如果當前讀到的字元不為空,並且當前節點的左孩子不存在,則建立它
if (s[i] != ' ' && btree->bLCExits == false)
{
btree->left = new Node(s[i]); //建立左孩子
btree->bLCExits = true; //左孩子存在
btree = btree->left;
st.push(btree); //入棧
++i;
}
// 3.如果當前讀到的字元不為空,並且當前節點的右孩子不存在,則建立它
else if (s[i] != ' ' && btree->bRCExits == false)
{
btree->right = new Node(s[i]); //建立右孩子
btree->bRCExits = true; //右孩子存在
btree = btree->right;
st.push(btree); //入棧
++i;
}
// 4.如果當前讀到的字元為空,並且當前節點的左孩子不存在,則將當前節點的左孩子置為存在(空也算孩子)
else if (s[i] == ' ' && btree->bLCExits == false) //空節點
btree->bLCExits = true, ++i; //左孩子存在
// 5.如果當前讀到的字元為空,並且當前節點的右孩子不存在,則將當前節點的右孩子置為存在(空也算孩子)
else if (s[i] == ' ' && btree->bRCExits == false)
btree->bRCExits = true, ++i; //右孩子存在
// 6.回溯回去,當該節點的度為2的時候(就是左右孩子都存在的情況)
if (btree->bLCExits && btree->bRCExits)
st.pop(), btree = st.top();
}
while (!st.empty()) //最後銷燬棧
st.pop();
}
3.java程式碼
/*
* function 建立二叉樹(前序建立)
* param String s 根據給定串建立
* return 無
*/
public void create(String s) {
if (s.isEmpty()) //如果s為空 則樹也為空
return ;
Stack<Node> st = new Stack<Node>(); //建立一個棧
int i = 0;
// 1.先建立根節點
root = new Node(s.charAt(i++));
Node btree = root; //用一個臨時的指標代替root,因為最後要返回root指標,所以它不能改變
st.push(btree); //將根節點壓入棧中
int len = s.length();
while (i < len) {
// 2.如果當前讀到的字元不為空,並且當前節點的左孩子不存在,則建立它
if (s.charAt(i) != ' ' && btree.bLCExits == false) {
btree.left = new Node(s.charAt(i)); //建立左孩子
btree.bLCExits = true; //左孩子存在
btree = btree.left;
st.push(btree); //入棧
++i;
}
// 3.如果當前讀到的字元不為空,並且當前節點的右孩子不存在,則建立它
else if (s.charAt(i) != ' ' && btree.bRCExits == false) {
btree.right = new Node(s.charAt(i)); //建立右孩子
btree.bRCExits = true; //右孩子存在
btree = btree.right;
st.push(btree); //入棧
++i;
}
// 4.如果當前讀到的字元為空,並且當前節點的左孩子不存在,則將當前節點的左孩子置為存在(空也算孩子)
else if (s.charAt(i) == ' ' && btree.bLCExits == false) {//空節點
btree.bLCExits = true;
++i; //左孩子存在
}
// 5.如果當前讀到的字元為空,並且當前節點的右孩子不存在,則將當前節點的右孩子置為存在(空也算孩子)
else if (s.charAt(i) == ' ' && btree.bRCExits == false) {
btree.bRCExits = true;
++i; //右孩子存在
}
// 6.回溯回去,當該節點的度為2的時候(就是左右孩子都存在的情況)
if (btree.bLCExits && btree.bRCExits) {
st.pop();
btree = st.peek();
}
}
st.clear(); //最後銷燬棧
}
1).為什麼要用兩個標誌位bLCExits和bRCExits?
這樣子比較容易找到回溯點,就是當左右兩個孩子都存在了就回溯,不能根據是否為空來判斷是否回溯。
二、前序遍歷二叉樹
參考出處,點選跳轉
遍歷順序是:根節點 -> 左節點 -> 右節點
然後一個二叉樹又可以分為很多子樹,每一顆子樹都會有根、左、右節點。
/*
* function 前序遍歷
* param PBTree root
* return 無
*/
void PreOrder(PBTree root)
{
Stack stack = CreateStack(); //建立一個棧
PBTree btree = root; //建立臨時指標指向root
// 1. 若當前節點不為空,或者棧中元素不為空(相當於還沒有遍歷完所有節點)
while (btree || !StackIsEmpty(&stack))
{
// 2. 先遍歷左子樹,一直到左子樹為空為止。
while (btree)
{
printf("%c", btree->data);
Push(&stack,btree);
btree = btree->left;
}
// 3.若棧中還有元素,則將當前btree賦值為它的右子樹,然後再重複 1~2步驟
if (!StackIsEmpty(&stack))
{
btree = Top(&stack);
Pop(&stack);
btree = btree->right;
}
}
printf("\n");
}
三、中序遍歷二叉樹
遍歷順序是:左節點 -> 根節點 -> 右節點
/*
* function 中序遍歷
* param PBTree root
* return 無
*/
void InOrder(PBTree root)
{
Stack stack = CreateStack(); //建立一個棧
PBTree btree = root; //建立臨時指標指向root
// 1. 若當前節點不為空,或者棧中元素不為空(相當於還沒有遍歷完所有節點)
while (btree || !StackIsEmpty(&stack))
{
// 2. 先遍歷左子樹,一直到左子樹為空為止。
while (btree)
{
Push(&stack, btree);
btree = btree->left;
}
// 3.若棧中還有元素,則將當前btree賦值為它的右子樹,然後再重複 1~2步驟
if (!StackIsEmpty(&stack))
{
btree = Top(&stack);
printf("%c", btree->data); //遍歷完左子樹後輸出它們的根節點
Pop(&stack);
btree = btree->right;
}
}
printf("\n");
}
四、後序遍歷二叉樹
遍歷順序:左節點 -> 右節點 -> 根節點
/*
* function 後序遍歷
* param PBTree root
* return 無
*/
void PostOrder(PBTree root)
{
Stack stack = CreateStack(); //建立一個棧
PBTree cur; //當前結點
PBTree pre = NULL; //前一次訪問的結點
Push(&stack,root); //先將根節點入棧
while (!StackIsEmpty(&stack))
{
cur = Top(&stack); //這裡的cur就像是每顆子樹的根節點,然後重複這些步驟
if ((!cur->left && !cur->right) ||
(pre && (pre == cur->left || pre == cur->right)))
{
printf("%c", cur->data); //如果當前結點沒有孩子結點或者孩子節點都已被訪問過
Pop(&stack);
pre = cur;
}
else
{
if (cur->right != NULL) //這裡先將右孩子入棧,這樣出棧的時候,左孩子就先出棧
Push(&stack,cur->right);
if (cur->left != NULL)
Push(&stack,cur->left);
}
}
printf("\n");
}
五、層次遍歷二叉樹
層次遍歷就簡單了,這個要用到佇列
步驟為:
1.將根節點入隊
2.將當前節點置為隊頭節點 (cur = front)
3.出隊
4.訪問當前節點
5.如果當前節點的左孩子不為空,左孩子入隊
6.如果當前節點的右孩子不為空,有孩子入隊
7.重複2~6步驟即可
這個就相當於訪問上一層的節點的時候,將下一層的結點依次入隊以待訪問
/*
* function 層次遍歷
* param PBTree root
* return 無
*/
void translevel(PBTree root)
{
if (!root) return;
Queue queue = CreateQueue(); //建立一個佇列
PBTree cur = root; //當前節點
QPush(&queue, root); //先將根節點加入佇列中
while (!QueueIsEmpty(&queue)) //當佇列中沒有元素時,遍歷完成
{
cur = Front(&queue); //獲取當前隊頭元素
QPop(&queue); //遍歷過後就出隊
printf("%c", cur->data); //先輸出該子樹的根節點
if (cur->left) //如果左孩子不為空,則入隊等待遍歷
QPush(&queue, cur->left);
if (cur->right) //如果右孩子不為空,則入隊等待遍歷
QPush(&queue, cur->right);
}
printf("\n");
}
六、計算二叉樹深度
利用層次遍歷來求,遍歷的最大層數即深度
/*
* function 計算二叉樹深度
* param PBTree root
* return 無
*/
int BTreeDepth(PBTree root)
{
if (!root) return 0;
Queue queue = CreateQueue(); //建立一個佇列
PBTree cur = root; //當前節點
int iDepth = 0;
QPush(&queue, root); //先將根節點加入佇列中
while (!QueueIsEmpty(&queue)) //當佇列中沒有元素時,遍歷完成
{
++iDepth; //外層迴圈控制層數
int curLevelNodes = queue.count; //當前層數的節點數
int temp = 0; //臨時記錄已經遍歷的節點數
while (temp++ < curLevelNodes) //當遍歷完當前層數後,退出內層迴圈,繼續遍歷下一層
{
cur = Front(&queue); //獲取當前隊頭元素
QPop(&queue); //遍歷過後就出隊
if (cur->left) //如果左孩子不為空,則入隊等待遍歷
QPush(&queue, cur->left);
if (cur->right) //如果右孩子不為空,則入隊等待遍歷
QPush(&queue, cur->right);
}
}
return iDepth;
}
七、計算二叉樹雙分支節點數
利用前序遍歷來求
/*
* function 計算二叉樹雙分支節點數
* param PBTree root
* return 無
*/
int GetN2(PBTree root)
{
Stack stack = CreateStack(); //建立一個棧
PBTree btree = root; //建立臨時指標指向root
int count = 0;
//利用前序遍歷來訪問所有的節點
while (btree || !StackIsEmpty(&stack))
{
while (btree)
{
Push(&stack, btree);
btree = btree->left;
}
if (!StackIsEmpty(&stack))
{
btree = Top(&stack);
if (btree) //再這裡能保證所有的節點都能被遍歷到
if (btree->left && btree->right) //當該節點有兩個分支的時候+1
++count;
Pop(&stack);
btree = btree->right;
}
}
return count;
}
八、計算二叉樹單分支節點數
和計算雙分支節點的方法一樣,只需要把判斷語句改一下即可
if (btree) //再這裡能保證所有的節點都能被遍歷到
if ((btree->left && !btree->right) || (!btree->left && btree->right)) //當該節點僅且只有一個分支的時候+1
++count;
九、計算二叉樹葉子節點數
這個就簡單了,有一個公式: n0 = n2 + 1
/*
* function 計算二叉樹終端節點數
* param PBTree root
* return 無
*/
int GetN0(PBTree root)
{
return GetN2(root) + 1; //計算公式n0 = n2 + 1;
}
十、新增節點
可以用前序、中序、後序、層次遍歷的方法來新增,前三個的缺點很明顯,最後新增後可能會退化成一個長長的單鏈表。所以這裡採用層次遍歷的方法新增,一層層掃描,遇到空節點就新增在它那裡。
/*
* function 新增節點值(新增的位置是不確定的)
* param PBTree root
* param char ch
* return 無
*/
void AddValue(PBTree root, char ch)
{
//採用層次遍歷的辦法,一層層掃描,若有空的地方,則新增到該地方
if (!root)
{
root = CreateNode(ch);
return;
}
Queue queue = CreateQueue(); //建立一個佇列
PBTree cur = root; //當前節點
QPush(&queue, root); //先將根節點加入佇列中
while (!QueueIsEmpty(&queue)) //當佇列中沒有元素時,遍歷完成
{
cur = Front(&queue); //獲取當前隊頭元素
QPop(&queue); //遍歷過後就出隊
if (cur->left) //如果左孩子不為空,則入隊等待遍歷
QPush(&queue, cur->left);
else //否則就新增值,然後退出
{
cur->left = CreateNode(ch);
break;
}
if (cur->right) //如果右孩子不為空,則入隊等待遍歷
QPush(&queue, cur->right);
else //否則就新增值,然後退出
{
cur->right = CreateNode(ch);//否則就新增值,然後退出
break;
}
}
}
十一、查詢二叉樹中的節點
用層次遍歷的方法查詢
/*
* function 查詢值
* param PBTree root
* param char ch
* return 成功返回true,否則返回false
*/
bool Search(PBTree root, char ch)
{
//利用層次遍歷來查詢
if (!root) return false;
Queue queue = CreateQueue(); //建立一個佇列
PBTree cur = root; //當前節點
QPush(&queue, root); //先將根節點加入佇列中
while (!QueueIsEmpty(&queue)) //當佇列中沒有元素時,遍歷完成
{
cur = Front(&queue); //獲取當前隊頭元素
if (cur->data == ch)
return true;
QPop(&queue); //遍歷過後就出隊
if (cur->left) //如果左孩子不為空,則入隊等待遍歷
QPush(&queue, cur->left);
if (cur->right) //如果右孩子不為空,則入隊等待遍歷
QPush(&queue, cur->right);
}
return false;
}