我开了一个LeetCode会员,选择了一些高频率题目。这个系列是希望帮助大家每天花30分钟的题目了解面试高频题,让大家面试更加游刃有余。
本期我们讲解两道树的中等题。我们之前几期的推送一直在强调递归和迭代,之前讲解的题目递归都比迭代简单,那么本期首先带来的就是迭代比递归更容易的题目。本期所用的树的数据结构均如下:
class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
114. 二叉树展开为链表
题意:给定一棵树,根据前序遍历将其展开为一个【只有右子树有值的,左子树为null的】类似于链表的结构,如下图
拿上图给出的例题作分析,我们发现4这个结点的右子树结点应该是5,但是怎么去找到5呢?是不是要把5先存起来,然后4才能找到5。那我们第一反应就是用一个数据结构去存储结点。那这就是迭代的思路,数据结构就先从队列和栈考虑,一般来讲,广搜用队列,深搜用栈,这里的前序遍历显然是深搜,那么我们用栈。因为前序遍历的顺序是根节点->左->右,所以我们先把右子树结点入栈,再把左子树结点入栈,然后依次操作。
迭代比递归好的一点,就是能根据代码很好的演算,如果大家还没有搞懂上面的思路,就根据下面的代码演算一遍吧。
public void flatten(TreeNode root) {
if(root==null)
return;
Stack<TreeNode> stack=new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode treeNode=stack.pop();
if(treeNode.right!=null)
stack.push(treeNode.right);
if(treeNode.left!=null)
stack.push(treeNode.left);
treeNode.left=null;
if(stack.isEmpty())
treeNode.right=null;
else
treeNode.right=stack.peek();
}
}
那么这可以用递归做吗?答案是肯定的,递归的好处是代码简练、可读性高,但是比较难想、比较难演算,完整代码如下:
private TreeNode prev = null;
public void flatten(TreeNode root) {
if (root == null)
return;
flatten(root.right);
flatten(root.left);
root.right = prev;
root.left = null;
prev = root;
}
看上去是不是很简单,只要几行就搞定了。我一直觉得递归的逻辑很难想,除非是那种一眼就知道的。那其实一眼就知道的题目也是归功于自己练习的多,所以想完全熟悉递归的题目,那就多做吧!上面的代码其实是先找了最右边的一个结点,然后反向往前操作。
我们做了那么多关于树的递归题目,其实无非就是两点:1.先递归左子树还是先递归右子树;2.逻辑放在递归前还是逻辑放在递归后。我们之后会出一个总结专题来好好讲讲树的递归。
95. 最大二叉树
题意:
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
1.二叉树的根是数组中的最大元素。
2.左子树是通过数组中最大值左边部分构造出的最大二叉树。
3.右子树是通过数组中最大值右边部分构造出的最大二叉树。
4.通过给定的数组构建最大二叉树,并且输出这个树的根节点。
这道题其实是一道简单版的构造一棵“二叉搜索树”,只是这的“二叉搜索树”被题中规定的“最大树”替换了。这道题的递归方法是比较简单的,代码如下:
public TreeNode constructMaximumBinaryTree(int[] nums) {
return help(nums,0,nums.length-1);
}
TreeNode help(int[] nums,int start,int end){
if(start>end)
return null;
int maxNum=Integer.MIN_VALUE;
int flag=0;
for(int i=start;i<=end;i++){
if(nums[i]>=maxNum){
maxNum=nums[i];
flag=i;
}
}
TreeNode root=new TreeNode(maxNum);
root.left=help(nums, start, flag-1);
root.right=help(nums, flag+1, end);
return root;
}
这道题有难度的是迭代方法,我们说迭代方法一定是要有定义一个用于存储结点的数据结构的,那么这道题的数据结构应该是什么?从栈和队列里考虑,这道题是深搜,应该是栈。但我们尝试用栈做下去之后发现,需要用到一个特殊的栈——双向队列,这是一种具有队列和栈的性质的数据结构。迭代的另一个难点是入栈(队列)和出栈(队列)的顺序和时机,我们来看代码:
public TreeNode constructMaximumBinaryTree(int[] nums) {
Deque<TreeNode> stack = new LinkedList<>();
for(int i = 0; i < nums.length; i++) {
TreeNode curr = new TreeNode(nums[i]);
while(!stack.isEmpty() && stack.peek().val < nums[i]) {
curr.left = stack.pop();
}
if(!stack.isEmpty()) {
stack.peek().right = curr;
}
stack.push(curr);
}
return stack.isEmpty() ? null : stack.removeLast();
}
注意这道题用双向队列的唯一原因就是最后一个return,主要逻辑和栈一样,所以我们说这道题的思路是先选择栈,最后再替换成双向队列。
关注公众号,更多算法知识点告诉你。