当前位置: 首页 > news >正文

Leetcode 刷题记录 11 —— 二叉树第二弹

本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答,01~07为C++语言,08及以后为Java语言。

01 二叉树的层序遍历

在这里插入图片描述

在这里插入图片描述

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public List<List<Integer>> levelOrder(TreeNode root) {//广度优先遍历 queue//1.创建queue集合,添加首节点Deque<TreeNode> queue = new LinkedList<>();queue.offer(root);//2.遍历tree,取出结点,判断结点情况List<List<Integer>> results = new ArrayList<>();if(root == null){return results;}while(!queue.isEmpty()){int size = queue.size();List<Integer> result = new ArrayList<>();for(int i=0; i<size; i++){TreeNode node = queue.poll();result.add(node.val);//3.添加左右孩子结点if(node.left != null){queue.offer(node.left);}if(node.right != null){queue.offer(node.right);}}results.add(result);}return results;}
}

02 将有序数组转换为二叉搜索树

在这里插入图片描述

在这里插入图片描述

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public TreeNode sortedArrayToBST(int[] nums) {}
}

给定二叉搜索树的中序遍历,是否可以唯一地确定二叉搜索树?答案是否定的。

如果增加一个限制条件,即要求二叉搜索树的高度平衡,是否可以唯一地确定二叉搜索树?答案仍然是否定的。

直观地看,我们可以选择中间数字作为二叉搜索树的根节点,这样分给左右子树的数字个数相同或只相差 1,可以使得树保持平衡。

方法一:递归

BST 是 Binary Search Tree(二叉搜索树)的缩写。

class Solution {public TreeNode sortedArrayToBST(int[] nums) {//1.构建递归函数,传递左右区间参数return helper(nums, 0, nums.length-1);}public TreeNode helper(int[] nums, int left, int right){if(left > right){ //特殊情况判断return null;}//2.添加当前root结点int mid = (left + right) / 2;TreeNode root = new TreeNode(nums[mid]);//3.递归添加左右孩子结点root.left = helper(nums, left, mid - 1);root.right = helper(nums, mid + 1, right);return root;}
}

03 验证二叉搜索树

在这里插入图片描述

在这里插入图片描述

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public boolean isValidBST(TreeNode root) {}
}

这启示我们设计一个递归函数 helper(root, lower, upper) 来递归判断,函数表示考虑以 root 为根的子树,判断子树中所有节点的值是否都在 (l,r) 的范围内(注意是开区间)。如果 root 节点的值 val 不在 (l,r) 的范围内说明不满足条件直接返回,否则我们要继续递归调用检查它的左右子树是否满足,如果都满足才说明这是一棵二叉搜索树。

方法一:递归

class Solution {public boolean isValidBST(TreeNode root) {//1.构建递归函数,传递左右区间参数return helper(root, Long.MIN_VALUE, Long.MAX_VALUE);}public boolean helper(TreeNode root, long lower, long upper){if(root == null){ //特殊情况判断return true;}//2.判断当前root结点if(root.val <= lower || root.val >= upper){return false;}//3.递归判断左右孩子结点return helper(root.left, lower, root.val) && helper(root.right, root.val, upper);}
}

Long.MIN_VALUELong.MAX_VALUE是啥?

  • Long.MIN_VALUE-2^63,即 -9,223,372,036,854,775,808。代表 long 类型变量可以存储的最小值。
  • Long.MAX_VALUE2^63 - 1,即 9,223,372,036,854,775,807。代表 long 类型变量可以存储的最大值。

方法二:中序遍历

class Solution {public boolean isValidBST(TreeNode root) {if(root == null){return true;}//queue: 创建 -> 当前 -> 孩子//stack: 创建 -> 左移 -> 右移//1.创建Deque<TreeNode> stack = new LinkedList<>();long inorder = Long.MIN_VALUE;while(!stack.isEmpty() || root != null){//2.左移while(root != null){stack.push(root);root = root.left;}root = stack.pop();if(root.val <= inorder){return false;}inorder = root.val;//3.右移root = root.right;}return true;}
}

04 二叉搜索树中第K小的元素

在这里插入图片描述

在这里插入图片描述

方法一:递归

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {int flag1, flag2;public int kthSmallest(TreeNode root, int k) {flag1 = k;flag2 = 0;return inorder(root);}//1.创建public int inorder(TreeNode root){if(root == null){return -1;}//2.左右孩子结点int left = inorder(root.left);if(left != -1){return left;}flag2++;if(flag2 == flag1){return root.val;}//3.返回return inorder(root.right);}
}

方法二:栈

05 二叉树的右视图

在这里插入图片描述

在这里插入图片描述

/*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
class Solution {public List<Integer> rightSideView(TreeNode root) {}
}

方法一:深度优先搜索(stack)

class Solution {public List<Integer> rightSideView(TreeNode root) {Map<Integer, Integer> map = new HashMap<>(); //深度,valint maxDepth = -1; //标记//1.创建并加入Deque<TreeNode> nodeStack = new LinkedList<>();Deque<Integer> depthStack = new LinkedList<>();nodeStack.push(root);depthStack.push(0);while(!nodeStack.isEmpty()){//2.弹出并判断TreeNode node = nodeStack.pop();int depth = depthStack.pop();if(node != null){maxDepth = Math.max(maxDepth, depth); //标记更新//⭐if(!map.containsKey(depth)){map.put(depth, node.val);}//3.左右孩子结点nodeStack.push(node.left);nodeStack.push(node.right);depthStack.push(depth + 1);depthStack.push(depth + 1);}}//⭐List<Integer> result = new ArrayList<>();for(int i=0; i<=maxDepth; i++){result.add(map.get(i));}return result;}
}

Deque是啥意思?

DequeDouble Ended Queue(双端队列) 的缩写,是一个接口,常用实现有LinkedListArrayDeque,既可以用作队列(先进先出),也可以用作(后进先出)。

② 这个代码是怎么保证每次栈弹出的都是没加入map的最右端结点?

因为栈是后进先出,右孩子会先被弹出访问。所以在访问该层节点时,第一个被访问到的节点是右孩子,也就是该层最右边的节点。

③ 为什么maxDepth 赋值为0,会出现解答错误?

  • 当树为空时,没有层被访问,maxDepth 应该还是未初始化状态。
  • 如果用 int maxDepth = 0;,说明认为至少访问了一层深度(深度0)
  • 代码循环从 0maxDepth,会执行一次。
  • 但实际上没有节点,也没往map中放任何数据,所以map.get(0)会返回null,结果列表加了一个null
  • 导致与预期的空列表([])不符。

方法二:广度优先搜索(queue)

class Solution {public List<Integer> rightSideView(TreeNode root) {Map<Integer, Integer> rightmostValueAtDepth = new HashMap<Integer, Integer>();int max_depth = -1;Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>();Queue<Integer> depthQueue = new LinkedList<Integer>();nodeQueue.add(root);depthQueue.add(0);while (!nodeQueue.isEmpty()) {TreeNode node = nodeQueue.remove();int depth = depthQueue.remove();if (node != null) {// 维护二叉树的最大深度max_depth = Math.max(max_depth, depth);// 由于每一层最后一个访问到的节点才是我们要的答案,因此不断更新对应深度的信息即可rightmostValueAtDepth.put(depth, node.val);nodeQueue.add(node.left);nodeQueue.add(node.right);depthQueue.add(depth + 1);depthQueue.add(depth + 1);}}List<Integer> rightView = new ArrayList<Integer>();for (int depth = 0; depth <= max_depth; depth++) {rightView.add(rightmostValueAtDepth.get(depth));}return rightView;}
}

为什么最后访问到的才是最右边节点?

  • rightmostValueAtDepth.put(depth, node.val); 不断地更新当前深度对应的节点值。
  • 访问顺序是每层从左到右,意味着:
    • 最开始访问的节点会写入该层的值。
    • 后面访问的节点会覆盖之前的值。
    • 因为是从左往右访问,最后访问的节点就是最右节点。
  • 最终 “rightmostValueAtDepth” 保存的,正是每一层的最右边节点值。

相关文章:

  • 获取 Stream 对象的方式
  • 内存管理(第五、六章)
  • RocketMQ 深度解析:消息中间件核心原理与实践指南
  • AUTOSAR图解==>AUTOSAR_SRS_ICUDriver
  • 关于 Web 安全:5. 认证绕过与权限控制分析
  • 前端面经-虚幻引擎5
  • 嵌入式项目之QT页面制作
  • Python笔记:windows下编译python3.8.20
  • 股票程序化交易-使用python获取新浪财经期货行情数据
  • 如何理解Pytorch中前向传播的计算过程
  • dify-plugin-daemon的.env配置文件
  • Java 流程控制:从「小白」到「能用」的 while 循环指南
  • DAY34
  • 市场需求文档撰写
  • 超大数值减法
  • 解决论文中字体未嵌入的问题
  • STM32中的SPI通信协议
  • SprigBoot整合rocketmq-v5-client-spring-boot
  • CMake从入门到实战:现代C++项目构建指南
  • Android组件化框架设计与实践
  • 微网站建设高端网站定制/网站推广途径和推广要点有哪些?
  • excel做网站二维码/木卢seo教程
  • 外国优秀设计网站/上海优化seo
  • 做网站的商标是哪类/优化大师win7官方免费下载
  • 贵州建网站/全球网站访问量排名
  • 手机能制作网站吗/推广任务接单平台