二叉樹的簡單面試題彙總
目錄
1.前中後序遍歷(遞迴與非遞迴)
1.前序遍歷
1.遞迴
void preOrder1(BinTree *root)
{
if (root != NULL)
{
cout << root->data << " ";
preOrder1(root->lchild);
preOrder1(root->rchild);
}
}
2.非遞迴
對於任一結點P:
1)訪問結點P,並將結點P入棧;
2)判斷結點P的左孩子是否為空,若為空,則取棧頂結點並進行出棧操作,並將棧頂結點的右孩子置為當前的結點P,迴圈至1);若不為空,則將P的左孩子置為當前的結點P;
3)直到P為NULL並且棧為空,則遍歷結束。
void preOrder2(BinTree *root) { stack<BinTree*> s; BinTree *p = root; while (p != NULL || !s.empty()) { while (p != NULL) { cout << p->data << " "; s.push(p); p = p->lchild; } if (!s.empty()) { p = s.top(); s.pop(); p = p->rchild; } } }
2.中序遍歷
1.遞迴
void inOrder1(BinTree *root)
{
if (root != NULL)
{
inOrder1(root->lchild);
cout << root->data << " ";
inOrder1(root->rchild);
}
}
2.非遞迴
對於任一結點P,
1)若其左孩子不為空,則將P入棧並將P的左孩子置為當前的P,然後對當前結點P再進行相同的處理;
2)若其左孩子為空,則取棧頂元素並進行出棧操作,訪問該棧頂結點,然後將當前的P置為棧頂結點的右孩子;
3)直到P為NULL並且棧為空則遍歷結束。
void inOrder2(BinTree *root)
{
stack<BinTree*> s;
BinTree *p = root;
while (p != NULL || !s.empty())
{
while (p != NULL)
{
s.push(p);
p = p->lchild;
}
if (!s.empty())
{
p = s.top();
cout << p->data << " ";
s.pop();
p = p->rchild;
}
}
}
3.後序遍歷
1.遞迴
void postOrder1(BinTree *root)
{
if (root != NULL)
{
postOrder1(root->lchild);
postOrder1(root->rchild);
cout << root->data << " ";
}
}
2.非遞迴
要保證根結點在左孩子和右孩子訪問之後才能訪問,因此對於任一結點P,先將其入棧。如果P不存在左孩子和右孩子,則可以直接訪問它;或者P存 在左孩子或者右孩子,但是其左孩子和右孩子都已被訪問過了,則同樣可以直接訪問該結點。若非上述兩種情況,則將P的右孩子和左孩子依次入棧,這樣就保證了 每次取棧頂元素的時候,左孩子在右孩子前面被訪問,左孩子和右孩子都在根結點前面被訪問。
void postOrder3(BinTree *root)
{
stack<BinTree*> s;
BinTree *cur; //當前結點
BinTree *pre = NULL; //前一次訪問的結點
s.push(root);
while (!s.empty())
{
cur = s.top();
if ((cur->lchild == NULL&&cur->rchild == NULL) ||
(pre != NULL && (pre == cur->lchild || pre == cur->rchild)))
{
cout << cur->data << " "; //如果當前結點沒有孩子結點或者孩子節點都已被訪問過
s.pop();
pre = cur;
}
else
{
if (cur->rchild != NULL)
s.push(cur->rchild);
if (cur->lchild != NULL)
s.push(cur->lchild);
}
}
}
2.層序遍歷
void printTree(BinTree *root)//層序遍歷
{
queue<BinTree *> s;
s.push(root);
while (!s.empty())
{
BinTree *p = s.front();
cout << p->data << "";
s.pop();
if (p->lchild != NULL)
s.push(p->lchild);
if (p->rchild != NULL)
s.push(p->rchild);
}
}
3.求二叉樹高度(深度)
int Depth(BinTree* root)
{
if (root == NULL)
return 0;
int left = Depth(root->lchild);
int right = Depth(root->rchild);
return left > right ? left + 1 : right + 1;
}
4.求葉子節點個數
size_t GetLeafSize(BinTree* root)//求葉子節點數
{
if (root == NULL)
return 0;
if (root->lchild == NULL
&& root->rchild == NULL)
{
return 1;
}
return GetLeafSize(root->lchild)
+ GetLeafSize(root->rchild);
}
5.求二叉樹第K層節點個數
1.求二叉樹第K層節點個數(遞迴)
int get_k_level_number(BinTree *root, int k)
{
if (root == NULL || k <= 0)
return 0;
if (root != NULL && k == 1)
return 1;
return (get_k_level_number(root->lchild, k - 1) +
get_k_level_number(root->rchild,k - 1));
}
2.非遞迴實現求二叉樹第k層的節點數
6.判斷一個節點是否在一棵二叉樹上
bool IsInTree(BinTree* root, int x)
{
if (root == NULL)
return false;
if (root->data == x)
return true;
return IsInTree(root->lchild, x)
|| IsInTree(root->rchild, x);
}
7.求兩個節點的最近公共祖先
1.搜尋樹
1 某一個節點為根節點,那麼公共節點就是根節點了。
2 這倆個節點在不同子樹,那麼公共節點就是根節點。
3 這倆個節點在同一子樹。
所以根據以上分析,可得我們可以通過不斷的遞迴直到找到一個子樹,在該子樹中倆節點分別位於它的左子樹和右子樹,那麼該子樹節點就是公共節點。
BinTree* findCom_ancestor(BinTree *root, BinTree *x, BinTree *x2)
{
if (root == NULL) return NULL;
if (x == root || x2 == root) return root;
if (x->data > root->data&&x2->data < root->data)
{
return root;
}
if (x->data < root->data&&x2->data > root->data)
{
return root;
}
if (x->data < root->data)
{
return findCom_ancestor(root->lchild, x, x2);
}
else
{
return findCom_ancestor(root->rchild, x, x2);
}
}
2.三叉鏈
1.給定的兩個節點都含有父節點,因此,可將這兩個節點看做是兩個連結串列的頭結點,將求兩個節點的最近公共祖先節點轉化為求兩連結串列的交點,這兩個連結串列的尾節點都是根節點。(O(n))
int Hight(BinTree* root, BinTree* node)
{
int len = 0;
while (node != NULL)
{
len++;
node = node->parent;
}
return len;
}
BinTree* GetLastCommonAncestor(BinTree* root, BinTree* node1, BinTree* node2)
{
if (root == NULL || node1 == NULL || node2 == NULL)
return NULL;
int len1 = Hight(root, node1);
int len2 = Hight(root, node2);
for (; len1 > len2; len1--)
node1 = node1->parent;
for (; len2 > len1; len2--)
node2 = node2->parent;
while (node1 && node2 && node1 != node2)
{
node1 = node1->parent;
node2 = node2->parent;
}
if (node1 == node2)
return node1;
else
return NULL;
}
2.首先給出node1的父節點node1->_parent,然後將node1的所有父節點依次和node2->parent作比較,如果發現兩個節點相等,則該節點就是最近公共祖先,直接將其返回。如果沒找到相等節點,則將node2的所有父節點依次和node1->_parent->_parent作比較......直到node1->_parent==NULL。O(n^2)
BinTree * GetLastCommonAncestor(BinTree * root, BinTree * node1, BinTree * node2)
{
BinTree * temp;
while (node1 != NULL)
{
node1 = node1->parent;
temp = node2;
while (temp != NULL)
{
if (node1 == temp->parent)
return node1;
temp = temp->parent;
}
}
}
3.一般二叉樹
從根節點開始遍歷,如果node1和node2中的任一個和root匹配,那麼root就是最低公共祖先。
如果都不匹配,則分別遞迴左、右子樹,
如果有一個 節點出現在左子樹,並且另一個節點出現在右子樹,則root就是最低公共祖先.
如果兩個節點都出現在左子樹,則說明最低公共祖先在左子樹中,否則在右子樹。
該函式當一個節點是另一個節點的祖先時,返回的是離根節點最近的那個節點,要想返回最近公共祖先節點需進行判斷兩節點是否有祖孫關係,
若兩節點為F,D,則判斷出B的左子樹中的D節點後,繼續判斷判斷節點D的左右子樹中是否含有節點F,若有,則返回B,若沒有則繼續判斷右子樹中的另一個節點。
BinTree* GetLastCommonAncestor(BinTree* root, BinTree* node1, BinTree* node2)
{
if (root == NULL || node1 == NULL || node2 == NULL)
return NULL;
if (node1 == root || node2 == root)
return root;
BinTree* cur = NULL;
BinTree* left_lca = GetLastCommonAncestor(root->lchild, node1, node2);
if (NULL != left_lca)
{
cur = GetLastCommonAncestor(left_lca->lchild, node1, node2);
if (cur == NULL)
cur = GetLastCommonAncestor(left_lca->rchild, node1, node2);
if ((cur == node1) && (left_lca == node2) || (cur == node2) && (left_lca == node1))
return root;
}
BinTree* right_lca = GetLastCommonAncestor(root->rchild, node1, node2);
if (NULL != right_lca)
{
cur = GetLastCommonAncestor(right_lca->rchild, node1, node2);
if (cur == NULL)
cur = GetLastCommonAncestor(right_lca->rchild, node1, node2);
if ((cur == node1) && (right_lca == node2) || (cur == node2) && (right_lca == node1))
return root;
}
if (left_lca && right_lca)
return root;
if (left_lca == NULL)
return right_lca;
else
return left_lca;
}
8.判斷一棵二叉樹是否是平衡二叉樹
//1
bool IsBalanceTree(BinTree* root)//(時間複雜度是N^2)
{
if (root == NULL)
return true;
int left = Depth(root->lchild);
int right = Depth(root->rchild);
return abs(left - right)<2
&& IsBalanceTree(root->lchild)
&& IsBalanceTree(root->rchild);
}
//2
bool IsBalance(BinTree* root, int& depth)//(時間複雜度是N)
{
if (root == NULL)
{
depth = 0;
return true;
}
int leftDepth = 0;
if (IsBalance(root->lchild, leftDepth) == false)
return false;
int rightDepth = 0;
if (IsBalance(root->rchild, rightDepth) == false)
return false;
depth = leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
return abs(leftDepth - rightDepth) < 2;
}
9.求二叉樹中最遠兩個節點的距離
int _FindMaxLen(BinTree* root, int& maxLen)
{
if (root == NULL)
return 0;
int left = _FindMaxLen(root->lchild, maxLen);
int right = _FindMaxLen(root->rchild, maxLen);
if (left + right > maxLen)
{
maxLen = left + right;
}
return left > right ? left + 1 : right + 1;
}
10. 由前序遍歷和中序遍歷重建二叉樹
BinTree* _RebuildBinaryTree(int* startPre, int* endPre, int* startIn, int* endIn)
{
//根據前序遍歷儲存根節點
BinTree *Root;
Root = new BinTree();
Root->data = startPre[0];
Root->lchild = NULL;
Root->rchild = NULL;
//判斷是否找完了此次中序遍歷,若是找完了,則返回Root;
if (startIn == endIn&&*startIn == *endIn)
return Root;
//在中序遍歷中找根節點所在的位置
int* RootIn = startIn;
while (*startPre != *RootIn)
RootIn++;
//根據根節點在中遍歷中的位置,遞迴還原左子樹
int leftlen = RootIn - startIn;
if (leftlen > 0)
{
Root->lchild = _RebuildBinaryTree(startPre + 1, startPre + leftlen, startIn, RootIn-1);
}
//左子樹長度加上中序的起始位置後若仍小於整個中序長度,則說明該節點右子樹存在,遞迴還原右子樹
if (leftlen + startIn < endIn)
{
Root->rchild = _RebuildBinaryTree(startPre + leftlen + 1, endPre, startIn + leftlen + 1, endIn);
}
return Root;
}
11. 判斷一棵樹是否是完全二叉樹
//->關鍵:找第一個度不為2的結點->後序結點:如果有孩子則不是完全二叉樹,否則是
bool IsCompleteBinaryTree(BinTree* root)
{
if (NULL == root) //
return false;
bool isOnlyLeft = false; //標記僅有左節點的結點
queue<BinTree*> q;
q.push(root);
while (!q.empty())
{
BinTree* pCur = q.front();
q.pop();
if (isOnlyLeft)
{
if (pCur->lchild || pCur->rchild)
return false;
}
else
{
if (NULL == pCur->lchild && NULL != pCur->rchild
) //存在右孩子沒有左孩子,一定不為完全二叉樹
return false;
else if (NULL != pCur->lchild && NULL == pCur->rchild) //存在左孩子沒有右孩子可能不是,記錄標記結點看後續結點
{
q.push(pCur->lchild);
isOnlyLeft = true; //只有左孩子是非滿結點
}
else if (NULL != pCur->lchild && NULL != pCur->rchild) // 左右孩子都存在,入佇列繼續迴圈判斷
{
q.push(pCur->lchild);
q.push(pCur->rchild);
}
else
isOnlyLeft = true; //左右孩子都存在,為非滿結點,看後續結點
}
}
return true;
}
12.求二叉樹的映象(即左右子樹交換)
BinTree * Mirror(BinTree * Root)
{
if (Root == NULL) // 返回NULL
return NULL;
BinTree * pLeft = Mirror(Root->lchild); // 求左子樹映象
BinTree * pRight = Mirror(Root->rchild); // 求右子樹映象
// 交換左子樹和右子樹
Root->lchild = pRight;
Root->rchild = pLeft;
return Root;
}
13.將二叉搜尋樹轉換成一個排序的雙向連結串列。要求不能建立任何新的結點,只能調整樹中結點指標的指向。
BinTree* Convert(BinTree* root)
{
if (root == NULL){//假如根節點為空,返回空
return NULL;
}
if (root->lchild == NULL&&root->rchild == NULL){//假如只有一個根節點,則返回根節點
return root;
}
//1、將左子樹構造成雙鏈表,並返回該連結串列頭結點left
BinTree* left = Convert(root->lchild);
//2、定位到左子樹連結串列的最後一個節點(左子樹最右邊的節點)
BinTree* p = left;//建立一個臨時節點P,用來遍歷找到左連結串列的最後一個節點(左子樹最右邊的節點),p初始化指向做左子樹的根節點,
while (p != NULL&&p->rchild != NULL)
{
p = p->lchild;//最終p為左子樹最右邊的節點
}
//3、如果左子樹連結串列不為空,將當前root追加到左子樹連結串列後
if (left != NULL){//左子樹連結串列不為空
p->rchild = root;//左子樹連結串列的最後一個節點p(左子樹最右邊節點)的右指標指向當前root節點
root->lchild = p;//當前root節點的左指標指向左子樹連結串列的最後一個節點p(左子樹最右邊節點)
}
//4、將右子樹構造成雙鏈表,並返回該連結串列的頭結點right
BinTree* right = Convert(root->rchild);
//5、如果右子樹連結串列不為空,將右子樹連結串列追加到當前root後
if (right != NULL)
{//右子樹連結串列不為空
right->lchild = root;//右子樹連結串列的頭結點right的左指標指向當前root
root->rchild = right;//當前root的右指標指向右子樹連結串列的頭結點right
}
return left != NULL ? left : root;//根據左子樹連結串列是否為空返回整個雙向連結串列的頭指標。
}