题目:给定一个二叉搜索树的根节点 root
,和一个整数 k
,请你设计一个算法查找其中第 k
个最小元素(从 1 开始计数)。
又是一道二叉树的题目,题目要求我们从一个二叉树中找到第K小的元素。
我自己第一次提交的时候使用的是暴力方法,即对整个二叉树进行层序遍历,并将遍历得到的所有节点的值都放进一个列表中,最后我们队列表进行从小到大的排序,然后获取该列表中的第K-1个元素。
该思路的代码如下:
public int kthSmallest(TreeNode230 root, int k) {
if (root == null) {
return 0;
}
Queue<TreeNode230> queue = new LinkedList<>();
List<Integer> small = new ArrayList<Integer>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode230 node = queue.poll();
small.add(node.val);
if (node.left!= null) {
queue.offer(node.left);
}
if (node.right!= null) {
queue.offer(node.right);
}
}
}
Collections.sort(small);
System.out.println(small);
return small.get(k - 1);
}
这段代码的确是能够成功执行的,但是这样的解法并没有利用到搜索二叉树的特性,效率也很低。
搜索二叉树的左子树上所有节点的值均小于它的根节点的值,而右子树上所有节点的值均大于它的根节点的值。
通过这个搜索二叉树的特性,理论上,我们通过对这颗二叉树进行中序遍历的方式,遍历到第K个元素,就获取到了二叉树第K小的元素。
中序遍历的顺序是左-根-右,由于搜索二叉树的排序是左节点<根节点<右节点,所以我们只要从最左侧的节点开始向右找K次,就可以获得答案需要的第K小的元素。
先创建一个栈,用来存放和更新对遍历到的二叉树:
Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
栈是先进后出,也就是说,最后放进去的,会最先拿出来。
第一步:将整个树放进stack中,然后一层一层地把这棵树的左子树放进栈中,直到整个二叉树最下面一层最左边的节点。
while (root != null)
{
stack.push(root);
root = root.left;
}
这样,我们从栈中获取元素的时候,就会从最后一个最节点开始,获取到后,K需要减一。如果K本来的值是1,那么当前获取到的红色元素,就已经是整个二叉树中,第一小的元素了。
接着我们要获取第二小的元素,也就是红色元素右上方的第一个元素。
但是有一个地方需要留意:如果红色节点有一个右子节点(就像红色节点右边的兄弟节点那样),那么红色节点的右子节点是比红色节点的父节点更小的,所以,我们要先尝试获取当前节点的右子节点,作为比当前节点更大的那个元素。
root = stack.pop();
--k;
if (k == 0) {
break;
}
root = root.right;
}
要实现不断地获取,就需要设立一个循环,并且实现如果没有右子节点,就取父节点,然后继续取父节点的右子节点。
所以在红色节点获取到父节点后,会继续获取黄色节点——紫色节点——蓝色节点,蓝色节点获取完之后,这可树的整个左子树都已经获取完了,如果K的值还没有减少到0,就会继续遍历整棵树的根节点——然后再从整棵树的右子树开始...
加上循环后的代码:
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
--k;
if (k == 0) {
break;
}
root = root.right;
}
完整代码:
class Solution {
public int kthSmallest(TreeNode root, int k) {
Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left;
}
root = stack.pop();
--k;
if (k == 0) {
break;
}
root = root.right;
}
return root.val;
}
}