leetcode二叉搜索樹經典題目(思路、方法、code)

二叉搜索樹的簡介

二叉搜索樹是這樣的二叉樹:任意一個節點,其左兒子節點小於它,右兒子節點大於它

        4
       / \
      2   7
     / \
    1   3

因此,二叉搜索樹的具有非常高效的查找、插入和刪除操作。

且,二叉搜索樹的中序遍歷,是一個遞增的序列

在這裏插入圖片描述

98. 驗證二叉搜索樹

給定一個二叉樹,判斷其是否是一個有效的二叉搜索樹。

假設一個二叉搜索樹具有如下特徵:

  • 節點的左子樹只包含小於當前節點的數。
  • 節點的右子樹只包含大於當前節點的數。
  • 所有左子樹和右子樹自身必須也是二叉搜索樹。
示例 1:
輸入:
    2
   / \
  1   3
輸出: true
示例 2:

輸入:
    5
   / \
  1   4
     / \
    3   6
輸出: false
解釋: 輸入爲: [5,1,4,null,null,3,6]。
     根節點的值爲 5 ,但是其右子節點值爲 4

分析:每個節點,需要大於左兒子節點的值,小於右兒子節點的值。且其左兒子和右兒子都是符合二叉搜索樹的,很顯然可以用遞歸解決。需要注意的是,右子樹的所有節點的值必須都大於根節點,左子樹的所有結點的值都必須小於根節點,因此需要設置一個輔助函數來限制範圍。

  • 空則true
  • 該結點的值沒有在限制的範圍內,則返回false
  • 否則返回兩個子樹的helper,需要更新子樹的限制範圍
class Solution {
public:
    bool helper(TreeNode* root, long long lower, long long upper) {
        if (root == NULL) return true;  //爲空返回true
        if (root -> val <= lower || root -> val >= upper) return false;
        return helper(root -> left, lower, root -> val) && helper(root -> right, root -> val, upper);
    }
    bool isValidBST(TreeNode* root) {
        return helper(root, LONG_MIN, LONG_MAX);  //根沒有限制
    }
};

700. 二叉搜索樹中的搜索

給定二叉搜索樹(BST)的根節點和一個值。 你需要在BST中找到節點值等於給定值的節點。 返回以該節點爲根的子樹。 如果節點不存在,則返回 NULL。

例如,
給定二叉搜索樹:

        4
       / \
      2   7
     / \
    1   3: 2
你應該返回如下子樹:

      2     
     / \   
    1   3
在上述示例中,如果要找的值是 5,但因爲沒有節點值爲 5,我們應該返回 NULL

分析:二叉搜索樹的搜索是最基礎的應用,如果要搜的結點值小於當前結點,則向左走,否則向右走,直至找到或者爲空。

//遞歸方式
class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) 
    {
        if(root==NULL) return NULL;
        if(root->val==val)
            return root;
        else if(root->val<val)
            return searchBST(root->right,val);
        else
            return searchBST(root->left,val);
    }
};
//非遞歸方式
class Solution {
public:
    TreeNode* searchBST(TreeNode* root, int val) 
    {
        if(root==NULL) return NULL;
        TreeNode* temp=root;
        while(temp!=NULL)
        {
            if(temp->val==val) return temp;
            else if(temp->val<val) temp=temp->right;
            else temp=temp->left;
        }
        return NULL;
    }
};

701. 二叉搜索樹中的插入操作

給定二叉搜索樹(BST)的根節點和要插入樹中的值,將值插入二叉搜索樹。 返回插入後二叉搜索樹的根節點。 保證原始二叉搜索樹中不存在新值。

注意,可能存在多種有效的插入方式,只要樹在插入後仍保持爲二叉搜索樹即可。 你可以返回任意有效的結果。

例如, 
給定二叉搜索樹:

        4
       / \
      2   7
     / \
    1   3
和 插入的值: 5
你可以返回這個二叉搜索樹:

         4
       /   \
      2     7
     / \   /
    1   3 5

分析:二叉搜索樹具有良好的順序。二叉搜索樹的巨大優勢就是:在平均情況下,能夠在 O(logN)O(logN) 的時間內完成搜索和插入元素。

因此等價於找到要插入的值對應的位置即可,將插入的節點作爲葉子節點的子節點插入。首先建立相應的結點,然後搜索,找到正確的位置然後插入。

  • val > node.val,插入到右子樹。
  • val < node.val,插入到左子樹。
//遞歸寫法:
class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) 
    {
          TreeNode* new_node=new TreeNode(val);
          if(root==NULL) return new_node;
          else if(root->val<val) root->right=insertIntoBST(root->right,val);
          else root->left=insertIntoBST(root->left,val);
          return root;
    }
};
//非遞歸寫法:
class Solution {
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) 
    {
          TreeNode* new_node=new TreeNode(val);
          if(root==NULL) return new_node;
          TreeNode* pre=NULL,*temp=root; //找到插入位置,然後連接起來
          while(temp!=NULL)
          {
              pre=temp;
              if(temp->val>val) temp=temp->left;
              else temp=temp->right;
          }
          //則temp位置就是要插入的位置,pre爲其父結點
          if(pre->val<val)
            pre->right=new_node;
          else
            pre->left=new_node;
        return root;

    }
};

450. 刪除二叉搜索樹中的節點

給定一個二叉搜索樹的根節點 root 和一個值 key,刪除二叉搜索樹中的 key 對應的節點,並保證二叉搜索樹的性質不變。返回二叉搜索樹(有可能被更新)的根節點的引用。

一般來說,刪除節點可分爲兩個步驟:

首先找到需要刪除的節點;如果找到了,刪除它。
說明: 要求算法時間複雜度爲 O(h),h 爲樹的高度。

示例:
root = [5,3,6,2,4,null,7]
key = 3

    5
   / \
  3   6
 / \   \
2   4   7
給定需要刪除的節點值是 3,所以我們首先找到 3 這個節點,然後刪除它。
一個正確的答案是 [5,4,6,2,null,null,7], 如下圖所示。
    5
   / \
  4   6
 /     \
2       7

另一個正確答案是 [5,2,6,null,4,null,7]5
   / \
  2   6
   \   \
    4   7

分析:二叉搜索樹的刪除操作較爲複雜,主要步驟爲:找到要刪除的節點,刪除該節點(保持二叉樹的特性)

一種可行的刪除操作爲:

  • 如果刪除的要刪除的節點是葉節點,則將其刪去即可

  • 需要刪除的節點沒有左兒子,則將右兒子提上去即可

  • 需要刪除的節點的左兒子沒有右兒子,則將左兒子提上去即可

  • 以上情況均不滿足,則將左兒子的子孫中最大的節點提到刪除的節點處

class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if (root==NULL) return root;
        //--------------找到要刪除的節點----------
        if (root->val < key)  
        {
            root->right = deleteNode(root->right, key);     
            return root;
        }
        if (root->val > key) 
        {
            root->left = deleteNode(root->left, key);    
            return root;
        }
        // 此時的root就是要刪除的節點
        if (root->left==NULL) //root的left爲空,則將右子樹替代root
        {
            TreeNode* tmp = root->right;   
            delete root;
            return tmp;
        }
        if (root->right==NULL)  //root的right爲空,則將左子樹替代root
        {
            TreeNode* tmp = root->left;  
            delete root;
            return tmp;
        }
        //都不是空的話,找到左子樹中的最大值放入
        TreeNode* tmp = root->left;
        if(tmp->right==NULL)  //如果左子樹沒有右節點,將左子樹放入即可
        {
            tmp->right=root->right;
            delete root;
            return tmp;
        }
        //否則找到左子樹中最大的節點
        TreeNode* pre=root;
        while (tmp->right!=NULL) 
        {
            pre=tmp;
            tmp = tmp->right; //找到左子樹的最大值
        }
        //將最大節點的值傳入root,將pre->right指向tmp的left即可
        root->val=tmp->val;
        pre->right=tmp->left;
        return root;
    }
};

面試題54. 二叉搜索樹的第k大節點

給定一棵二叉搜索樹,請找出其中第k大的節點。

示例 1:
輸入: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
輸出: 4
    
示例 2:
輸入: root = [5,3,6,2,4,null,null,1], k = 3
       5
      / \
     3   6
    / \
   2   4
  /
 1
輸出: 4

分析:二叉搜索樹的中序遍歷是遞增數列,因此,中序遍歷的逆數列是一個遞減數列。可以用一個值記錄已經遍歷多少個節點,則遍歷到第k個時即爲所求。

如何獲取中序遍歷倒序數列?改變遍歷順序即可

  • 先以該方式遍歷右子樹
  • 再遍歷根節點
  • 再以該方式遍歷左子樹
class Solution 
{    private:
    	int n = 0;  //記錄次數
   		int res = 0; //記錄結果
    public:
    int kthLargest(TreeNode *root, int k) 
    {
        n = k;
        helper(root);
        return res;
    }
    void helper(TreeNode *root){
        if(root->right != NULL && n>0 )  
            helper(root->right);
        n--;
        if(n==0)
        {
            res = root->val;
            return;
        }
        if(root->left != NULL && n>0 )  helper(root->left);
    }
};

938. 二叉搜索樹的範圍和

給定二叉搜索樹的根結點 root,返回 LR(含)之間的所有結點的值的和。

二叉搜索樹保證具有唯一的值。

示例 1:
輸入:root = [10,5,15,3,7,null,18], L = 7, R = 15
輸出:32
    
示例 2:
輸入:root = [10,5,15,3,7,13,18,1,null,6], L = 6, R = 10
輸出:23

分析:找到 L R 範圍內的節點之和,因此可採用遞歸的方式

  • 如果當前節點是NULL,返回0即可

  • 如果當前節點的值小於L,說明 L-R範圍內的樹都在右子樹,返回 右子樹在該範圍的和即可

  • 如果當前節點的值大於R,說明 L-R範圍內的樹都在左子樹,返回 左子樹在該範圍的和即可

  • 否則,說明當前節點的值在 L和R之間,返回當前節點的值加上左子樹、右子樹在該範圍的和即可

    (因爲二叉搜索樹的排序性,因此不需要更新L,R)

class Solution {
public:
    int rangeSumBST(TreeNode* root, int L, int R) 
    {
        if(root==NULL) return 0;
        if(root->val<L) return rangeSumBST(root->right,L,R);
        else if(root->val>R) return rangeSumBST(root->left,L,R);
        else return root->val+rangeSumBST(root->right,L,R)+rangeSumBST(root->left,L,R);
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章