剑指 Offer 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
限制:
1 ≤ k ≤ 二叉搜索树元素个数
解法一:中序遍历
二叉搜索树
的中序遍历
就是有序
的,故中序遍历后取出倒数第k
个元素即可。
这里介绍一下序列中倒数第k个数
的计算。不总结的话,有时候不小心就出错了。
倒着数的第k
个元素,是顺着数的第n - k + 1
个元素,n
表示元素总个数,原因是共n
个数,那么倒数第k
个前面还有n - k
个元素,则顺着数应该是n - k + 1
,又因为下标是从0
开始的,故第n - k + 1
个元素的下标是n - k
。举例如图:
Java代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int kthLargest(TreeNode root, int k) {
if(root == null) return -1;
List<TreeNode> list = new ArrayList<>();
inOrder(root,list);
//中序遍历结束后返回倒数第k个元素的值
return list.get(list.size() - k).val;
}
//中序遍历
private void inOrder(TreeNode root,List<TreeNode> list){
if(root.left != null){
inOrder(root.left,list);
}
list.add(root);
if(root.right != null){
inOrder(root.right,list);
}
}
}
解法二:小顶堆
由于题目中给定的二叉搜索树,所以可以直接中序遍历然后拿到倒数第k
个元素即可。但如果给定的是一棵普通的树呢?那么没有了中序遍历后便是有序列表的性质,我们就不能用解法一了,故这里再介绍一个更通用
的解法,使用小顶堆
。
无论用前序、中序、后序
遍历都行,能遍历完树的所有节点就可以。为了和解法一统一,我们这里也选中序遍历
。
遍历到当前节点时,直接加入小顶堆
,但当堆中的元素大于k
个时,就弹出堆顶元素,即让堆中最多保留k
个元素,那么遍历结束后,堆中就剩下k
个元素,且堆顶的就是我们要的第k
大的元素。
剑指offer中有关使用堆的题还有两个:
40. 最小的k个数
41. 数据流中的中位数
Java代码
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int kthLargest(TreeNode root, int k) {
if(root == null) return -1;
Queue<Integer> queue = new PriorityQueue<>();
inOrder(root,queue,k);
return queue.peek();
}
private void inOrder(TreeNode root,Queue<Integer> queue,int k ){
if(root.left != null){
inOrder(root.left,queue,k);
}
queue.offer(root.val);
if(queue.size() > k){
queue.poll();
}
if(root.right != null){
inOrder(root.right,queue,k);
}
}
}
总结
解法二可能效率比解法一低一些,但是解法二更通用。且很多时候遇到第k个XX字眼
的场景时,我们基本第一反应都是用堆
来做的,因为通用呀,0.0。