二叉搜索樹中第K小的元素-- 二分查找


題目

給定一個二叉搜索樹,編寫一個函數 kthSmallest 來查找其中第 k 個最小的元素。

說明:
你可以假設 k 總是有效的,1 ≤ k ≤ 二叉搜索樹元素個數。

示例 1:

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

示例 2:

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

進階:
如果二叉搜索樹經常被修改(插入/刪除操作)並且你需要頻繁地查找第 k 小的值,你將如何優化 kthSmallest 函數?

 

前序

首先了解一下二叉搜索樹.

二叉搜索樹(Binary Search Tree)是指一顆空樹或者具有下列性質的二叉樹

  • 任意節點的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;

  • 任意節點的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;

  • 任意節點的左、右子樹也分別為二叉查找樹;

  • 沒有鍵值相等的節點。

二叉搜索樹有一個有一個重要的性質, 中序遍歷為排序數組,也就是從小到大排序

1. 查找 BST 中的某個元素

在二叉搜索樹b中查找x的過程為:

  1. 若b是空樹,則搜索失敗,否則:

  2. 若x等於b的根節點的數據域之值,則查找成功;否則:

  3. 若x小於b的根節點的數據域之值,則搜索左子樹;否則:

  4. 查找右子樹。

2. 從有序數組構造一個二叉查找樹

 3. 往 BST 中插入元素

向一個二叉搜索樹b中插入一個節點s的算法,過程為:

  1. 若b是空樹,則將s所指結點作為根節點插入,否則:

  2. 若s->data等於b的根節點的數據域之值,則返回,否則:

  3. 若s->data小於b的根節點的數據域之值,則把s所指節點插入到左子樹中,否則:

  4. 把s所指節點插入到右子樹中。(新插入節點總是葉子節點)

通過上面的前續,了解了二叉搜索樹的基本內容和性質, 下面我們着重解決本題目!!!


思想 與 解法

1. 二分法

思想

二叉搜索樹特點是左節點值小於根節點,而右節點值大於根節點;利用這個特性可以采用二分法,將整個樹的節點分為左節點和右節點兩部分,當k值等於左節點值+1時,說明此時root為要求的第k小元素;當k值小於左節點值時,說明第k小元素位於根節點左側;當k值大於左節點時,說明第k小元素位於根節點右側;之后遞歸,在滿足條件的左右兩側節點中進行遍歷划分,直到求出第k小。

代碼

public class TreeNode {
    public var val: Int
    public var left: TreeNode?
    public var right: TreeNode?
    public init(_ val: Int){
        self.val = val
        self.left = nil
        self.right = nil
    }
}

func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int {
    let left_num = calculate(root?.left)
    if k - 1 == left_num {
        return root?.val ?? 0
    } else if k - 1 < left_num {
        return kthSmallest(root?.left, k)
    } else {
        return kthSmallest(root?.right, k - 1 - left_num)
    }
}

func calculate(_ root: TreeNode?) -> Int {
    if root == nil {
        return 0
    }
    let num = 1 + calculate(root?.left) + calculate(root?.right)
    return num
}

 

2. 中序排序(遞歸)

二叉搜索樹特點是左節點值小於根節點,而右節點值大於根節點;當我們進行中序遍歷時【左中右】,就可以將整個二叉搜索樹按照從小到大的順序進行遍歷,使用一個計數器,當與k值相等時,就可以輸出當前節點元素。
public:
    int kthSmallest(TreeNode* root, int k) { //利用中序遍歷進行計數
        int num = 0;
        int res = 0;
        Inorder(root,k,num,res);
        return res;
    }//中序遍歷(左中右)
    void Inorder(TreeNode* root,int k,int &num,int &res){
        if(root == NULL)
        {
            return ;
        }
        Inorder(root->left,k,num,res);
        num++;
        if(k == num)
        {
            res = root->val;
        }
        Inorder(root->right,k,num,res);
    }

 

3. 中序排序(棧)

原理同樣是中序遍歷,但是結合了棧進行了操作,降低了遞歸引起的效率低的問題

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int kthSmallest(TreeNode *root, int k)
    {
        stack<TreeNode *> s;
        while (1)
        {
            if (root)
            {
                s.push(root);
                root = root->left;
                continue;
            }
            if (k == 1)
                return s.top()->val;
            root = s.top()->right;
            s.pop();
            k--;
        }
    }
};

 

以上就是二叉搜索樹第k小元素的求解過程!!!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM