亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入二叉樹兩個結(jié)點的最低共同父結(jié)點的詳解

 更新時間:2013年05月23日 18:21:23   作者:  
本篇文章是對二叉樹兩個結(jié)點的最低共同父結(jié)點進行了詳細的分析介紹,需要的朋友參考下
題目:二叉樹的結(jié)點定義如下:
復制代碼 代碼如下:

struct TreeNode
   {
              int m_nvalue;
             TreeNode* m_pLeft;
             TreeNode* m_pRight;
};

輸入二叉樹中的兩個結(jié)點,輸出這兩個結(jié)點在數(shù)中最低的共同父結(jié)點。
分析:求數(shù)中兩個結(jié)點的最低共同結(jié)點是面試中經(jīng)常出現(xiàn)的一個問題。這個問題至少有兩個變種。
第一變種是二叉樹是一種特殊的二叉樹:查找二叉樹。也就是樹是排序過的,位于左子樹上的結(jié)點都比父結(jié)點小,而位于右子樹的結(jié)點都比父結(jié)點大。我們只需要從根結(jié)點開始和兩個結(jié)點進行比較。如果當前結(jié)點的值比兩個結(jié)點都大,則最低的共同父結(jié)點一定在當前結(jié)點的左子樹中。如果當前結(jié)點的值比兩個結(jié)點都小,則最低的共同父結(jié)點一定在當前結(jié)點的右子樹中。
第二個變種是樹不一定是二叉樹,每個結(jié)點都有一個指針指向它的父結(jié)點。于是我們可以從任何一個結(jié)點出發(fā),得到一個到達樹根結(jié)點的單向鏈表。因此這個問題轉(zhuǎn)換為求兩個單向鏈表的第一個公共結(jié)點。
現(xiàn)在我們回到這個問題本身。所謂共同的父結(jié)點,就是兩個結(jié)點都出現(xiàn)在這個結(jié)點的子樹中。因此我們可以定義一函數(shù),來判斷一個結(jié)點的子樹中是不是包含了另外一個結(jié)點。這不是件很難的事,我們可以用遞歸的方法來實現(xiàn):
復制代碼 代碼如下:

/*
// If the tree with head pHead has a node pNode, return true.
// Otherwise return false.
*/
bool HasNode(TreeNode* pHead, TreeNode* pNode)
{
 if(pHead == pNode)
  return true;
 bool has = false;
 if(pHead->m_pLeft != NULL)
  has = HasNode(pHead->m_pLeft, pNode);
 if(!has && pHead->m_pRight != NULL)
  has = HasNode(pHead->m_pRight, pNode);
 return has;
}

我們可以從根結(jié)點開始,判斷以當前結(jié)點為根的樹中左右子樹是不是包含我們要找的兩個結(jié)點。如果兩個結(jié)點都出現(xiàn)在它的左子樹中,那最低的共同父結(jié)點也出現(xiàn)在它的左子樹中。如果兩個結(jié)點都出現(xiàn)在它的右子樹中,那最低的共同父結(jié)點也出現(xiàn)在它的右子樹中。如果兩個結(jié)點一個出現(xiàn)在左子樹中,一個出現(xiàn)在右子樹中,那當前的結(jié)點就是最低的共同父結(jié)點?;谶@個思路,我們可以寫出如下代碼:
復制代碼 代碼如下:

/*
// Find the last parent of pNode1 and pNode2 in a tree with head pHead
*/
TreeNode* LastCommonParent_1(TreeNode* pHead, TreeNode* pNode1, TreeNode* pNode2)
{
 if(pHead == NULL || pNode1 == NULL || pNode2 == NULL)
  return NULL;
 // check whether left child has pNode1 and pNode2
 bool leftHasNode1 = false;
 bool leftHasNode2 = false;
 if(pHead->m_pLeft != NULL)
 {
  leftHasNode1 = HasNode(pHead->m_pLeft, pNode1);
  leftHasNode2 = HasNode(pHead->m_pLeft, pNode2);
 }
 if(leftHasNode1 && leftHasNode2)
 {
  if(pHead->m_pLeft == pNode1 || pHead->m_pLeft == pNode2)
   return pHead;
  return LastCommonParent_1(pHead->m_pLeft, pNode1, pNode2);
 }
 // check whether right child has pNode1 and pNode2
 bool rightHasNode1 = false;
 bool rightHasNode2 = false;
 if(pHead->m_pRight != NULL)
 {
  if(!leftHasNode1)
   rightHasNode1 = HasNode(pHead->m_pRight, pNode1);
  if(!leftHasNode2)
   rightHasNode2 = HasNode(pHead->m_pRight, pNode2);
 }
 if(rightHasNode1 && rightHasNode2)
 {
  if(pHead->m_pRight == pNode1 || pHead->m_pRight == pNode2)
   return pHead;
  return LastCommonParent_1(pHead->m_pRight, pNode1, pNode2);
 }
 if((leftHasNode1 && rightHasNode2) || (leftHasNode2 && rightHasNode1))
  return pHead;
 return NULL;
}

接著我們來分析一下這個方法的效率。函數(shù)HasNode的本質(zhì)就是遍歷一棵樹,其時間復雜度是O(n)(n是樹中結(jié)點的數(shù)目)。由于我們根結(jié)點開始,要對每個結(jié)點調(diào)用函數(shù)HasNode。因此總的時間復雜度是O(n^2)。
我們仔細分析上述代碼,不難發(fā)現(xiàn)我們判斷以一個結(jié)點為根的樹是否含有某個結(jié)點時,需要遍歷樹的每個結(jié)點。接下來我們判斷左子結(jié)點或者右結(jié)點為根的樹中是否含有要找結(jié)點,仍然需要遍歷。第二次遍歷的操作其實在前面的第一次遍歷都做過了。由于存在重復的遍歷,本方法在時間效率上肯定不是最好的。
前面我們提過如果結(jié)點中有一個指向父結(jié)點的指針,我們可以把問題轉(zhuǎn)化為求兩個鏈表的共同結(jié)點?,F(xiàn)在我們可以想辦法得到這個鏈表。我們在這里稍作變化即可:
復制代碼 代碼如下:

/*
// Get the path form pHead and pNode in a tree with head pHead
*/
bool GetNodePath(TreeNode* pHead, TreeNode* pNode, std::list<TreeNode*>& path)
{
 if(pHead == pNode)
  return true;
 path.push_back(pHead);
 bool found = false;
 if(pHead->m_pLeft != NULL)
  found = GetNodePath(pHead->m_pLeft, pNode, path);
 if(!found && pHead->m_pRight)
  found = GetNodePath(pHead->m_pRight, pNode, path);
 if(!found)
  path.pop_back();
 return found;
}

 由于這個路徑是從跟結(jié)點開始的。最低的共同父結(jié)點就是路徑中的最后一個共同結(jié)點:
復制代碼 代碼如下:

/*
// Get the last common Node in two lists: path1 and path2
*/
TreeNode* LastCommonNode
(
 const std::list<TreeNode*>& path1,
 const std::list<TreeNode*>& path2
 )
{
 std::list<TreeNode*>::const_iterator iterator1 = path1.begin();
 std::list<TreeNode*>::const_iterator iterator2 = path2.begin();  
 TreeNode* pLast = NULL;
 while(iterator1 != path1.end() && iterator2 != path2.end())
 {
  if(*iterator1 == *iterator2)
   pLast = *iterator1;
  iterator1++;
  iterator2++;
 }
 return pLast;
}

有了前面兩個子函數(shù)之后,求兩個結(jié)點的最低共同父結(jié)點就很容易了。我們先求出從根結(jié)點出發(fā)到兩個結(jié)點的兩條路徑,再求出兩條路徑的最后一個共同結(jié)點。代碼如下:
復制代碼 代碼如下:

/*
// Find the last parent of pNode1 and pNode2 in a tree with head pHead
*/
TreeNode* LastCommonParent_2(TreeNode* pHead, TreeNode* pNode1, TreeNode* pNode2)
{
 if(pHead == NULL || pNode1 == NULL || pNode2 == NULL)
  return NULL;
 std::list<TreeNode*> path1;
 GetNodePath(pHead, pNode1, path1);
 std::list<TreeNode*> path2;
 GetNodePath(pHead, pNode2, path2);
 return LastCommonNode(path1, path2);
}

這種思路的時間復雜度是O(n),時間效率要比第一種方法好很多。但同時我們也要注意到,這種思路需要兩個鏈表來保存路徑,空間效率比不上第一個方法。

相關(guān)文章

  • C++實現(xiàn)多源最短路徑之Floyd算法示例

    C++實現(xiàn)多源最短路徑之Floyd算法示例

    這篇文章主要介紹了C++實現(xiàn)多源最短路徑之Floyd算法,結(jié)合實例形式分析了多源最短路徑之Floyd算法的原理、實現(xiàn)方法及核心代碼,需要的朋友可以參考下
    2017-08-08
  • c語言/c++溢出問題淺談

    c語言/c++溢出問題淺談

    這篇文章主要給大家介紹了關(guān)于c語言/c++溢出問題的相關(guān)資料,文中通過圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-02-02
  • 一盤王者的時間用C語言實現(xiàn)三子棋

    一盤王者的時間用C語言實現(xiàn)三子棋

    相信我們都玩過三子棋,規(guī)則很簡單,但想用c語言做出這個游戲,事實上也是比較簡單的,下面通過c語言進行對五子棋的分析
    2022-02-02
  • QT使用QComBox和QLineEdit實現(xiàn)模糊查詢功能

    QT使用QComBox和QLineEdit實現(xiàn)模糊查詢功能

    模糊查詢是指根據(jù)用戶輸入的文本,在下拉框的選項中進行模糊匹配,并動態(tài)地顯示匹配的選項,本文將使用QComBox和QLineEdit實現(xiàn)模糊查詢功能,需要的可以參考下
    2023-11-11
  • C語言實現(xiàn)井字棋游戲

    C語言實現(xiàn)井字棋游戲

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)井字棋游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-04-04
  • epoll多路復用的一個實例程序(C實現(xiàn))

    epoll多路復用的一個實例程序(C實現(xiàn))

    這篇文章主要為大家詳細介紹了epoll多路復用的一個實例程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • C語言選擇排序算法及實例代碼

    C語言選擇排序算法及實例代碼

    本篇文章主要介紹了 C語言選擇排序算法,這里提供代碼實例以便大家理解,通過本文,更好的理解排序算法
    2016-07-07
  • C++中命名空間(namespace)詳解及其作用介紹

    C++中命名空間(namespace)詳解及其作用介紹

    考慮一種情況,當我們有兩個同名的人,Zara,在同一個班里。當我們需要對它們進行區(qū)分我們必須使用一些額外的信息和它們的名字,比如它們生活在不同的區(qū)域或者興趣愛好什么的,在C++程序中也會遇到同樣的情況,所以命名空間就此產(chǎn)生
    2022-08-08
  • C++圖形界面開發(fā)Qt教程:嵌套圓環(huán)示例

    C++圖形界面開發(fā)Qt教程:嵌套圓環(huán)示例

    這篇文章主要介紹了C++實現(xiàn)圖形界面開發(fā)Qt教程,涉及坐標函數(shù)的應用及圖形界面程序設(shè)計,需要的朋友可以參考下,希望能給你帶來幫助
    2021-08-08
  • C語言實現(xiàn)linux網(wǎng)卡檢測精簡版

    C語言實現(xiàn)linux網(wǎng)卡檢測精簡版

    這篇文章主要為大家詳細介紹了C語言實現(xiàn)linux網(wǎng)卡檢測的精簡版,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06

最新評論