项目地址:https://github.com/SpecialYy/Sword-Means-Offer
问题
给定一棵二叉搜索树,请找出其中的第k小的结点。
解析
二叉搜索树是这样定义的:它允许是棵空树;根节点的值小于其所有左子树中的节点,根节点的值大于其所有右子树中的节点。其左右子树的所有节点也满足此约束。比如下图就是一个二叉树搜索树:
很明显上图满足二叉搜索树的定义,我们可以发现对于任意一棵子树,其左孩子<子树的根节点<右孩子,这不就是中序遍历的规则。再来看题目要求输出第k小个节点,我们只需采用中序遍历的方式找到第k个节点即可。问题就转换为如何进行中序遍历二叉树问题!
思路一
采用递归的方式,不断递归深入根节点的左孩子,直到碰到空节点为止,然后回溯输出当前节点。再以同样的方式递归遍历其右孩子。在此期间,每访问一个节点,我们都对k进行减一操作,直到k为0,说明该节点即为第k个节点。
TreeNode kThNode = null;
/**
* 递归版中序遍历
* @param pRoot
* @param k
* @return
*/
TreeNode KthNode1(TreeNode pRoot, int k) {
if (pRoot == null) {
return null;
}
int[] count = new int[] {k};
KthNode(pRoot, count);
return kThNode;
}
void KthNode(TreeNode node, int[] count) {
if (count[0] != 0 && node != null) {
KthNode(node.left, count);
if (--count[0] == 0) {
kThNode = node;
return;
}
KthNode(node.right, count);
}
}
思路二
非递归版中序遍历,如果你理解了递归版的写法,其实改成非递归的方式还是挺容易的。因为递归的过程其实就是函数不断的调入,在计算机中每一个函数都是一个栈帧,函数的调入与完成对应入栈与出栈。我们可以利用栈来模拟递归遍历,首先根入栈,然后令根节点的左孩子不断入栈直到为空,弹出栈顶,令其右孩子入栈,重复以上操作,直到遍历结束或者访问第k个节点为止。
/**
* 非递归版中序遍历
* @param pRoot
* @param k
* @return
*/
TreeNode KthNode(TreeNode pRoot, int k) {
if (pRoot == null) {
return null;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode p = pRoot;
while(!stack.isEmpty() || p != null) {
while(p != null) {
stack.push(p);
p = p.left;
}
TreeNode node = stack.pop();
if ((--k) == 0) {
return node;
}
p = node.right;
}
return null;
}
总结
关于二叉树求某一个节点的问题,都可以尝试使用先序,中序,后序和层序的方式来解决。