面试题53、54、55-Ⅰ、55-Ⅱ

面试题53-Ⅱ.0~n-1中缺失的数字

在这里插入图片描述

  • 根据题意,数组可以按照以下规则划分为两部分。

    • 左子数组: nums[i] = i;
    • 右子数组: nums[i] ≠ i;
  • 缺失的数字等于 “右子数组的首位元素” 对应的索引;因此考虑使用二分法查找 “右子数组的首位元素” 。

在这里插入图片描述

class Solution {
    public int missingNumber(int[] nums) {
    	if(nums.length < 1) return -1;
		int i = 0, j = nums.length - 1;
		while(i <= j) {
			int mid = i + (j - i) / 2;
			//若相等,则右子数组的首位元素一定在闭区间[m+1,j]中,因此执行 i = mid + 1
			if(nums[mid] == mid) i = mid + 1;
			//若不等,则左子数组的末位元素一定在闭区间[i,m-1]中,因此执行 j = mid - 1
			else j = mid - 1;
		}
		//跳出循环时,i指向右子树组首位元素,j指向左子数组末位元素
		return i;
    }
}
  • 时间复杂度 O(logN): 二分法为对数级别复杂度。
  • 空间复杂度 O(1): 几个变量使用常数大小的额外空间。

————————————————————————————————————————

面试题54、二叉搜索树的第k大节点

在这里插入图片描述
解题思路:

本文基于此性质:二叉搜索树的中序遍历为递增序列

  • 根据以上性质,易得二叉搜索树的中序遍历倒序为递减序列

  • 因此,求“二叉搜索树第 k 大的节点” 可转化为求 “此树的中序遍历倒序的第 k 个节点”。
    在这里插入图片描述
    其中需要注意的是:

  • 1.提前返回:若 k = 0,代表已找到目标节点,无序继续遍历,因此直接返回;

  • 2.记录结果:若 k = 0,代表当前节点为第 k 大的节点,因此记录 res = root.val;

class Solution {
    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);
    }
}
  • 时间复杂度 O(N) : 当树退化为链表时(全部为右子节点),无论 k 的值大小,递归深度都为 N ,占用 O(N) 时间。
  • 空间复杂度 O(N) : 当树退化为链表时(全部为右子节点),系统使用 O(N) 大小的栈空间。

————————————————————————————————————————

面试题55-Ⅰ.二叉树的深度

在这里插入图片描述

DFS

此树的深度和其左(右)子树的深度之间的关系。显然,此树的深度等于左子树的深度与右子树的深度中的最大值 +1

在这里插入图片描述
算法解析:

  • 1.终止条件: 当 root​ 为空,说明已越过叶节点,因此返回 深度 0 。

  • 2.递推工作: 本质上是对树做后序遍历。

    • 计算节点 root​ 的 左子树的深度 ,即调用 maxDepth(root.left);
    • 计算节点 root​ 的 右子树的深度 ,即调用 maxDepth(root.right);
  • 3.返回值: 返回此树的深度 ,即 max(maxDepth(root.left), maxDepth(root.right)) + 1。

class Solution {
	//计算root为根节点的树的深度
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        //左子树深度与右子树深度中的最大值 + 1
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}
  • 时间复杂度 O(N) : N 为树的节点数量,计算树的深度需要遍历所有节点。
  • 空间复杂度 O(N) : 最差情况下(当树退化为链表时),递归深度可达到 N 。

BFS

class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        Queue<TreeNode> queue = new LinkedList<>();
        int dep = 0;
        queue.add(root);
        while(!queue.isEmpty()) {
            int len = queue.size();
            for(int i = 0; i < len; i++) {
                TreeNode node = queue.poll();
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            //对当前层操作完,深度应该+1。操作一层,深度加一层
            dep++;
        }
        return dep;
    }
}
  • 时间复杂度 O(N) : N 为树的节点数量,计算树的深度需要遍历所有节点。
  • 空间复杂度 O(N) : 最差情况下(当树平衡时),队列 queue 同时存储 N/2 个节点。

————————————————————————————————————————

面试题55-Ⅱ.平衡二叉树

在这里插入图片描述

后序遍历-从底至顶

思路是对二叉树做后序遍历,从底至顶返回子树深度,若判定某子树不是平衡树则 “剪枝” ,直接向上返回。

class Solution {
    public boolean isBalanced(TreeNode root) {
		return dfs(root) != -1;
    }
    //计算root树的深度,若返回-1,则说明该树不是平衡二叉树,否则返回该树的深度
    public int dfs(TreeNode root) {
    	if(root == null) return 0;
    	int left = dfs(root.left); //计算左子树的深度
    	if(left == -1) return -1; //若为-1,说明左子树不是平衡二叉树,直接返回-1
    	int right = dfs(root.right); //计算右子树的深度
    	if(right == -1) return -1; //若为-1,说明右子树不是平衡二叉树,直接返回-1
    	//如果左右子树的深度差 < 2,则说明以该节点是平衡二叉树的头节点,则返回该节点的深度,否则返回-1
    	return Math.abs(left - rigth) < 2 ? Math.max(left, right) + 1 : -1;
    }
}
  • 时间复杂度 O(N): N 为树的节点数;最差情况下,需要递归遍历树的所有节点。
  • 空间复杂度 O(N): 最差情况下(树退化为链表时),系统递归需要使用 O(N) 的栈空间。

先序遍历-从顶至底

一个根节点为Node的子树要是一颗平衡树必须满足以下三个条件:

  • Node的左子树是平衡树

  • Node的右子树是平衡树

  • Node的左子树和右子树的深度差值小于等于1

abs(depth(root.left) - depth(root.right)) <= 1 :判断 当前子树 是否是平衡树;
isBalanced(root.left) : 先序遍历递归,判断 当前子树的左子树 是否是平衡树;
isBalanced(root.right) : 先序遍历递归,判断 当前子树的右子树 是否是平衡树;

class Solution {
	//判断当前树是否为平衡树
    public boolean isBalanced(TreeNode root) {
		if(root == null) return true;
		return Math.abs(depth(root.left) - depth(root.right)) <= 1 
			   && isBalanced(root.left) && isBalanced(root.right);
    }
    //计算root为根节点的树的深度
    public int depth(TreeNode root) {
    	if(root == 0) return 0;
    	//该树的深度 = 它左子树的深度和右子树的深度中较大的那个 + 1
    	return Math.max(depth(root.left), depth(root.right)) + 1;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值