題目
給定一個二叉搜索樹,編寫一個函數 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的過程為:
-
若b是空樹,則搜索失敗,否則:
-
若x等於b的根節點的數據域之值,則查找成功;否則:
-
若x小於b的根節點的數據域之值,則搜索左子樹;否則:
-
查找右子樹。
2. 從有序數組構造一個二叉查找樹
3. 往 BST 中插入元素
向一個二叉搜索樹b中插入一個節點s的算法,過程為:
-
若b是空樹,則將s所指結點作為根節點插入,否則:
-
若s->data等於b的根節點的數據域之值,則返回,否則:
-
若s->data小於b的根節點的數據域之值,則把s所指節點插入到左子樹中,否則:
-
把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小元素的求解過程!!!