题解 | #二叉搜索树的第k个结点#

二叉搜索树的第k个结点

http://www.nowcoder.com/practice/ef068f602dde4d28aab2b210e859150a

题目描述

给定一棵二叉搜索树,请找出其中的第k小的TreeNode结点。

考查知识点

  • 二叉搜索树(bst)
  • 树的遍历
  • 二叉搜索树中序遍历的特殊性质

分析

1.首先我们给出二叉搜索数的定义:
如果一颗二叉树,满足如下条件

  • 若其结点的左子树不空,且左子树上所有结点的值均不大于它的根结点的值。
  • 若任意结点的右子树不空,则右子树上所有结点的值均不小于它的根结点的值。
    则我们称这样的二叉数为二叉搜索树
    如下图即为一颗二叉搜索树(以下统称为bst), 对于任意节点,其左子树的所有节点一定不大于该节点,其右子树的节点一定不小于该节点
    图片说明
    2.树的遍历: 关于树的四种遍历方式我在JZ61这道题中已经介绍,这里不做赘述,这里给需要的读者附上那题的链接:
    https://blog.nowcoder.net/n/50edaca0531e4693a2db6e0575143d72
    3.二叉搜索树中序遍历的特殊性质
    关于这个性质当结论背过即可,该性质为:bst中序遍历得到的序列即为将bst上所有节点按从小到大排序的序列,可以举个例子说明其正确性:
    图片说明

解法一:使用递归实现中序遍历,因为bst的中序遍历结果即为节点的值从小到大排序的结果,所以通过一个全局变量记录当前遍历至第k个即可

  • 优点:代码简洁,实现简单
  • 缺点:递归所耗费的时间和空间比迭代实现大得多,若是多二者要求比较苛刻时要慎重考虑
    正确代码及注释如下
/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    int index = 0;
    TreeNode* ans = nullptr;
    TreeNode* KthNode(TreeNode* pRoot, int k) {
        if(!pRoot) return nullptr;    //边界处理
        KthNode(pRoot->left, k);      //处理左子树
        index ++ ;
        if(index == k) ans = pRoot;   //处理当前节点
        KthNode(pRoot->right, k);     //处理右子树
        return ans;                   //返回结果
    }
};

时间复杂度:空间复杂度与系统堆栈有关,系统栈需要记住每个节点的值,所以空间复杂度为O(n)。时间复杂度应该为O(n),根据公式T(n)=2T(n/2)+1=2(2T(n/4)+1)+1=2^logn+2^(logn-1)+...+2+1 ~= n,所以时间复杂度为O(n)

方法二:通过迭代的方式实现中序遍历,依旧是使用一个变量记录遍历至第k个

  • 优点:占用时间,空间少,速度较递归实现快
  • 缺点:代码复杂,实现难度比递归写法大

正确代码及注释如下

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    TreeNode* KthNode(TreeNode* pRoot, int k) {
        if(!pRoot || k == 0) return nullptr;   //空树与边界处理
        int idx = 0;
        stack<TreeNode*> stk;          //用栈模拟中序遍历的递归与回溯过程
        while (pRoot || stk.size()) {
            while (pRoot) {        //先遍历左子树
                stk.push(pRoot);
                pRoot = pRoot->left;  
            }
            pRoot = stk.top();
            stk.pop();                        //模拟回溯
            idx ++;
            if(idx == k) return pRoot;        //再遍历当前节点
            pRoot = pRoot->right; //最后遍历右子树
        }
        return nullptr;
    }


};

时间复杂度分析:由于不管是先序遍历还是中序遍历以及后序遍历,我们都需要利用一个辅助栈来进行每个节点的存储打印,所以每个节点都要进栈和出栈,不过是根据那种遍历方式改变的是每个节点的进栈顺序,所以时间复杂度为O(n),同样空间复杂度也为O(n),n为结点数。

全部评论

相关推荐

头像
10-13 18:10
已编辑
东南大学 C++
。收拾收拾心情下一家吧————————————————10.12更新上面不知道怎么的,每次在手机上编辑都会只有最后一行才会显示。原本不想写凉经的,太伤感情了,但过了一天想了想,凉经的拿起来好好整理,就像象棋一样,你进步最快的时候不是你赢棋的时候,而是在输棋的时候。那废话不多说,就做个复盘吧。一面:1,经典自我介绍2,项目盘问,没啥好说的,感觉问的不是很多3,八股问的比较奇怪,他会深挖性地问一些,比如,我知道MMU,那你知不知道QMMU(记得是这个,总之就是MMU前面加一个字母)4,知不知道slab内存分配器-&gt;这个我清楚5,知不知道排序算法,排序算法一般怎么用6,写一道力扣的,最长回文子串反问:1,工作内容2,工作强度3,关于友商的问题-&gt;后面这个问题问HR去了,和中兴有关,数通这个行业和友商相关的不要提,这个行业和别的行业不同,别的行业干同一行的都是竞争关系,数通这个行业的不同企业的关系比较微妙。特别细节的问题我确实不知道,但一面没挂我。接下来是我被挂的二面,先说说我挂在哪里,技术性问题我应该没啥问题,主要是一些解决问题思路上的回答,一方面是这方面我准备的不多,另一方面是这个面试写的是“专业面试二面”,但是感觉问的问题都是一些主管面/综合面才会问的问题,就是不问技术问方法论。我以前形成的思维定式就是专业面会就是会,不会就直说不会,但事实上如果问到方法论性质的问题的话得扯一下皮,不能按照上面这个模式。刚到位置上就看到面试官叹了一口气,有一些不详的预感。我是下午1点45左右面的。1,经典自我介绍2,你是怎么完成这个项目的,分成几个步骤。我大致说了一下。你有没有觉得你的步骤里面缺了一些什么,(这里已经在引导我往他想的那个方向走了),比如你一个人的能力永远是不够的,,,我们平时会有一些组内的会议来沟通我们的所思所想。。。。3,你在项目中遇到的最困难的地方在什么方面4,说一下你知道的TCP/IP协议网络模型中的网络层有关的协议......5,接着4问,你觉得现在的socket有什么样的缺点,有什么样的优化方向?6,中间手撕了一道很简单的快慢指针的问题。大概是在链表的倒数第N个位置插入一个节点。————————————————————————————————————10.13晚更新补充一下一面说的一些奇怪的概念:1,提到了RPC2,提到了fu(第四声)拷贝,我当时说我只知道零拷贝,知道mmap,然后他说mmap是其中的一种方式,然后他问我知不知道DPDK,我说不知道,他说这个是一个高性能的拷贝方式3,MMU这个前面加了一个什么字母我这里没记,别问我了4,后面还提到了LTU,VFIO,孩子真的不会。
走呀走:华子二面可能会有场景题的,是有些开放性的问题了
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务