輕鬆搞定面試中的二叉樹題目
版權所有,轉載請註明出處,謝謝!
http://blog.csdn.net/walkinginthewind/article/details/7518888
樹是一種比較重要的資料結構,尤其是二叉樹。二叉樹是一種特殊的樹,在二叉樹中每個節點最多有兩個子節點,一般稱為左子節點和右子節點(或左孩子和右孩子),並且二叉樹的子樹有左右之分,其次序不能任意顛倒。二叉樹是遞迴定義的,因此,與二叉樹有關的題目基本都可以用遞迴思想解決,當然有些題目非遞迴解法也應該掌握,如非遞迴遍歷節點等等。本文努力對二叉樹相關題目做一個較全的整理總結,希望對找工作的同學有所幫助。
二叉樹節點定義如下:
struct BinaryTreeNode
{
int m_nValue;
BinaryTreeNode* m_pLeft;
BinaryTreeNode* m_pRight;
};
相關連結:
輕鬆搞定面試中的連結串列題目
題目列表:
1. 求二叉樹中的節點個數
2. 求二叉樹的深度
3. 前序遍歷,中序遍歷,後序遍歷
4.分層遍歷二叉樹(按層次從上往下,從左往右)
5. 將二叉查詢樹變為有序的雙向連結串列
6. 求二叉樹第K層的節點個數
7. 求二叉樹中葉子節點的個數
8. 判斷兩棵二叉樹是否結構相同
9. 判斷二叉樹是不是平衡二叉樹
10. 求二叉樹的映象
11. 求二叉樹中兩個節點的最低公共祖先節點
12. 求二叉樹中節點的最大距離
13. 由前序遍歷序列和中序遍歷序列重建二叉樹
14.判斷二叉樹是不是完全二叉樹
詳細解答
1. 求二叉樹中的節點個數
遞迴解法:
(1)如果二叉樹為空,節點個數為0
(2)如果二叉樹不為空,二叉樹節點個數 = 左子樹節點個數 + 右子樹節點個數 + 1
參考程式碼如下:
- int GetNodeNum(BinaryTreeNode * pRoot)
- {
- if(pRoot == NULL) // 遞迴出口
- return 0;
- return GetNodeNum(pRoot->m_pLeft) + GetNodeNum(pRoot->m_pRight) + 1;
- }
遞迴解法:
(1)如果二叉樹為空,二叉樹的深度為0
(2)如果二叉樹不為空,二叉樹的深度 = max(左子樹深度, 右子樹深度) + 1
參考程式碼如下:
- int GetDepth(BinaryTreeNode * pRoot)
- {
- if(pRoot == NULL) // 遞迴出口
- return 0;
- int depthLeft = GetDepth(pRoot->m_pLeft);
- int depthRight = GetDepth(pRoot->m_pRight);
- return depthLeft > depthRight ? (depthLeft + 1) : (depthRight + 1);
- }
前序遍歷遞迴解法:
(1)如果二叉樹為空,空操作
(2)如果二叉樹不為空,訪問根節點,前序遍歷左子樹,前序遍歷右子樹
參考程式碼如下:
- void PreOrderTraverse(BinaryTreeNode * pRoot)
- {
- if(pRoot == NULL)
- return;
- Visit(pRoot); // 訪問根節點
- PreOrderTraverse(pRoot->m_pLeft); // 前序遍歷左子樹
- PreOrderTraverse(pRoot->m_pRight); // 前序遍歷右子樹
- }
(1)如果二叉樹為空,空操作。
(2)如果二叉樹不為空,中序遍歷左子樹,訪問根節點,中序遍歷右子樹
參考程式碼如下:
- void InOrderTraverse(BinaryTreeNode * pRoot)
- {
- if(pRoot == NULL)
- return;
- InOrderTraverse(pRoot->m_pLeft); // 中序遍歷左子樹
- Visit(pRoot); // 訪問根節點
- InOrderTraverse(pRoot->m_pRight); // 中序遍歷右子樹
- }
(1)如果二叉樹為空,空操作
(2)如果二叉樹不為空,後序遍歷左子樹,後序遍歷右子樹,訪問根節點
參考程式碼如下:
- void PostOrderTraverse(BinaryTreeNode * pRoot)
- {
- if(pRoot == NULL)
- return;
- PostOrderTraverse(pRoot->m_pLeft); // 後序遍歷左子樹
- PostOrderTraverse(pRoot->m_pRight); // 後序遍歷右子樹
- Visit(pRoot); // 訪問根節點
- }
相當於廣度優先搜尋,使用佇列實現。佇列初始化,將根節點壓入佇列。當佇列不為空,進行如下操作:彈出一個節點,訪問,若左子節點或右子節點不為空,將其壓入佇列。
- void LevelTraverse(BinaryTreeNode * pRoot)
- {
- if(pRoot == NULL)
- return;
- queue<BinaryTreeNode *> q;
- q.push(pRoot);
- while(!q.empty())
- {
- BinaryTreeNode * pNode = q.front();
- q.pop();
- Visit(pNode); // 訪問節點
- if(pNode->m_pLeft != NULL)
- q.push(pNode->m_pLeft);
- if(pNode->m_pRight != NULL)
- q.push(pNode->m_pRight);
- }
- return;
- }
遞迴解法:
(1)如果二叉樹查詢樹為空,不需要轉換,對應雙向連結串列的第一個節點是NULL,最後一個節點是NULL
(2)如果二叉查詢樹不為空:
如果左子樹為空,對應雙向有序連結串列的第一個節點是根節點,左邊不需要其他操作;
如果左子樹不為空,轉換左子樹,二叉查詢樹對應雙向有序連結串列的第一個節點就是左子樹轉換後雙向有序連結串列的第一個節點,同時將根節點和左子樹轉換後的雙向有序鏈 表的最後一個節點連線;
如果右子樹為空,對應雙向有序連結串列的最後一個節點是根節點,右邊不需要其他操作;
如果右子樹不為空,對應雙向有序連結串列的最後一個節點就是右子樹轉換後雙向有序連結串列的最後一個節點,同時將根節點和右子樹轉換後的雙向有序連結串列的第一個節點連 接。參考程式碼如下:
- /******************************************************************************
- 引數:
- pRoot: 二叉查詢樹根節點指標
- pFirstNode: 轉換後雙向有序連結串列的第一個節點指標
- pLastNode: 轉換後雙向有序連結串列的最後一個節點指標
- ******************************************************************************/
- void Convert(BinaryTreeNode * pRoot,
- BinaryTreeNode * & pFirstNode, BinaryTreeNode * & pLastNode)
- {
- BinaryTreeNode *pFirstLeft, *pLastLeft, * pFirstRight, *pLastRight;
- if(pRoot == NULL)
- {
- pFirstNode = NULL;
- pLastNode = NULL;
- return;
- }
- if(pRoot->m_pLeft == NULL)
- {
- // 如果左子樹為空,對應雙向有序連結串列的第一個節點是根節點
- pFirstNode = pRoot;
- }
- else
- {
- Convert(pRoot->m_pLeft, pFirstLeft, pLastLeft);
- // 二叉查詢樹對應雙向有序連結串列的第一個節點就是左子樹轉換後雙向有序連結串列的第一個節點
- pFirstNode = pFirstLeft;
- // 將根節點和左子樹轉換後的雙向有序連結串列的最後一個節點連線
- pRoot->m_pLeft = pLastLeft;
- pLastLeft->m_pRight = pRoot;
- }
- if(pRoot->m_pRight == NULL)
- {
- // 對應雙向有序連結串列的最後一個節點是根節點
- pLastNode = pRoot;
- }
- else
- {
- Convert(pRoot->m_pRight, pFirstRight, pLastRight);
- // 對應雙向有序連結串列的最後一個節點就是右子樹轉換後雙向有序連結串列的最後一個節點
- pLastNode = pLastRight;
- // 將根節點和右子樹轉換後的雙向有序連結串列的第一個節點連線
- pRoot->m_pRight = pFirstRight;
- pFirstRight->m_pLeft = pRoot;
- }
- return;
- }
遞迴解法:
(1)如果二叉樹為空或者k<1返回0
(2)如果二叉樹不為空並且k==1,返回1
(3)如果二叉樹不為空且k>1,返回左子樹中k-1層的節點個數與右子樹k-1層節點個數之和
參考程式碼如下:
- int GetNodeNumKthLevel(BinaryTreeNode * pRoot, int k)
- {
- if(pRoot == NULL || k < 1)
- return 0;
- if(k == 1)
- return 1;
- int numLeft = GetNodeNumKthLevel(pRoot->m_pLeft, k-1); // 左子樹中k-1層的節點個數
- int numRight = GetNodeNumKthLevel(pRoot->m_pRight, k-1); // 右子樹中k-1層的節點個數
- return (numLeft + numRight);
- }
遞迴解法:
(1)如果二叉樹為空,返回0
(2)如果二叉樹不為空且左右子樹為空,返回1
(3)如果二叉樹不為空,且左右子樹不同時為空,返回左子樹中葉子節點個數加上右子樹中葉子節點個數
參考程式碼如下:
- int GetLeafNodeNum(BinaryTreeNode * pRoot)
- {
- if(pRoot == NULL)
- return 0;
- if(pRoot->m_pLeft == NULL && pRoot->m_pRight == NULL)
- return 1;
- int numLeft = GetLeafNodeNum(pRoot->m_pLeft); // 左子樹中葉節點的個數
- int numRight = GetLeafNodeNum(pRoot->m_pRight); // 右子樹中葉節點的個數
- return (numLeft + numRight);
- }
不考慮資料內容。結構相同意味著對應的左子樹和對應的右子樹都結構相同。
遞迴解法:
(1)如果兩棵二叉樹都為空,返回真
(2)如果兩棵二叉樹一棵為空,另一棵不為空,返回假
(3)如果兩棵二叉樹都不為空,如果對應的左子樹和右子樹都同構返回真,其他返回假
參考程式碼如下:
- bool StructureCmp(BinaryTreeNode * pRoot1, BinaryTreeNode * pRoot2)
- {
- if(pRoot1 == NULL && pRoot2 == NULL) // 都為空,返回真
- return true;
- else if(pRoot1 == NULL || pRoot2 == NULL) // 有一個為空,一個不為空,返回假
- return false;
- bool resultLeft = StructureCmp(pRoot1->m_pLeft, pRoot2->m_pLeft); // 比較對應左子樹
- bool resultRight = StructureCmp(pRoot1->m_pRight, pRoot2->m_pRight); // 比較對應右子樹
- return (resultLeft && resultRight);
- }
遞迴解法:
(1)如果二叉樹為空,返回真
(2)如果二叉樹不為空,如果左子樹和右子樹都是AVL樹並且左子樹和右子樹高度相差不大於1,返回真,其他返回假
參考程式碼:
- bool IsAVL(BinaryTreeNode * pRoot, int & height)
- {
- if(pRoot == NULL) // 空樹,返回真
- {
- height = 0;
- return true;
- }
- int heightLeft;
- bool resultLeft = IsAVL(pRoot->m_pLeft, heightLeft);
- int heightRight;
- bool resultRight = IsAVL(pRoot->m_pRight, heightRight);
- if(resultLeft && resultRight && abs(heightLeft - heightRight) <= 1) // 左子樹和右子樹都是AVL,並且高度相差不大於1,返回真
- {
- height = max(heightLeft, heightRight) + 1;
- return true;
- }
- else
- {
- height = max(heightLeft, heightRight) + 1;
- return false;
- }
- }
遞迴解法:
(1)如果二叉樹為空,返回空
(2)如果二叉樹不為空,求左子樹和右子樹的映象,然後交換左子樹和右子樹
參考程式碼如下:
- BinaryTreeNode * Mirror(BinaryTreeNode * pRoot)
- {
- if(pRoot == NULL) // 返回NULL
- return NULL;
- BinaryTreeNode * pLeft = Mirror(pRoot->m_pLeft); // 求左子樹映象
- BinaryTreeNode * pRight = Mirror(pRoot->m_pRight); // 求右子樹映象
- // 交換左子樹和右子樹
- pRoot->m_pLeft = pRight;
- pRoot->m_pRight = pLeft;
- return pRoot;
- }
遞迴解法:
(1)如果兩個節點分別在根節點的左子樹和右子樹,則返回根節點
(2)如果兩個節點都在左子樹,則遞迴處理左子樹;如果兩個節點都在右子樹,則遞迴處理右子樹
參考程式碼如下:
- bool FindNode(BinaryTreeNode * pRoot, BinaryTreeNode * pNode)
- {
- if(pRoot == NULL || pNode == NULL)
- return false;
- if(pRoot == pNode)
- return true;
- bool found = FindNode(pRoot->m_pLeft, pNode);
- if(!found)
- found = FindNode(pRoot->m_pRight, pNode);
- return found;
- }
- BinaryTreeNode * GetLastCommonParent(BinaryTreeNode * pRoot,
- BinaryTreeNode * pNode1,
- BinaryTreeNode * pNode2)
- {
- if(FindNode(pRoot->m_pLeft, pNode1))
- {
- if(FindNode(pRoot->m_pRight, pNode2))
- return pRoot;
- else
- return GetLastCommonParent(pRoot->m_pLeft, pNode1, pNode2);
- }
- else
- {
- if(FindNode(pRoot->m_pLeft, pNode2))
- return pRoot;
- else
- return GetLastCommonParent(pRoot->m_pRight, pNode1, pNode2);
- }
- }
非遞迴解法:
先求從根節點到兩個節點的路徑,然後再比較對應路徑的節點就行,最後一個相同的節點也就是他們在二叉樹中的最低公共祖先節點
參考程式碼如下:
- bool GetNodePath(BinaryTreeNode * pRoot, BinaryTreeNode * pNode,
- list<BinaryTreeNode *> & path)
- {
- if(pRoot == pNode)
- {
- path.push_back(pRoot);
- return true;
- }
- if(pRoot == NULL)
- return false;
- path.push_back(pRoot);
- bool found = false;
- found = GetNodePath(pRoot->m_pLeft, pNode, path);
- if(!found)
- found = GetNodePath(pRoot->m_pRight, pNode, path);
- if(!found)
- path.pop_back();
- return found;
- }
- BinaryTreeNode * GetLastCommonParent(BinaryTreeNode * pRoot, BinaryTreeNode * pNode1, BinaryTreeNode * pNode2)
- {
- if(pRoot == NULL || pNode1 == NULL || pNode2 == NULL)
- return NULL;
- list<BinaryTreeNode*> path1;
- bool bResult1 = GetNodePath(pRoot, pNode1, path1);
- list<BinaryTreeNode*> path2;
- bool bResult2 = GetNodePath(pRoot, pNode2, path2);
- if(!bResult1 || !bResult2)
- return NULL;
- BinaryTreeNode * pLast = NULL;
- list<BinaryTreeNode*>::const_iterator iter1 = path1.begin();
- list<BinaryTreeNode*>::const_iterator iter2 = path2.begin();
- while(iter1 != path1.end() && iter2 != path2.end())
- {
- if(*iter1 == *iter2)
- pLast = *iter1;
- else
- break;
- iter1++;
- iter2++;
- }
- return pLast;
- }
在上述演算法的基礎上稍加變化即可求二叉樹中任意兩個節點的距離了。
12. 求二叉樹中節點的最大距離
即二叉樹中相距最遠的兩個節點之間的距離。
遞迴解法:
(1)如果二叉樹為空,返回0,同時記錄左子樹和右子樹的深度,都為0
(2)如果二叉樹不為空,最大距離要麼是左子樹中的最大距離,要麼是右子樹中的最大距離,要麼是左子樹節點中到根節點的最大距離+右子樹節點中到根節點的最大距離,同時記錄左子樹和右子樹節點中到根節點的最大距離。
參考程式碼如下:
- int GetMaxDistance(BinaryTreeNode * pRoot, int & maxLeft, int & maxRight)
- {
- // maxLeft, 左子樹中的節點距離根節點的最遠距離
- // maxRight, 右子樹中的節點距離根節點的最遠距離
- if(pRoot == NULL)
- {
- maxLeft = 0;
- maxRight = 0;
- return 0;
- }
- int maxLL, maxLR, maxRL, maxRR;
- int maxDistLeft, maxDistRight;
- if(pRoot->m_pLeft != NULL)
- {
- maxDistLeft = GetMaxDistance(pRoot->m_pLeft, maxLL, maxLR);
- maxLeft = max(maxLL, maxLR) + 1;
- }
- else
- {
- maxDistLeft = 0;
- maxLeft = 0;
- }
- if(pRoot->m_pRight != NULL)
- {
- maxDistRight = GetMaxDistance(pRoot->m_pRight, maxRL, maxRR);
- maxRight = max(maxRL, maxRR) + 1;
- }
- else
- {
- maxDistRight = 0;
- maxRight = 0;
- }
- return max(max(maxDistLeft, maxDistRight), maxLeft+maxRight);
- }
二叉樹前序遍歷序列中,第一個元素總是樹的根節點的值。中序遍歷序列中,左子樹的節點的值位於根節點的值的左邊,右子樹的節點的值位
於根節點的值的右邊。
遞迴解法:
(1)如果前序遍歷為空或中序遍歷為空或節點個數小於等於0,返回NULL。
(2)建立根節點。前序遍歷的第一個資料就是根節點的資料,在中序遍歷中找到根節點的位置,可分別得知左子樹和右子樹的前序和中序遍
歷序列,重建左右子樹。
- BinaryTreeNode * RebuildBinaryTree(int* pPreOrder, int* pInOrder, int nodeNum)
- {
- if(pPreOrder == NULL || pInOrder == NULL || nodeNum <= 0)
- return NULL;
- BinaryTreeNode * pRoot = new BinaryTreeNode;
- // 前序遍歷的第一個資料就是根節點資料
- pRoot->m_nValue = pPreOrder[0];
- pRoot->m_pLeft = NULL;
- pRoot->m_pRight = NULL;
- // 查詢根節點在中序遍歷中的位置,中序遍歷中,根節點左邊為左子樹,右邊為右子樹
- int rootPositionInOrder = -1;
- for(int i = 0; i < nodeNum; i++)
- if(pInOrder[i] == pRoot->m_nValue)
- {
- rootPositionInOrder = i;
- break;
- }
- if(rootPositionInOrder == -1)
- {
- throw std::exception("Invalid input.");
- }
- // 重建左子樹
- int nodeNumLeft = rootPositionInOrder;
- int * pPreOrderLeft = pPreOrder + 1;
- int * pInOrderLeft = pInOrder;
- pRoot->m_pLeft = RebuildBinaryTree(pPreOrderLeft, pInOrderLeft, nodeNumLeft);
- // 重建右子樹
- int nodeNumRight = nodeNum - nodeNumLeft - 1;
- int * pPreOrderRight = pPreOrder + 1 + nodeNumLeft;
- int * pInOrderRight = pInOrder + nodeNumLeft + 1;
- pRoot->m_pRight = RebuildBinaryTree(pPreOrderRight, pInOrderRight, nodeNumRight);
- return pRoot;
- }
14.判斷二叉樹是不是完全二叉樹
若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全
二叉樹。
有如下演算法,按層次(從上到下,從左到右)遍歷二叉樹,當遇到一個節點的左子樹為空時,則該節點右子樹必須為空,且後面遍歷的節點左
右子樹都必須為空,否則不是完全二叉樹。
- bool IsCompleteBinaryTree(BinaryTreeNode * pRoot)
- {
- if(pRoot == NULL)
- return false;
- queue<BinaryTreeNode *> q;
- q.push(pRoot);
- bool mustHaveNoChild = false;
- bool result = true;
- while(!q.empty())
- {
- BinaryTreeNode * pNode = q.front();
- q.pop();
- if(mustHaveNoChild) // 已經出現了有空子樹的節點了,後面出現的必須為葉節點(左右子樹都為空)
- {
- if(pNode->m_pLeft != NULL || pNode->m_pRight != NULL)
- {
- result = false;
- break;
- }
- }
- else
- {
- if(pNode->m_pLeft != NULL && pNode->m_pRight != NULL)
- {
- q.push(pNode->m_pLeft);
- q.push(pNode->m_pRight);
- }
- else if(pNode->m_pLeft != NULL && pNode->m_pRight == NULL)
- {
- mustHaveNoChild = true;
- q.push(pNode->m_pLeft);
- }
- else if(pNode->m_pLeft == NULL && pNode->m_pRight != NULL)
- {
- result = false;
- break;
- }
- else
- {
- mustHaveNoChild = true;
- }
- }
- }
- return result;
- }
相關文章
- 輕鬆搞定面試中的連結串列題目面試
- 一篇文章搞定面試中的二叉樹題目(java實現)面試二叉樹Java
- [演算法總結] 20 道題搞定 BAT 面試——二叉樹演算法BAT面試二叉樹
- 輕鬆讓你徹底搞定c++版 線索二叉樹的線索化演算法C++二叉樹演算法
- 辦公室革命,教你輕鬆搞定輕鬆玩轉ExcelExcel
- 二叉樹相關題目二叉樹
- 輕鬆搞定分組報表中的各種排序排序
- 面試題7:重建二叉樹面試題二叉樹
- 部分二叉樹題目彙總二叉樹
- Leetcode 二叉樹題目集合 (看完這個面試不會做二叉樹題,辣條給你!!!!!)LeetCode二叉樹面試
- MongoDB系列--輕鬆應對面試中遇到的MongonDB索引(index)問題MongoDB面試索引Index
- 面試題37:序列化二叉樹面試題二叉樹
- 輕鬆搞定專案流程自動化
- 【谷歌面試題】有序輸出兩棵二叉查詢樹中的元素谷歌面試題
- 一點也不復雜!Nginx 可以輕鬆搞定跨域問題Nginx跨域
- 3D 穿梭效果?使用 CSS 輕鬆搞定3DCSS
- Android輕鬆搞定Dialog提示動畫效果Android動畫
- 12步輕鬆搞定python裝飾器Python
- 【動畫】看動畫輕鬆理解「Trie樹」動畫
- 一篇搞定面試中的跨域問題面試跨域
- 一文搞定二叉排序(搜尋)樹排序
- 輕鬆搞定專案管理軟體、分配任務專案管理
- 一份配置,輕鬆搞定Vue表單渲染Vue
- 上億條資料,OurwayBI也能輕鬆搞定
- MySQL8.0輕鬆搞定GTID組複製MySql
- Jenkins部署安裝,輕鬆三步搞定Jenkins
- 輕鬆搞定實時分析及監控大屏
- 輕鬆搞定Windows惡意程式碼攻擊(轉)Windows
- 一文輕鬆搞定 tarjan 演算法(二)演算法
- 【java 資料結構】還不會二叉樹?一篇搞定二叉樹Java資料結構二叉樹
- 資料結構中的樹(二叉樹、二叉搜尋樹、AVL樹)資料結構二叉樹
- 100行程式碼,輕鬆搞定文字編輯器中草稿箱行程
- 幾道和「二叉樹」有關的演算法面試題二叉樹演算法面試題
- 面試題34:二叉樹中和為某一值的路徑面試題二叉樹
- 一道關於二叉樹的位元組面試題的思考二叉樹面試題
- 資料結構和演算法面試題系列—二叉樹面試題彙總資料結構演算法面試題二叉樹
- 如何輕鬆玩轉樹莓派系統?樹莓派
- 只需一分鐘輕鬆搞定Win7桌面假死問題Win7