数据结构和算法十八

剑指 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 ≤ 二叉搜索树元素个数

解题思路:
本题解法基于此性质:二叉搜索树的中序遍历为 递增序列 。
根据以上性质,易得二叉搜索树的 中序遍历倒序 为 递减序列 。
因此,求 “二叉搜索树第 kk 大的节点” 可转化为求 “此树的中序遍历倒序的第k个节点”。

//中序遍历 为 “左、根、右” 顺序,递归法代码如下:
// 打印中序遍历
void dfs(TreeNode root) {
    if(root == null) return;
    dfs(root.left); // 左
    System.out.println(root.val); // 根
    dfs(root.right); // 右
}
//中序遍历的倒序 为 “右、根、左” 顺序,递归法代码如下:
// 打印中序遍历倒序
void dfs(TreeNode root) {
    if(root == null) return;
    dfs(root.right); // 右
    System.out.println(root.val); // 根
    dfs(root.left); // 左
}

为求第 k 个节点,需要实现以下 三项工作 :
1、递归遍历时计数,统计当前节点的序号;
2、递归到第 k 个节点时,应记录结果res ;
3、记录结果后,后续的遍历即失去意义,应提前终止(即返回)。

递归解析:
         1、终止条件:当节点 root 为空(越过叶节点),则直接返回;
         2、递归右子树:即 dfs(root.right) ;
         3、三项工作:
               【1】提前返回:若 k=0 ,代表已找到目标节点,无需继续遍历,因此直接返回;
               【2】统计序号:执行k=k−1 (即从k 减至0 );
               【3】记录结果:若 k=0,代表当前节点为第 k 大的节点,因此记录res=root.val ;
         4、递归左子树: 即dfs(root.left) ;

复杂度分析:
        时间复杂度O(N) :当树退化为链表时(全部为右子节点),无论k 的值大小,递归深度都为N ,占用O(N) 时间。
        空间复杂度O(N) :当树退化为链表时(全部为右子节点),系统使用O(N) 大小的栈空间。

代码:

//题目指出:1≤k≤N (二叉搜索树节点个数);因此无需考虑k>N 的情况。
//若考虑,可以在中序遍历完成后判断k>0 是否成立,若成立则说明k>N 。
class Method{
    int res, k;
    public int kthLargest(TreeNode root, int k) {
        this.k = k;
        dfs(root);
        return res;
    }
    void dfs(TreeNode root) {
        if(root == null) return;
        dfs(root.right);
        if(k == 0) return;
        if(--k == 0) res = root.val;
        dfs(root.left);
    }
}

剑指 Offer 55 - I. 二叉树的深度

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。

例如:
        给定二叉树 [3,9,20,null,null,15,7],

        3
       / \
      9  20
        /  \
       15   7
    返回它的最大深度 3 。

提示:
        节点总数 <= 10000

解题思路:

树的遍历方式总体分为两类:深度优先搜索(DFS)、广度优先搜索(BFS);

  • 常见的 DFS : 先序遍历、中序遍历、后序遍历;
  • 常见的 BFS : 层序遍历(即按层遍历)。

方法一:后序遍历(DFS)

  • 树的后序遍历 / 深度优先搜索往往利用 递归 或 栈 实现,本文使用递归实现
  • 关键点: 此树的深度和其左(右)子树的深度之间的关系。显然,此树的深度 等于左子树的深度与右子树的深度中的最大值 +1。

在这里插入图片描述

算法解析:
         1、终止条件:当 root​ 为空,说明已越过叶节点,因此返回深度0 。
         2、递推工作:本质上是对树做后序遍历。
               【1】计算节点root​的左子树的深度,即调用 maxDepth(root.left);
               【2】计算节点root​的右子树的深度,即调用 maxDepth(root.right);
         3、返回值: 返回 此树的深度 ,即 max(maxDepth(root.left), maxDepth(root.right)) + 1。
复杂度分析:
        时间复杂度 O(N): N为树的节点数量,计算树的深度需要遍历所有节点。
        空间复杂度 O(N): 最差情况下(当树退化为链表时),递归深度可达到 N。

class Method1{
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

方法二:层序遍历(BFS)

  • 树的层序遍历 / 广度优先搜索往往利用队列实现。
  • 关键点: 每遍历一层,则计数器+1,直到遍历完成,则可得到树的深度。

算法解析:
         1、特例处理:当root​为空,直接返回 深度0 。
         2、初始化:队列queue(加入根节点 root ),计数器 res = 0。
         3、循环遍历:当queue 为空时跳出。
               【1】初始化一个空列表 tmp ,用于临时存储下一层节点;
               【2】遍历队列:遍历queue中的各节点 node,并将其左子节点和右子节点加入 tmp;
               【3】更新队列:执行 queue = tmp ,将下一层节点赋值给 queue;
               【4】统计层数:执行 res += 1 ,代表层数加1;
         4、返回值: 返回 res 即可。
复杂度分析:
         时间复杂度 O(N):N 为树的节点数量,计算树的深度需要遍历所有节点。
         空间复杂度 O(N):最差情况下(当树平衡时),队列 queue 同时存储N/2个节点。

class Method2{
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        List<TreeNode> queue = new LinkedList<>() {{ add(root); }}, tmp;
        int res = 0;
        while(!queue.isEmpty()) {
            tmp = new LinkedList<>();
            for(TreeNode node : queue) {
                if(node.left != null) tmp.add(node.left);
                if(node.right != null) tmp.add(node.right);
            }
            queue = tmp;
            res++;
        }
        return res;
    }
}

总结:

        有的人说上本科没有太大的用处!其实这只是相对而言的!在学生时代!第一,你没办法和别人比工作经验!第二,你没办法和别人比社会阅历!第三,你和别人所处的时代不一样!就比如:马云毕业于杭州师范,那每个毕业于杭州师范的学生都能像马云一样优秀?很显然!是不一定的!如果现在有小伙伴还在学校上学,就一定要明确目标,把本科上出来,然后利用寒暑假或者课余时间多学习一些技术来弥补技术方面的缺陷!现在的大学中都不会教太深的东西,基本上都只是一些偏理论的基础知识!所以学习一定得靠自己了!如果已经是专科,那就狠狠恶补技术!拼学历拼不过那就拼技术经验和社会阅历,这样我们也会有很好的发展!如果有机会还是通过成人教育升一下本科的第二学历,它也可以在一定程度上可以帮助我们度过瓶颈期!
        最后,愿我们都能在各行各业中能够取得不同的成就!能够用自身的所学知识为国家贡献出自己的一份力量!一起加油!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值