寫文的目的是鍛鍊自己,歡迎各位大牛提出建議,批評指正~
第一題:把二分查找樹轉變成排序的雙向鏈表
輸入一棵二分查找樹,將該二分查找樹轉換成一個排序的雙向鏈表。要求不能創建任何新的結點,只調整指針的指向。如:
10
/ \
6 14
/ \ / \
4 8 12 16
轉換成雙向鏈表4=6=8=10=12=14=16 。
我們定義的二分查找樹結點的數據結構如下:
struct BSTreeNode
{
int m_nValue; // value of node
BSTreeNode *m_pLeft; // left child of node
BSTreeNode *m_pRight; // right child of node
};
我的解題思路如下:
本題中不能建立新的結點,只能改變指針的指向。先觀察根結點,根據二叉查找樹的特點,可以發現根結點的左指針應該指向左子樹雙向鏈表的尾部,而根結點的右指針
應該指向右子樹雙向鏈表的頭部。因此該問題可以遞歸爲求解左、右孩子的雙向鏈表。求解完左、右孩子的雙向鏈表後,再接上根結點,即可完成對雙向鏈表的求解。
根據上述思路,先來看一下代碼結構,之後會有例子講解。
編寫如下的遞歸函數:
void ConvertRecursive(BSTNode* &subHead, BSTNode* &subTail, BSTNode* subRoot)
然後在函數中求解左、右孩子的雙向鏈表:
BSTNode *leftTail = NULL, *rightHead = NULL;
ConvertRecursive(subHead,leftTail,subRoot->pLeft); //求解左子的鏈表
ConvertRecursive(rightHead,subTail,subRoot->pRight); //求解右子的鏈表
。。。。。。 //將左子鏈表、根、右子鏈表連接起來
這個遞歸初看可能有點不清晰,我們可以先看一下求解左子鏈表的語句:
ConvertRecursive(subHead,leftTail,subRoot->pLeft);
這個subHead可看成是當前鏈表的頭,在遞歸結束後,最上一層的subHead將是整個雙向鏈表的頭。而leftTail是我們想要的左孩子雙向鏈表的尾部。
ConvertRecursive(rightHead,subTail,subRoot->pRight);
rightHead是我們想要的右孩子雙向鏈表的頭部,而subTail可看成是當前鏈表的尾,在遞歸結束後,最上一層的subTail將是整個雙向鏈表的尾。
現在看一下左子鏈表、根與右子鏈表連接的操作:如果左子沒有雙向鏈表,那麼根結點就成爲當前鏈表的頭;如果有,根結點就連接上左子鏈表的尾部。
同理,如果右子沒有鏈表,那麼根結點就成爲當前鏈表的尾;如果有,則根結點與右子鏈表的頭部相連接。
//連接左子鏈表與根
if (leftTail == NULL) //左子沒有雙向鏈表
{
subHead = subRoot;
}
else
{
leftTail->pRight = subRoot;
subRoot->pLeft = leftTail;
}//連接右子鏈表與根
if (rightHead == NULL) //右子沒有雙向鏈表
{
subTail = subRoot;
}
else
{
subRoot->pRight = rightHead;
rightHead->pLeft = subRoot;
}
以子樹 6 爲例,
/ \
4 8
遞歸到4時,因爲4是葉子結點,其左、右子樹鏈表爲空,則有subHead -> 4, subTail -> 4。同理8也是這種情況。
返回到6的調用,因爲6調用4的遞歸中,形參subTail實際上是實參leftTail的引用,因此6的左子鏈表leftTail->4不爲空,按照之前的思路,我們可以將結點6與左子鏈表連
接起來,就有了鏈表4=6;同理,在6調用8的遞歸中,形參subHead實際上是實參rightHead的引用,因此6的右子鏈表rightHead->8也不爲空,將6與右子鏈表連接起來,我們
有了雙向鏈表4=6=8,而此時當前鏈表的subHead->4,subTail->8。
由此,可以看出整個遞歸過程中,leftTail和rightHead起到了連接整個鏈表中間結點的作用,而subHead和subTail則保存了目前遞歸層次中的最小和最大值。
完整的代碼如下,因爲自己有一個BST樹結點的模板類,所以直接拿來用了,BSTNode<int>和題目中的BSTreeNode的結構是一樣的。
//written by zero
#include "BST.h"
#include <iostream>
using namespace std;
void ConvertRecursive(BSTNode<int>* &subHead, BSTNode<int>* &subTail, BSTNode<int>* subRoot)
{
BSTNode<int> *leftTail = NULL, *rightHead = NULL;
if (subRoot == NULL)
{
subHead = NULL;
subTail = NULL;
return;
}
ConvertRecursive(subHead,leftTail,subRoot->pLeft); //求解左子的鏈表
ConvertRecursive(rightHead,subTail,subRoot->pRight); //求解右子的鏈表
//將左子鏈表,根,右子鏈表接在一起
//連接左子鏈表與根
if (leftTail == NULL) //左子沒有雙向鏈表
{
subHead = subRoot;
}
else
{
leftTail->pRight = subRoot;
subRoot->pLeft = leftTail;
}
//連接右子鏈表與根
if (rightHead == NULL) //右子沒有雙向鏈表
{
subTail = subRoot;
}
else
{
subRoot->pRight = rightHead;
rightHead->pLeft = subRoot;
}
}
int main()
{
//建立二叉查找樹
BSTree<int> tree;
tree.Insert(10);
tree.Insert(6);
tree.Insert(14);
tree.Insert(4);
tree.Insert(8);
tree.Insert(12);
tree.Insert(16);
/*tree.TravelRecursive(&BSTree<int>::PosOderRecursive,&BSTree<int>::process);
cout << endl;
tree.TravelRecursive(&BSTree<int>::PosOderNonRecursive,&BSTree<int>::process);
cout << endl;*/
//轉換成雙向鏈表
BSTNode<int> *head = NULL, *tail = NULL, *pNode;
ConvertRecursive(head,tail,tree.root);
//驗證正向遍歷雙向鏈表
cout << "正向遍歷鏈表:";
pNode = head;
while(pNode)
{
cout << pNode->value << " ";
pNode = pNode->pRight;
}
cout << endl;
//驗證逆向遍歷雙向鏈表
cout << "逆向遍歷鏈表:";
pNode = tail;
while(pNode)
{
cout << pNode->value << " ";
pNode = pNode->pLeft;
}
cout << endl;
}
運行結果如下: