天天看點

二叉樹面試題

求二叉樹的高度

判斷一棵樹是否為平衡二叉樹

求二叉樹葉子節點的個數

求二叉樹第k層節點的個數

判斷某個節點是否存在于二叉樹中

求二叉樹的鏡像

求二叉樹中最遠的兩個節點之間的距離

由前序周遊和中序周遊重建二叉樹

二叉樹的高度

題目:輸入一棵二叉樹的根結點,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度為樹的深度。

思路: 1、如果樹為空,傳回0

2、若果二叉樹隻有一個節點,傳回1

3、如果二叉樹中有左子樹,傳回左子樹的高度+1

4、如果二叉樹中有右子樹,傳回右子樹的高度+1

5、如果左右子樹都存在,則傳回高度較大的子樹值+1

代碼實作如下(遞歸思想):

樹的節點結構:

struct BTNode
{
    BTNode(const T& data=T())//定義結構體  并初始化
    :_data(data)
    , _left(NULL)
    , _right(NULL)
    {}
    BTNode* _left;
    BTNode* _right;
    T _data;
};

           
size_t _Depth(Node* _root)//  左右子樹高的+1依次遞歸
    {
        if (_root == NULL)
        {
            return 0;
        }
        if (_root)
        {
        //int left=_Depth(_root->_left)
        //int right=_Depth(_root->_right) 
        //return left>right?left+1:right+1
        return (_Depth(_root->_left) > _Depth(_root->_right) 
                ? _Depth(_root->_left) : _Depth(_root->_right)) + 1;
        }
    }
           

判斷一棵樹是否為平衡二叉樹

題目: 輸入一棵二叉樹的根結點,判斷該樹是不是平衡二叉樹。如果某二叉樹中任意結點的左右子樹的深度相差不超過1,那麼它就是一棵平衡二叉樹。例如,下圖中的二叉樹就是一棵平衡二叉樹。

思路:

1、由于在上一個題中已經可以得到了樹的高度,是以隻需要求得左右子樹的高度差就可以了

2、兩顆子樹也要平衡

bool Isbalance(Node* _root)
    {
        if (_root == NULL)
            return NULL;
        int left_height = _Depth(_root->_left);
        int right_height = _Depth(_root->_right);
        int sub_height = abs(left_height - right_height);
        if (sub_height > 1)
        return false;
        //判斷兩顆子樹是否平衡
        return Isbalance(_root->_left) && Isbalance(_root->_right);
    }
           

但是,上面的“先序周遊”判斷二叉樹平衡的方法,時間複雜度比較大。因為,二叉樹中的很多結點周遊了多次。

比如,求解根的的高度時,需要先求解根的左子樹高度和右子樹高度,這就周遊了整棵左子樹中的結點和右子樹中的結點。而求解以根的左孩子為根的子樹的高度時,又需要周遊它(根的左孩子)的左子樹和右子樹。這樣,相當于很多結點的高度重複計算了。

根本原因是采用了“先序周遊”,求根的高度,需要先知道根的左右孩子的高度。

如果采用後序周遊,先知道某結點左右子樹的高度,如果左右子樹的高度都不滿足平衡二叉樹(二者高度相減大于1),那麼都不需要再去求解該結點的高度了。

因為,平衡二叉樹要求二叉樹中任意結點的左右子樹高度相差不超過1

代碼實作如下:

bool isBalance(TreeNode *root, int &height)
    {
        if (root == NULL)
        {
            height = 0;
            return true;
        }

        int leftHeight = 0, rightHeight = 0;
        bool leftRet = isBalance3(root->left, leftHeight);
        bool rightRet = isBalance3(root->right, rightHeight);

        height = max(leftHeight, rightHeight) + 1;
        if (abs(leftHeight - rightHeight) > 1)
            return false;
        return leftRet && rightRet;
    }
};
           

二叉樹葉子節點的個數

題目:輸入一棵二叉樹的根結點,求該樹的葉子節點的個數。

思路: 1、當樹為空時,傳回0;

2、當樹隻有根節點時,傳回1

3、當樹的左右子樹都為空時,樹的葉子節點為左右子樹的葉子節點之和

代碼實作如下:

size_t _LeafSize( const Node* _root)//傳回二叉樹中葉子節點的個數 三種情況都要分别考慮到
    {
        if (_root == NULL)
        {
            return 0;
        }
        if ((_root->_left == NULL) && (_root->_right==NULL))
        {
            return 1;
        }
        if (_root)
        {
            return _LeafSize(_root->_left) + _LeafSize(_root->_right);
        }
    }
           

第K層節點的個數

題目:給定一個二叉樹的根節點,求二叉樹第K層節點的個數

思路: 1、如果樹為空或者K<0,傳回0

2、如果樹不為空,并且求的是樹的第一層的節點個數,傳回1

3、樹的左右子樹都不為空,傳回第k-1層左右子樹節點的個數之和

遞歸方法

size_t _GetKLevel_NodeSzie( Node* _root,int k)//傳回第k層節點的個數
    {
        if (_root == NULL||K<=0)
        {
            return 0;
        }
        if (_root!=NULL&&k==1)
        {
            return 1;
        }
        return _GetKLevel_NodeSzie(_root->_left, k - 1)
            + _GetKLevel_NodeSzie(_root->_right, k - 1);
    }
           

判斷一個節點是否在一顆二叉樹中

題目: 給一個二叉樹的根節點,和另外一個節點,判斷此節點是否在二叉樹中

思路:

1、如果樹為空或者所給節點為空,傳回false

2、如果所給節點的值,與樹中某一個節點的值相等,傳回true

3、如果樹不為空,所給節點也不為空,依次周遊樹的左子樹和右子樹

代碼實作如下:

//判斷一個二叉樹的節點是否在二叉樹中
  bool IsNodeIntree(Node* _root, Node *node)
    {
        if (_root == NULL || node == NULL)
            return false;
        if (_root->_data == node->_data)
            return true;
        if (IsNodeIntree(_root->_left, node) || IsNodeIntree(_root->_right, node))
            return true;
        return false;
     }
           

求二叉樹的鏡像

題目: 輸入一顆二叉樹,輸出它的鏡像

思路:

樹的鏡像就是将樹的所有左右節點全部進行交換

1、當樹為空時,直接傳回

2、當樹隻有一個節點時,直接傳回

3、當樹的左右節點都不為空時,依次交換樹的左右節點

方法1:遞歸法

void Mirrorcursively(Node *_root)
     {
         if (_root == NULL || (_root->_left == NULL&&_root->_right == NULL))
             return;
         Node *ptmp = _root->_left;
         _root->_left = _root->_right;
         _root->_right = ptmp;
         if (_root->_left)
             Mirrorcursively(_root->_left);
         if (_root->_right)
             Mirrorcursively(_root->_right);
       }
           

方法二:非遞歸法

用非遞歸方法實作,借助棧來完成

void Iteratively(Node *_root)
     {
         if (_root == NULL)
             return;
         stack<Node*> s;
         s.push(_root);
         while (!s.empty())
         {
             Node *top = s.top();
             s.pop();
             Node *ptmp = top->_left;
             top->_left = top->_right;
             top->_right = ptmp;
             if (top->_left)
                 s.push(top->_left);
             if (top->_right)
                 s.push(top->_right)
         }
    }
           

求二叉樹中最遠的兩個節點之間的距離

題目:如果我們把二叉樹看做圖,父子節點之間的連線看成是雙向的,我們姑且定義“距離”為兩個節點之間邊的個數。寫一個程式求一棵二叉樹中相距最遠的兩個節點之間的距離。

思路:

1、如果最遠距離的兩個節點都經過了根節點,則最遠距離就是左邊最深的深度到根節點的距離+右邊最深的深度到根節點的距離

如下圖所示,樹中相距最遠的兩個節點為A,B,最大距離為6。

2、如果具有最遠距離的兩個節點不經過根節點,那麼最遠距離就是根節點上某個子樹上面的兩個節點

如下圖所示,樹中相距最遠的兩個節點為A,B,最大距離為5。

代碼實作如下:

int GetFarDistance()
       {
              int distance = -1;
              _Height(_root,distance);
              return distance;
       }
protected:
       int _Height(Node* root, int& distance)
       {
              if (root == NULL)
              {
                     return 0;
              }
              int leftH = _Height(root->_left);
              int rightH = _Height(root->_right);
              if (leftH+rightH > distance)
              {
                     distance = leftH + rightH;
              }
              return leftH > rightH ? leftH+1 : rightH+1;
       }
           

由前序周遊和中序周遊重建二叉樹

題目:輸入某二叉樹的前序周遊和中序周遊的結果,請重建出該二叉樹。假設輸入的前序周遊和中序周遊的結果中都不含重複的數字。例如輸入前序周遊序列{1,2,4,7,3,5,6,8}和中序周遊序列{4,7,2,1,5,3,8,6},則重建二叉樹并傳回。

思路:

1、在二叉樹的前序周遊序列中,第一個數字總是樹的根結點的值。但在中序周遊序列中,根結點的值在序列的中間,左子樹的結點的值位于根結點的值的左邊,而右子樹的結點的值位于根結點的值的右邊。是以我們需要掃描中序周遊序列,才能找到根結點的值。

2、如下圖所示,前序周遊序列的第一個數字1就是根結點的值。掃描中序周遊序列,就能确定根結點的值的位置。根據中序周遊特點,在根結點的值1前面的3個數字都是左子樹結點的值,位于1後面的數字都是右子樹結點的值。

3、同樣,在前序周遊的序列中,根結點後面的3個數字就是3個左子樹結點的值,再後面的所有數字都是右子樹結點的值。這樣我們就在前序周遊和中序周遊兩個序列中,分别找到了左右子樹對應的子序列。

4、既然我們已經分别找到了左、右子樹的前序周遊序列和中序周遊序列,我們可以用同樣的方法分别去建構左右子樹。也就是說,接下來的事情可以用遞歸的方法去完成。

代碼實作如下:

#include <iostream>
#include <vector>
using namespace std;
// Definition for binary tree
struct TreeNode {
  int val;
  TreeNode *left;
  TreeNode *right;
  TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
/* 先序周遊第一個位置肯定是根節點node,
中序周遊的根節點位置在中間p,在p左邊的肯定是node的左子樹的中序數組,p右邊的肯定是node的右子樹的中序數組
另一方面,先序周遊的第二個位置到p,也是node左子樹的先序子數組,剩下p右邊的就是node的右子樹的先序子數組
把四個數組找出來,分左右遞歸調用即可
 */
class Solution {
public:
struct TreeNode* reConstructBinaryTree(vector<int>       pre,vector<int> in) {

int in_size = in.size();//獲得序列的長度
2 if (in_size == 0)
return NULL;
//分别存儲先序序列的左子樹,先序序列的右子樹,中序序列的左子樹,中序序列的右子樹
vector<int> pre_left, pre_right, in_left, in_right;
int val = pre[0];//先序周遊第一個位置肯定是根節點node,取其值
//建立一個樹結點,并傳入結點值
TreeNode* node = new TreeNode(val);//root node is the first element in pre
//p用于存儲中序序列中根結點的位置
int p = 0;
for (p; p < in_size; ++p){
if (in[p] == val) //Find the root position in in 
break;        //找到即跳出for循環
}
for (int i = 0; i < in_size; ++i){
if (i < p){
 //建立中序序列的左子樹和前序序列的左子樹
in_left.push_back(in[i]);//Construct the left pre and in 
pre_left.push_back(pre[i + 1]);//前序第一個為根節點,+1從下一個開始記錄
 }
else if (i > p){
 //建立中序序列的右子樹和前序序列的左子樹
in_right.push_back(in[i]);//Construct the right pre and in 
 pre_right.push_back(pre[i]);
}
 }
//取出前序和中序周遊根節點左邊和右邊的子樹
//遞歸,再對其進行上述所有步驟,即再區分子樹的左、右子子數,直到葉節點
node->left = reConstructBinaryTree(pre_left, in_left);
node->right = reConstructBinaryTree(pre_right, in_right);
return node;
  }
 };
           

作者:wyn126

來源:CSDN

原文:https://blog.csdn.net/wyn126/article/details/81450285

版權聲明:本文為部落客原創文章,轉載請附上博文連結!