LeetCode高频题:二叉树(五)

我开了一个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,主要逻辑和栈一样,所以我们说这道题的思路是先选择栈,最后再替换成双向队列。

关注公众号,更多算法知识点告诉你。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值