最近在学习算法,之前一直是薄弱项(数组双重循环暴力排序水平)。
学习的过程中有不少的提升,主要就是对数据结构的理解又加深了一层,各种结构的优缺点,为什么会有这种结构,适用的场景是什么。
除此以外,深感写算法题就像在做脑筋急转弯,一道题自己写,太难了~ 再一看答案,感觉我又行了,过了两天再一看,我当时怎么写的来着?
写这篇文章,纯粹就当做一份笔记吧,方便后续复习回顾使用,快速的对一些做过的题目加深印象。
二叉树理解
基础数据结构
二叉树的基本结构都知道。一个节点分左右子节点,只有两个叉所以叫二叉树。
二叉树在数据结构设计方面既可以使用链表,也可以使用数组。
链表
数组
两种的区别,本质上还是数组和链表的区别。
数组要求空间连续,如果不是满二叉树,会有很多的空间浪费。
链表则不要求空间连续,实现上会好很多。但是也缺少了下标随机访问的特性。
通常在LeetCode中解题使用的都是链表形式。
二叉树的遍历
二叉树的遍历是树相关算法中基础的基础,必须要记住。
通常我们遍历会有两种形式:
- 一种先从上到下,再从左到右。(深度优先)
- 一种先从左到右,再从上到下。(广度优先)
在看遍历之前先统一几个概念:
中间节点:树的节点。根节点,子节点我们都称为中间节点。
左/右子节点: 相对于中间节点来说,都有左/右子节点。
深度优先
基于中间节点的顺序,存在三种深度优先的遍历方式。
前序遍历:对于树中的任意节点来说,先打印中间节点,然后再打印它的左子节点,最后打印它的右子节点。(中间节点在前,所以为前序)
中序遍历:对于树中的任意节点来说,先打印它的左子节点,然后再打印中间节点,最后打印它的右子节点。(中间节点在中,所以为中序)
后续遍历:对于树中的任意节点来说,先打印它的左子节点,然后再打印它的右子节点,最后打印这个节点本身。(中间节点在后,所以为后序)
具体顺序如下图所示:
广度优先
广度优先遍历也叫层级遍历。就是按照从左至右,从上到下的顺序。
结合上图顺序就是:A -> B -> C -> D -> E -> F -> G
代码实现
深度优先
以前序遍历为例:先中间节点 -> 左子节点 -> 最后右子节点。从图中发现有两个特点:
- 从根节点开始,输出中间节点,然后输出左子节点,在输出左子节点的同时,也相当于输出了中间节点。也就是说从根节点开始,一直输出左子节点,直到叶子结点为止,我们就已经完成了中间节点、左子节点的输出。
- 左子节点只要存在,就先不找右子节点。不存在,则从最后一个左子节点开始,找右子节点输出。且右子节点作为中间节点情况时,也需要遵循先左后右顺序。
有些东西越解释越迷糊,先看代码吧!
// TreeNode的实现 在文字最后有
public void printTree(TreeNode node) {
if (node == null) {
return;
}
// 1. 前序 先输出当前节点
System.out.println(node.val);
// 2. 再输出 左子节点
this.printTree(node.left);
// 3. 再输出右子节点
this.printTree(node.right);
}
代码看起来很简单。利用递归的思想,将复杂问题进行拆分,对于二叉树而言,我们没有必要看那么多层,只需要看两层即可。根节点 -> 左子节点 -> 右子节点
中序、后续也是一样,无非就是 1. 2. 3. 部分代码的位置顺序而已。
前序: 1 -> 2 -> 3
中序: 2 -> 1 -> 3
后续: 2 -> 3 -> 1
除了利用递归实现以外,我们还会通过迭代来去实现,代码如下:
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
// 使用栈结构(先进后出)来记录节点
Deque<TreeNode> queue = new LinkedList();
while (root != null || !queue.isEmpty()) {
while (root != null) {
res.add(root.val);
queue.offerFirst(root);
root = root.left;
}
// 在左子节点为空时切换为右子节点,继续执行
root = queue.remove();
root = root.right;
}
return res;
}
这里可以思考一下,为什么有了递归还要用迭代,两者有什么区别呢?哪种更好呢?
常见算法题
二叉树
前序遍历(禁止使用递归)
前序的顺序: 中间节点 -》 左子节点 -》 右子节点
- 中间节点不为空,输出当前节点。(当前节点)
- 中间节点为左子节点。(左子节点)
- 中间节点为空,需要输出右侧节点。
重点:
- 使用队列记录当前节点,便于获取右侧节点。
- 记录当前节点的队列需要用栈结构,先进后出。
private static void qianxu1(TreeNode root) {
List<String> res = new ArrayList<>();
// 使用队列记录节点顺序,便于获取右侧子节点
Deque<TreeNode> deque = new LinkedList();
TreeNode node = root;
// 当前节点可能为空 为空时 需要从队列中 获取新的节点
while (!deque.isEmpty() || node != null) {
while (node != null) {
// 当前节点不为空 加入结果集
res.add(node.getVal());
// 记录当前节点,使用先进后出方式。从头部入队
deque.offerFirst(node);
node = node.getLeft();
}
// 使用先进后出方式。从头部弹出
TreeNode right = deque.pop();
node = right.getRight();
}
System.out.println(res);
}
层级遍历
按照层级逐层输出。
- 判断当前节点是否存在 子节点,存在则按照数据加入队列。先进先出。
- 每次从队列中弹出一个节点,重复步骤1,直到队列没有元素
重点:
如果需要感知第几层,则使用额外变量记录每一层子节点数量。由于每次循环从队列弹出节点,当一层节点弹出完毕后,重新获取队列中节点数量即为新的一层数量。
无需感知层级
private static void iteratorTree(TreeNode node) {
List<String> result = new ArrayList<>(16);
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(node);
while (queue.size() > 0) {
TreeNode now = queue.remove();
if (now.getLeft() != null) {
queue.add(now.getLeft());
}
if (now.getRight() != null) {
queue.add(now.getRight());
}
result.add(now.getVal());
}
System.out.println(result);
}
需要感知层级
private static void iteratorTree1(TreeNode node) {
List<List<String>> result = new ArrayList<>(16);
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(node);
while (queue.size() > 0) {
int size = queue.size();
List<String> item = new ArrayList<>();
while (size > 0) {
TreeNode now = queue.remove();
if (now.getLeft() != null) {
queue.add(now.getLeft());
}
if (now.getRight() != null) {
queue.add(now.getRight());
}
item.add(now.getVal());
size--;
}
result.add(item);
}
System.out.println(result);
}
锯齿遍历
题目
给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:[[3],[20,9],[15,7]] 示例 2:
输入:root = [1] 输出:[[1]] 示例 3:
输入:root = [] 输出:[]
提示:
树中节点数目在范围 [0, 2000] 内 -100 <= Node.val <= 100
思路:
- 按照层级遍历逻辑。区别就是使用交替方式添加左、右节点。不够好!!
锯齿实际是值的顺序,如果从节点顺序入手,虽然也可以实现,但是没有值击本质。
所以直接从值方面入手,使用值的头插或者尾插方式实现。
实现
private static void iteratorTree2(TreeNode node) {
List<List<String>> result = new ArrayList<>();
Queue<TreeNode> queue = new LinkedList();
queue.add(node);
int size = 1;
boolean isLeft = true;
Deque<String> levelVal = new LinkedList<>();
while (queue.size() > 0) {
TreeNode now = queue.remove();
if (now.getLeft() != null) {
queue.add(now.getLeft());
}
if (now.getRight() != null) {
queue.add(now.getRight());
}
if (isLeft) {
levelVal.offerLast(now.getVal());
} else {
levelVal.offerFirst(now.getVal());
}
size--;
if (size == 0) {
size = queue.size();
isLeft = !isLeft;
result.add(new ArrayList<>(levelVal));
levelVal = new LinkedList<>();
}
}
System.out.println(result);
}
最大深度
最大深度其实就是逐层遍历。可以按层级遍历,也可以直接递归找节点深度。
在逐层遍历的基础上,维护一个层数变量。
最小深度
题目
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:2
示例 2:
输入:root = [2,null,3,null,4,null,5,null,6]
输出:5
提示:
- 树中节点数的范围在
[0, 105]内 -1000 <= Node.val <= 1000
思路
最小深度有一个前提,如果根节点的左子节点为空,则使用右侧节点最小深度。如果都为空,则最小深度为1。
该题可以考虑使用递归实现。判断左右子节点哪个深度最小。
返回条件为:根节点为空,或者两个节点都为空。
- 当前节点(根节点)不为空
- 左右子节点不为空。
- 左子节点不为空,递归获取左子节点最小深度。
- 右子节点不为空,递归获取右子节点最小深度。
- 取最小值 + 1 返回。( +1 表示层级)
实现(待完善)
此法效率不高
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
if (root.left == null && root.right == null) {
return 1;
}
int minCount = Integer.MAX_VALUE;
if (root.left != null) {
minCount = Math.min(minDepth(root.left), minCount);
}
if (root.right != null) {
minCount = Math.min(minDepth(root.right), minCount);
}
return minCount + 1;
}
// 解答成功:
// 执行耗时:9 ms,击败了34.19% 的Java用户
// 内存消耗:64.4 MB,击败了5.14% 的Java用户
路径总和
题目
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
示例 2:
输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。
示例 3:
输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。
提示:
- 树中节点的数目在范围
[0, 5000]内 -1000 <= Node.val <= 1000-1000 <= targetSum <= 1000
思路
路径总和相当于找节点值。以题目实例1为例
- 首先验证根节点是否为空。不为空 总和 - 根节点值 =》 22 - 5
- 根节点左/右子节点不为空,继续去左右子节点检索,检索值为 17
- 如果左右子节点为空,判断 22 - 5 是否为0,为0也返回true。
实现
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null) {
return false;
}
int sum = targetSum - root.val;
if (root.left == null && root.right == null) {
return sum == 0;
}
if (hasPathSum(root.left, sum) || hasPathSum(root.right, sum)) {
return true;
}
return false;
}
两颗二叉树是否一致
是否一致的关键在于比对值是否一样。
可以采用深度优先(递归),也可以采用广度优先(迭代)。
思路
深度优先:
-
比对两个节点。
- 如果都为空,则一致
- 如果都不为空。比对值是否一样
- 如果一个为空,一个不为空则不一致
-
继续比对左侧节点
-
继续比对右侧节点
public static boolean validConsistent(TreeNode treeNode1, TreeNode treeNode2) {
// 判断是否都为null
if (treeNode1 == null && treeNode2 == null) {
return true;
}
// 按断是否 一个 为null 一个不为 null
if (treeNode1 == null || treeNode2 == null) {
return false;
}
// 判断值是否一致
if (!treeNode1.getVal().equals(treeNode2.getVal())) {
return false;
}
// 判断子节点是否一致
return validConsistent(treeNode1.getLeft(), treeNode2.getLeft()) && validConsistent(treeNode1.getRight(), treeNode2.getRight());
}
有序数组转二叉搜索树
题目
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
示例 1:
输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:
示例 2:
输入:nums = [1,3]
输出:[3,1]
解释:[1,null,3] 和 [3,1] 都是高度平衡二叉搜索树。
提示:
1 <= nums.length <= 104-104 <= nums[i] <= 104nums按 严格递增 顺序排列
思路
题目要求绝对平衡。而且提供的是有序的数组。所以思路为:取数组中间值作为节点值方式。
比如:
- 先取数组中间值为根节点值。
- 数组 0 ~ 中间值,范围内,再取中间值 作为左侧节点值。
- 数组 中间值 ~ 数组尾部 范围内,再取中间值 作为右侧节点值。
- 重复上述步骤,每次只取中间值作为节点值。
实现
public TreeNode sortedArrayToBST(int[] nums) {
TreeNode root = new TreeNode();
return arrayToBst(nums, 0, nums.length -1);
}
public TreeNode arrayToBst(int[] nums, int left, int right) {
if (left > right) {
return null;
}
int mid = left + (right - left) / 2;
TreeNode leftNode = arrayToBst(nums, left, mid - 1);
TreeNode rightNode = arrayToBst(nums, mid+1, right);
return new TreeNode(nums[mid], leftNode, rightNode);
}
平衡二叉树
题目
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:true
示例 2:
输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例 3:
输入:root = []
输出:true
提示:
- 树中的节点数在范围
[0, 5000]内 -104 <= Node.val <= 104
Related Topics
树
深度优先搜索
二叉树
思路
判断是否平衡,其实就是看左右子节点深度差是否大于1。
- 获取左右节点深度
- 比对深度差。
实现
错误实现;
public boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
}
// 获取 左右 子节点最大深度 然后进行比对的方式 不完善
int left = validBalanced(root.left);
int right = validBalanced(root.right);
return Math.abs(left - right) <= 1;
}
// 获取最大深度
public int validBalanced(TreeNode node) {
if (node == null) {
return 0;
}
int left = validBalanced(node.left);
int right = validBalanced(node.right);
return Integer.max(left, right) + 1;
}
正确实现:
public boolean isBalanced(TreeNode root) {
if (root == null) {
return true;
}
int left = validBalanced(root);
return left >= 0;
}
public int validBalanced(TreeNode node) {
if (node == null) {
return 0;
}
int left = validBalanced(node.left);
if (left < 0) {
return left;
}
int right = validBalanced(node.right);
if (right < 0) {
return right;
}
// 这里需要注意 差值的绝对值 > 1
if (Math.abs(left - right) > 1) {
return -1;
}
// 这里需要注意返回最大值 如果范围最小值 那就都是 0
return Integer.max(left, right) + 1;
}
左右节点对称
左侧节点的外侧节点与右侧节点的外侧节点一致。我们认为其对称。
此类问题可以考虑递归实现。为什么可以用递归?节点比对方式是一致的。
- 从根节点的左右节点开始比对。
- 先判断是否都不为空。
- 判断值是否一致。
- 然后递归比对当前节点的左侧节点是否一致。
- 然后递归比对当前节点的右侧节点是否一致。
private static boolean validSymmetry(TreeNode left, TreeNode right) {
if (left == null && right == null) {
return true;
}
if (left == null || right == null) {
return false;
}
if (!left.getVal().equals(right.getVal())) {
return false;
}
return validSymmetry(left.getLeft(), right.getRight()) && validSymmetry(left.getRight(), right.getLeft());
}
所有路径
返回二叉树从根节点到所有叶子节点路径。
使用递归方式,从根节点开始,依次将路径往下(左右子节点)传递,并拼装,直到子节点为空,将结果存储。
- 判断根节点不为空。如果节点为空,将路径加入结果集。
- 处理当前节点路径,
- 如果左子节点不为空,递归获取左子节点路径。
- 如果右子节点不为空,递归获取右子节点路径。
private static void printTreePath(TreeNode node, String path, List<String> result) {
// 可以单独提取方法 处理根节点 以及 初始化结果集
if (node == null) {
return;
}
path = path + " -> " + node.val;
if (node.left == null && node.right == null) {
result.add(path);
return;
}
if (node.left != null) {
printTreePath(node.left, path, result);
}
if (node.right != null) {
printTreePath(node.right, path, result);
}
}
二叉树反转
左子节点变右子节点。
可以采用逐层遍历方式,自上而下翻转。
也可以采用前序遍历方式,自下而上翻转。
也可以采用递归方式,自左向右翻转。
// 使用逐层遍历方式
private static TreeNode reverseTree(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
while (queue.size() > 0) {
int size = queue.size();
while (size > 0) {
size--;
TreeNode node = queue.remove();
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
}
}
return root;
}
// 使用前序遍历 方式
private static TreeNode reverseTree1(TreeNode root) {
Deque<TreeNode> queue = new LinkedList();
TreeNode node = root;
while (queue.size() > 0 || node != null) {
while (node != null) {
queue.offerFirst(node);
node = node.left;
}
TreeNode temp = queue.pop();
node = temp.right;
temp.right = temp.left;
temp.left = node;
}
return root;
}
// 递归方式
private static TreeNode reverseTree2(TreeNode node) {
if (node == null) {
return null;
}
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
reverseTree2(node.right);
reverseTree2(node.left);
return node;
}
最底层的最左侧值
该题本质还是层次遍历。
我们再层次遍历时会使用一个队列,这个队列按照层级入队,出队。而每次最后一个出队的就是最底层,最右侧节点。
相对于此题我们简单变换一下,让最左侧的节点最后出队即可。
因此入队的顺序需要调整为先右后左。
搜索树
搜索树最大的特点就是有顺序。左 -》 中 -》 右 (中序遍历)
查找节点
查找节点只需要遵循,如果当前节点值 < 查找值,去左子节点查询,否则去右子节点查询。如果最后节点为空表示不存在查找值。
验证搜索树
验证一棵树是否真正的有序。
该题其实就是按层次遍历树,在遍历的同时验证是否有序。开始以为只需要验证节点左中右顺序即可。但是实际上需要保证当前节点的左子节点值全部小于当前节点,右侧节点值全部大于当前节点。
比如上图,15的左子节点为8,小于 15 的父级节点 10 ,此树不为搜索树!!!
因此对于搜索树的判断,不能仅仅通过左中右顺序来判断,还需要考虑是否大于/小于父级节点。
这里需要记住一点,搜索树按照中序遍历是顺序递增的,满足此情况即为搜索树。
思路
搜索树是绝对有序的。这个等价于,搜索树使用中序遍历时也是绝对有序的。
所以验证是否为搜索树:
- 先取一个最小值作为上一个节点的值。
- 使用中序遍历,每次与上一个节点的值进行比对,必须要大于上个节点(如果小于等于上个节点则 不是搜索树)
错误示例1
private static TreeNode validSearchTree(TreeNode root) {
Deque<TreeNode> queue = new LinkedList<>();
// 这个赋值操作没有必要
TreeNode node = root;
// 这里使用 int的最小值不合适。 题目提示已经说明,树内值可能为 int 类型的最小值
Integer lastVal = Integer.MIN_VALUE;
while (queue.size() > 0 || node != null) {
while (node != null) {
queue.offerFirst(node);
node = node.left;
}
node = queue.remove();
// 这里没有考虑 等于的情况 子节点值 与 父节点值相等
if (node.getIntVal() < lastVal) {
return node;
}
lastVal = node.getIntVal();
node = node.right;
}
return root;
}
本质还是边界值考虑问题: 边界值
- 如果只有一个节点
- 如果父子节点值相等
public boolean isValidBST(TreeNode root) {
Deque<TreeNode> queue = new LinkedList<>();
long lastVal = Long.MIN_VALUE;
while (queue.size() > 0 || root != null) {
while (root != null) {
queue.offerFirst(root);
root = root.left;
}
root = queue.remove();
if (root.val <= lastVal) {
return false;
}
lastVal = root.val;
root = root.right;
}
return true;
}
错误示例2
public static void main(String[] args) {
TreeNode res = validSearchTree1(root, Integer.MIN_VALUE);
}
private static TreeNode validSearchTree1(TreeNode node, Integer pre) {
if (node == null) {
return node;
}
TreeNode errNode;
if ((errNode = validSearchTree1(node.left, pre)) != null) {
return errNode;
}
System.out.println(pre);
if (node.getIntVal() <= pre) {
System.out.println(false);
return node;
}
pre = node.getIntVal();
if ((errNode = validSearchTree1(node.right, pre)) != null) {
return errNode;
}
return null;
}
错误位置:
- Integer 作为最小值。
- Integer参数传递属于引用传递。被调用者修改值不会对调用者产生影响。
class Solution {
// 使用全局变量 并用 long 类型作为最小值
long prev = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
if (!isValidBST(root.left)) {
return false;
}
if (root.val <= prev) {
return false;
}
prev = root.val;
return isValidBST(root.right);
}
}
补充
TreeNode结构定义
public class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode() {
}
public TreeNode(int val) {
this.val = val;
}
public TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
/**
* 7
* 5 10
* 3 6 8 9
* 1 2 4 12
* 0
*/
public static TreeNode initNode() {
TreeNode node0 = new TreeNode(0);
TreeNode node1 = new TreeNode(1, node0, null);
TreeNode node2 = new TreeNode(2);
TreeNode node3 = new TreeNode(3, node1, node2);
TreeNode node4 = new TreeNode(4);
TreeNode node6 = new TreeNode(6, node4, null);
TreeNode node5 = new TreeNode(5, node3, node6);
TreeNode node12 = new TreeNode(12);
TreeNode node8 = new TreeNode(8);
TreeNode node9 = new TreeNode(9, null, node12);
TreeNode node10 = new TreeNode(10, node8, node9);
TreeNode root = new TreeNode(7, node5, node10);
return root;
}
/**
* 7
* 5 5
* 3 6 6 3
* 1 1
*
*/
public static TreeNode initSymmetryNode() {
TreeNode node1l = new TreeNode(1);
TreeNode node1r = new TreeNode(1);
TreeNode node3l = new TreeNode(3, node1l, null);
TreeNode node3r = new TreeNode(3, null, node1r);
TreeNode node6l = new TreeNode(6);
TreeNode node6r = new TreeNode(6);
TreeNode node5l = new TreeNode(5, node3l, node6l);
TreeNode node5r = new TreeNode(5, node6r, node3r);
TreeNode root = new TreeNode(7, node5l, node5r);
return root;
}
/**
* 20
* 10 30
* 5 15 25 35
* 3 9 14 40
* 1
*/
public static TreeNode initSearchNode() {
TreeNode node1 = new TreeNode(1);
TreeNode node3 = new TreeNode(3, node1, null);
TreeNode node9 = new TreeNode(9);
TreeNode node5 = new TreeNode(5, node3, node9);
TreeNode node14 = new TreeNode(8);
TreeNode node15 = new TreeNode(15, node14, null);
TreeNode node10 = new TreeNode(10, node5, node15);
TreeNode node40 = new TreeNode(40);
TreeNode node35 = new TreeNode(35, null, node40);
TreeNode node25 = new TreeNode(25);
TreeNode node30 = new TreeNode(30, node25, node35);
TreeNode root = new TreeNode(20, node10, node30);
return root;
}
}
迭代与递归比对
在二叉树遍历中,我们有两种方式,递归和迭代,对于两者效率,肯定是迭代要优于递归。因为递归属于方法级别的调用,在JVM中方法调用是需要在本地方法栈中进行记录的,而且如果出现深层递归,还会抛出StackOverFlowError。所以通常我们建议使用迭代代替递归。
当然,相对于递归来讲,迭代的实现通常要比递归麻烦很多,如果非深层次的处理,也是可以使用递归的。比如下方两种leetcode 提交记录。递归的内存消耗反而要高于迭代,原因想必也是迭代使用了额外的变量。
递归:执行时间 0ms, 内存 39.5 MB
迭代: