Given a non-empty binary search tree and a target value, find k values in the BST that are closest to the target.
Note:
- Given target value is a floating point.
- You may assume k is always valid, that is: k≤ total nodes.
- You are guaranteed to have only one unique set of k values in the BST that are closest to the target.
Example:
Input: root = [4,2,5,1,3], target = 3.714286, and k = 2 4 / \ 2 5 / \ 1 3 Output: [4,3]
Follow up:
Assume that the BST is balanced, could you solve it in less than O(n) runtime (where n = total nodes)?
這道題是之前那道 Closest Binary Search Tree Value 的拓展,那道題只讓找出離目標值最近的一個節點值,而這道題讓找出離目標值最近的k個節點值,難度瞬間增加了不少,博主最先想到的方法是用中序遍歷將所有節點值存入到一個一維數組中,由於二分搜索樹的性質,這個一維數組是有序的,然后再在有序數組中需要和目標值最近的k個值就簡單的多,參見代碼如下:
解法一:
class Solution { public: vector<int> closestKValues(TreeNode* root, double target, int k) { vector<int> res, v; inorder(root, v); int idx = 0; double diff = numeric_limits<double>::max(); for (int i = 0; i < v.size(); ++i) { if (diff >= abs(target - v[i])) { diff = abs(target - v[i]); idx = i; } } int left = idx - 1, right = idx + 1; for (int i = 0; i < k; ++i) { res.push_back(v[idx]); if (left >= 0 && right < v.size()) { if (abs(v[left] - target) > abs(v[right] - target)) { idx = right; ++right; } else { idx = left; --left; } } else if (left >= 0) { idx = left; --left; } else if (right < v.size()) { idx = right; ++right; } } return res; } void inorder(TreeNode *root, vector<int> &v) { if (!root) return; inorder(root->left, v); v.push_back(root->val); inorder(root->right, v); } };
還有一種解法是直接在中序遍歷的過程中完成比較,當遍歷到一個節點時,如果此時結果數組不到k個,直接將此節點值加入結果 res 中,如果該節點值和目標值的差值的絕對值小於結果 res 的首元素和目標值差值的絕對值,說明當前值更靠近目標值,則將首元素刪除,末尾加上當前節點值,反之的話說明當前值比結果 res 中所有的值都更偏離目標值,由於中序遍歷的特性,之后的值會更加的遍歷,所以此時直接返回最終結果即可,參見代碼如下:
解法二:
class Solution { public: vector<int> closestKValues(TreeNode* root, double target, int k) { vector<int> res; inorder(root, target, k, res); return res; } void inorder(TreeNode *root, double target, int k, vector<int> &res) { if (!root) return; inorder(root->left, target, k, res); if (res.size() < k) res.push_back(root->val); else if (abs(root->val - target) < abs(res[0] - target)) { res.erase(res.begin()); res.push_back(root->val); } else return; inorder(root->right, target, k, res); } };
下面這種方法是上面那種方法的迭代寫法,原理一模一樣,參見代碼如下:
解法三:
class Solution { public: vector<int> closestKValues(TreeNode* root, double target, int k) { vector<int> res; stack<TreeNode*> s; TreeNode *p = root; while (p || !s.empty()) { while (p) { s.push(p); p = p->left; } p = s.top(); s.pop(); if (res.size() < k) res.push_back(p->val); else if (abs(p->val - target) < abs(res[0] - target)) { res.erase(res.begin()); res.push_back(p->val); } else break; p = p->right; } return res; } };
在來看一種利用最大堆來解題的方法,堆里保存的一個差值 diff 和節點值的 pair,中序遍歷二叉樹(也可以用其他遍歷方法),然后對於每個節點值都計算一下和目標值之差的絕對值,由於最大堆的性質,diff 大的自動拍到最前面,維護k個 pair,如果超過了k個,就把堆前面大的 pair 刪掉,最后留下的k個 pair,將 pair 中的節點值取出存入結果 res 中返回即可,參見代碼如下:
解法四:
class Solution { public: vector<int> closestKValues(TreeNode* root, double target, int k) { vector<int> res; priority_queue<pair<double, int>> q; inorder(root, target, k, q); while (!q.empty()) { res.push_back(q.top().second); q.pop(); } return res; } void inorder(TreeNode *root, double target, int k, priority_queue<pair<double, int>> &q) { if (!root) return; inorder(root->left, target, k, q); q.push({abs(root->val - target), root->val}); if (q.size() > k) q.pop(); inorder(root->right, target, k, q); } };
下面的這種方法用了兩個棧,pre 和 suc,其中 pre 存小於目標值的數,suc 存大於目標值的數,開始初始化 pre 和 suc 的時候,要分別將最接近目標值的稍小值和稍大值壓入 pre 和 suc,然后循環k次,每次比較 pre 和 suc 的棧頂元素,看誰更接近目標值,將其存入結果 res 中,然后更新取出元素的棧,依次類推直至取完k個數返回即可,參見代碼如下:
解法五:
class Solution { public: vector<int> closestKValues(TreeNode* root, double target, int k) { vector<int> res; stack<TreeNode*> pre, suc; while (root) { if (root->val <= target) { pre.push(root); root = root->right; } else { suc.push(root); root = root->left; } } while (k-- > 0) { if (suc.empty() || !pre.empty() && target - pre.top()->val < suc.top()->val - target) { res.push_back(pre.top()->val); getPredecessor(pre); } else { res.push_back(suc.top()->val); getSuccessor(suc); } } return res; } void getPredecessor(stack<TreeNode*> &pre) { TreeNode *t = pre.top(); pre.pop(); if (t->left) { pre.push(t->left); while (pre.top()->right) pre.push(pre.top()->right); } } void getSuccessor(stack<TreeNode*> &suc) { TreeNode *t = suc.top(); suc.pop(); if (t->right) { suc.push(t->right); while (suc.top()->left) suc.push(suc.top()->left); } } };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/272
類似題目:
Closest Binary Search Tree Value
參考資料:
https://leetcode.com/problems/closest-binary-search-tree-value-ii/