LeetCode入门指南 之 二叉树
二叉树的遍历
递归:
void traverse (TreeNode root) {
if (root == null) {
return null;
}
//前序遍历位置
traverse(root.left);
//中序遍历位置
traverse(root.right);
//后序遍历位置
}
144. 二叉树的前序遍历
前序非递归:
public static List<Integer> preOrder(TreeNode root) {
if (root == null) {
return null;
}
List<Integer> res = new LinkedList<>();
Deque<TreeNode> stack = new LinkedList<>();
stack.push(root);
while (!stack.isEmpty()) {
// 先遍历根结点
TreeNode node = stack.pop();
res.add(node.val);
// 打印顺序为:根 左 右,因此先将右子结点入栈
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return res;
}
94. 二叉树的中序遍历
中序非递归:
public static List<Integer> infixOrder(TreeNode root) {
if (root == null) {
return null;
}
List<Integer> res = new LinkedList<>();
TreeNode temp = root;
Deque<TreeNode> stack = new LinkedList<>();
while (temp != null || !stack.isEmpty()) {
while (temp != null) {
stack.push(temp); // 加入栈
temp = temp.left; // 到最左边结点停止
}
temp = stack.pop(); // 访问栈顶元素
res.add(temp.val);
temp = temp.right; //下一个遍历的元素是temp的右子树的最左边结点
}
return res;
}
145. 二叉树的后序遍历
后序非递归:
// 后序可参照前序:后序为:左右根,我们只需按照:根右左遍历然后翻转即可
public static List<Integer> postOrder(TreeNode root) {
if (root == null) {
return null;
}
LinkedList<Integer> res = new LinkedList<>();
Deque<TreeNode> stack = new LinkedList<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode temp = stack.pop();
// 每次添加到头,最后输出的结果自然为:根右左的逆序
res.addFirst(temp.val);
if (temp.left != null) {
stack.push(temp.left);
}
if (temp.right != null) {
stack.push(temp.right);
}
}
return res;
}
注意:如果非递归解法难以理解,可以先按照上面的代码结合案例手推一下。重要的还是要先形成模板并记忆,间隔着多做几次也就慢慢理解了。
102. 二叉树的层序遍历
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
/**
* 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) {
List<List<Integer>> res = new LinkedList<>();
if (root == null) {
return res;
}
// 创建队列并加入头结点
Deque<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
// 获取当前层的结点个数
int size = queue.size();
List<Integer> rowList = new LinkedList<>();
// 将当前层结点按照先进先出(从左至右)的方式出队,同时将非空子结点加入队尾
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
rowList.add(node.val);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
res.add(rowList);
}
return res;
}
}
104. 二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
lass Solution {
/**
* 定义:
* 返回以root为根结点的最大深度
*/
public int maxDepth(TreeNode root) {
//base case, root为空说明树的高度为0,退出递归
if (root == null) {
return 0;
}
/**
* 根据定义,就根节点来说,树的最大深度为:
* 左右子树的最大深度中的最大值 + 1
*/
int left = maxDepth(root.left);
int right = maxDepth(root.right);
return 1 + Math.max(left, right);
}
}
- 二叉树相关的很多题目都是由二叉树的三种递归遍历演化而来
- 此题其实就是二叉树的后序遍历演化而来,要知道当前二叉树的最大深度自然要先知道两棵子树的最大深度,因此用后序遍历(自底向上)
- 编写递归程序切记不要用脑袋模拟递归栈,函数定义好后,根据定义编写代码即可
110. 平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
class Solution {
//先假定是平衡二叉树
private boolean balance = true;
public boolean isBalanced(TreeNode root) {
height(root);
return balance;
}
//后序遍历而来,自底向上获取两棵子树的高度,并检查节点左右子树高度只差是否小于等于1
private int height(TreeNode root) {
if (root == null) {
return 0;
}
int left = height(root.left);
int right = height(root.right);
if (Math.abs(left - right) > 1) {
balance = false;
}
return Math.max(left, right) + 1;
}
}
124. 二叉树中的最大路径和
路径 被定义为一条从树中任意节点出发,沿父节点 - 子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点
root,返回其 最大路径和 。
/**
* 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 int maxPathSum(TreeNode root) {
maxSumToDescendant(root);
return maxPathSum;
}
//先将 树的最大路径和 初始化为最小值
private int maxPathSum = Integer.MIN_VALUE;
// 函数定义:当前结点到 子孙(不一定包含叶子结点) 结点的最大路径和(最少为其自身一个结点)
private int maxSumToDescendant(TreeNode root) {
if (root == null) {
return 0;
}
// 小于0则认为对最大路径和没有贡献
int left = Math.max(0, maxSumToDescendant(root.left));
int right = Math.max(0, maxSumToDescendant(root.right));
// 自底向上返回的过程中顺带计算 树的最大路径和(当前结点到左边子孙结点的最大路径和 + 当前结点 + 当前结点到右边子孙结点的最大路径和)
maxPathSum = Math.max(maxPathSum, left + root.val + right);
return root.val + Math.max(left, right);
}
}
236. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
思路:后序遍历演变而来,若找到其中一个结点就自底向上返回。若p、q互不为对方子树中的结点,p、q最终会在某个结点相遇,该节点就为最近公共祖先;否则p或q即为最近公共结点。
class Solution {
//重要已知:p != q
// p 和 q 均存在于给定的二叉树中。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) {
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) {
return root;
}
return left != null ? left : right;
}
}
107. 二叉树的层序遍历 II
给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
LinkedList<List<Integer>> res = new LinkedList<>();
if (root == null) {
return res;
}
Deque<TreeNode> queue = new LinkedList<>();
queue.offerLast(root);
while (!queue.isEmpty()) {
//获取当前层的结点数量
int size = queue.size();
//暂存当前层的所有结点
List<Integer> tempList = new LinkedList<>();
TreeNode tempNode;
for (int i = 0; i < size; i++) {
tempNode = queue.poll();
tempList.add(tempNode.val);
if (tempNode.left != null) {
queue.offer(tempNode.left);
}
if (tempNode.right != null) {
queue.offer(tempNode.right);
}
}
//将每一层的结果 逆序 放入最终的list中
res.addFirst(tempList);
}
return res;
}
}
103. 二叉树的锯齿形层序遍历
给定一个二叉树,返回其节点值的锯齿形层序遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new LinkedList<>();
if (root == null) {
return res;
}
Deque<TreeNode> queue = new LinkedList<>();
queue.offerLast(root);
boolean flag = true;
while(!queue.isEmpty()) {
//获取当前层的结点数量
int size = queue.size();
LinkedList<Integer> tempList = new LinkedList<>();
TreeNode tempNode;
for (int i = 0; i < size; i++) {
tempNode = queue.pollFirst();
if (flag == true) {
//从前往后,顺序存放
tempList.addLast(tempNode.val);
} else {
//从前往后,逆序存放
tempList.addFirst(tempNode.val);
}
if (tempNode.left != null) {
queue.offerLast(tempNode.left);
}
if (tempNode.right != null) {
queue.offerLast(tempNode.right);
}
}
res.add(tempList);
//每遍历完一层切换flag
flag = !flag;
}
return res;
}
}
二叉搜索树
98. 验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
/**
* 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) {
return isValidBST(root, null, null);
}
//定义:当前结点为根结点的二叉树是否为二叉搜索树。二叉搜索树的每个结点都有一个上下界(除了根节点)
private boolean isValidBST(TreeNode node, TreeNode low, TreeNode high) {
//base case
if (node == null) {
return true;
}
//base case,当前结点小于等于下界或大于等于上界都不满足二叉搜索树
if (low != null && node.val <= low.val) return false;
if (high != null && node.val >= high.val) return false;
boolean ret = isValidBST(node.left, low, node) && isValidBST(node.right, node, high);
return ret;
}
}
推荐题解:验证二叉搜索树(BST:给子树上所有节点都加一个边界)
701. 二叉搜索树中的插入操作
给定二叉搜索树(
BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
class Solution {
// 定义:将当前值插入以当前结点为根的二叉搜索树并返回根节点
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) {
return new TreeNode(val);
}
// 一定要根据定义来编写递归代码
if (root.val > val) {
// val 小于当前结点值则插入左子树
root.left = insertIntoBST(root.left, val);
} else {
root.right = insertIntoBST(root.right, val);
}
return root;
}
}
450. 删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
- 首先找到需要删除的节点;
- 如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
class Solution {
// 定义:删除当前结点为根结点的二叉搜索树中值为 key 的结点
public TreeNode deleteNode(TreeNode root, int key) {
// base case
if (root == null) {
return root;
}
// 切记根据定义编写递归代码
if (root.val == key) {
//base case, 删除结点是叶子结点或只是有一个子树的非叶结点
if (root.left == null) return root.right;
if (root.right == null) return root.left;
// 有两个子树的非叶子结点,用右子树的最小结点替换当前结点,然后删除右子树最小结点
TreeNode node = getMin(root.right);
root.val = node.val;
root.right = deleteNode(root.right, node.val);
// key 大于当前结点值 则根据定义在右子树中删除
} else if (root.val < key) {
root.right = deleteNode(root.right, key);
} else if (root.val > key){
root.left = deleteNode(root.left, key);
}
return root;
}
// 获取root为根的子树的最小结点(最左边结点)
private TreeNode getMin(TreeNode root) {
while(root.left != null) {
root = root.left;
}
return root;
}
}
LeetCode入门指南 之 二叉树的更多相关文章
- LeetCode入门指南 之 链表
83. 删除排序链表中的重复元素 存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 .返回同样按升序排列的结果链表. class Soluti ...
- LeetCode入门指南 之 排序
912. 排序数组 给你一个整数数组 nums,请你将该数组升序排列. 归并排序 public class Sort { //归并排序 public static int[] MergeSort(in ...
- LeetCode入门指南 之 栈和队列
栈 155. 最小栈 设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈. push(x) -- 将元素 x 推入栈中. pop() -- 删除栈顶的元素. top( ...
- LeetCode入门指南 之 回溯思想
模板 result = {} void backtrack(选择列表, 路径) { if (满足结束条件) { result.add(路径) return } for 选择 in 选择列表 { 做选择 ...
- LeetCode入门指南 之 动态规划思想
推荐学习labuladong大佬的动态规划系列文章:先弄明白什么是动态规划即可,不必一次看完.接着尝试自己做,没有思路了再回过头看相应的文章. 动态规划一般可以由 递归 + 备忘录 一步步转换而来,不 ...
- LeetCode入门指南 之 二分搜索
上图表示常用的二分查找模板: 第一种是最基础的,查找区间左右都为闭区间,比较后若不等,剩余区间都不会再包含mid:一般在不需要确定目标值的边界时,用此法即可. 第二种查找区间为左闭右开,要确定targ ...
- Web API 入门指南 - 闲话安全
Web API入门指南有些朋友回复问了些安全方面的问题,安全方面可以写的东西实在太多了,这里尽量围绕着Web API的安全性来展开,介绍一些安全的基本概念,常见安全隐患.相关的防御技巧以及Web AP ...
- Vue.js 入门指南之“前传”(含sublime text 3 配置)
题记:关注Vue.js 很久了,但就是没有动手写过一行代码,今天准备入手,却发现自己比菜鸟还菜,于是四方寻找大牛指点,才终于找到了入门的“入门”,就算是“入门指南”的“前传”吧.此文献给跟我一样“白痴 ...
- yii2实战教程之新手入门指南-简单博客管理系统
作者:白狼 出处:http://www.manks.top/document/easy_blog_manage_system.html 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文 ...
随机推荐
- linux下nginx访问ftp目录权限问题
在将nginx目录设置为ftp目录访问时会报错:403 forbidden 原因在于nginx访问时账户问题,通过修改nginx.conf中的访问名解决 打开nginx.conf 修改user值,去掉 ...
- MySQL 8.x 新版本特性赶紧学!!Linux 服务器上安装 MySQL 8.x
我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 引言 ...
- Leetcode春季打卡活动 第二题:206. 反转链表
Leetcode春季打卡活动 第二题:206. 反转链表 206. 反转链表 Talk is cheap . Show me the code . /** * Definition for singl ...
- 动态 DP
一道入门 DP + 修改 = 动态 DP. 以模板题为例,多次询问树的最大独立集,带修改. 先有 naive 的 DP,记 \(f_{u,0/1}\) 表示 \(u\) 点不选/选时以 \(u\) 为 ...
- BOM(Bill of Material)物料清单基础知识(一)
一.BOM的基础概念 概 ...
- 如何用C++自己实现mysql数据库的连接池?
为什么是mysql? 现在几乎所有的后台应用都要用到数据库,什么关系型的.非关系型的:正当关系的,不正当关系的:主流的和非主流的, 大到Oracle,小到sqlite,以及包括现在逐渐流行的基于物联网 ...
- Flask 之linux部署
1.装python > `[root ~]# yum install gcc [root ~]# wget https://www.python.org/ftp/python/3.6.5/Pyt ...
- Android系统编程入门系列之界面Activity响应多元的属性动画
在响应丝滑动画一篇文章中,分别介绍了作用于普通视图.绘制视图的绘制对象.和界面这三种对象的动画效果,但是都有一些使用的局限性.比如这些动画都只是以屏幕上绘制更新的方式绘制动画,并没有真实改变作用对象的 ...
- PySpider爬取去哪儿攻略数据项目
1 创建项目 点击WEB中的Create创建项目 填入相关项目名和其实爬取URL 创建后进入项目首页 右边 Handler 是pyspider的主类,整个爬虫一个Handler,其中可定义爬虫的爬取. ...
- 记一次针对excel导出的优化
最近发现我们系统导出excel文件时由于是导出百万级数据导出,速度过慢并且内存占用多,故进行了下面的一次优化. 我们使用apache的poi进行excel文件操作 主要耗时: 1.从数据库得到需要导出 ...