二叉树搜索序列(回溯)

        最近不知道干啥事,好像好多事要干,又好像啥事没有..我的心情be like

         然后我就打开力扣开始看题,按顺序刷题,就看到了这个题目:

         刚一看到,感觉简简单单小回溯,轻松拿捏

         再一看难度:困难,感觉不妙...再仔细一看,好像没有那么简单

         于是我想了一整堂课,终于有了一丢丢思路,然后就写了下来。主要是通过bfs和回溯算法联合使用解决。

 二、解题思路

        1、看示例、了解题意

        题目中说,从左向右遍历一个数组就插入数据就可以生成二叉搜索树。然后给了一个[2,1,3]的实例,如图可以看到它是一层一层插入的。也就是说[2,1,3]和[2,3,1]的差距就在于左右节点插入的顺序不同。因此我们很容易就看出,只要我们通过bfs的方法遍历每一层,然后在每一层通过回溯的方法先插入左节点,得到结果后回溯回来,再插入右节点,就可以完成操作了。

         2、确定数据结构和大致框架

        有了大概思路之后,我们就需要确定需要使用什么样的数据结构和什么样的框架了。

        首先,题目需要返回的类型是List<List<Integer>>,因此我们需要定义一个最终返回结果ans,类型为List<List<Integer>>。然后bfs必不可少的就是队列了,但是我们不选择Queue,因为我们需要进行回溯操作,需要删除指定节点,因此采用List较好。其次,ans保存的是所有可能的路径,因此我们还需要一个List<Integer>类型的变量储存单次的路径。

        确定了数据结构,我们需要大概的框架,如下:

class Solution {
    // 定义全局的变量,也是函数返回值
    List<List<Integer>> ans = new ArrayList<>();
    public List<List<Integer>> BSTSequences(TreeNode root) {
        // 定义队列(存的是节点)
        List<TreeNode> queue = new LinkedList<>();
        //定义单次路径
        List<Integer> path = new ArrayList<>();
        // 判空处理
        if(root == null){
            ans.add(new ArrayList<>(path));
            return ans;
        }
        // bfs老套路,先把头结点放进去,不然循环进不去
        queue.add(root);
        bfs(queue,path);
        // 调用后返回结果
        return ans;
    }
    public void bfs(List<TreeNode> queue,List<Integer> path){
        
    }
}

        咋样,有没有清晰一点?

        有?

        难点还在后面

         主函数大概写好了,那么关键的bfs函数确实是个难点。

        那么我们再写下回溯的框架吧

        

    public void bfs(List<TreeNode> queue,List<Integer> path){
        if(queue.isEmpty()){
            ans.add(new ArrayList<>(path));
            return;
        }
        int size = queue.size();
        // 保存当前队列
        for(int i = 0; i < size; i++){
            // 加节点等一系列操作
            // 再次调用
            bfs(queue,path);
            //当执行到这一步说明一条路径走完了,需要删除当点节点重新选取节点再来一遍
            path.remove(path.size() - 1);
            //恢复队列
        }

        
    }

               大致框架是这个:

                当队列为空的时候,说明节点都已经加入到path里面了,因此path里存的已经是一个完整的路径了,可以加入到ans里去,然后别忘记返回。

                之后循环队列,取出当前队列头部的节点,把它的值放入path中,代表了选择了这个节点,然后再将选择的节点的左右节点加入队列(bfs套路)。放入之后,再次调用bfs循环操作。之后指定到下一步的时候已经需要恢复现场了。

                这里注意!!恢复现场需要恢复两个东西,一个是队列queue、一个是路径path。path比较容易,只需要删除最新加入的那个节点的值就可以了。但是queue稍微麻烦点,不可以通过queue.remove(queue.size() - 1);这种粗暴的方式实现。原因是队列有时候执行到这一步的时候已经是空队列了,执行这一步会报下标越界的错误。

                正确的做法是在进入循环之前,先保存一次队列,然后恢复现场的时候直接让队列等于保存的队列就可以了。总的代码如下:

                

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    public List<List<Integer>> BSTSequences(TreeNode root) {
        List<TreeNode> queue = new LinkedList<>();
        List<Integer> path = new ArrayList<>();
        if(root == null){
            ans.add(new ArrayList<>(path));
            return ans;
        }
        queue.add(root);
        bfs(queue,path);
        return ans;
    }
    public void bfs(List<TreeNode> queue,List<Integer> path){
        if(queue.isEmpty()){
            ans.add(new ArrayList<>(path));
            return;
        }
        int size = queue.size();
        List<TreeNode> copy = new ArrayList<>(queue);
        for(int i = 0; i < size; i++){
            TreeNode head = queue.get(i);
            path.add(head.val);
            queue.remove(i);
            if(head.left != null)
                queue.add(head.left);
            if(head.right != null)
                queue.add(head.right);
            bfs(queue,path);
            path.remove(path.size() - 1);
            queue = new ArrayList<>(copy);
            //queue.remove(queue.size() - 1);
        }

        
    }
}

        最后运行的结果如下:

         好像不是特别好,于是我又在题解里找了份比较好的回溯。写的相当的nice啊

         代码贴下去了:

class Solution {
    private LinkedList<Integer> path = new LinkedList<>();
    private List<List<Integer>> result = new LinkedList<>();
    public List<List<Integer>> BSTSequences(TreeNode root) {
        Deque<TreeNode> dq = new LinkedList<>();
        if (root != null) {
            dq.offer(root);
        }
        dfs(dq);
        return result;
    }
    public void dfs(Deque<TreeNode> dq) {
        if (dq.isEmpty()) {
            result.add(new ArrayList<Integer>(path));
            return;
        }
        int size = dq.size();
        while(size > 0) {
            TreeNode cur = dq.pollFirst();
            path.add(cur.val);
            int children = 0;
            if (cur.left != null) {
                children++;
                dq.offerLast(cur.left);
            }
            if (cur.right != null) {
                children++;
                dq.offerLast(cur.right);
            }
            dfs(dq);
            while (children > 0) {
                dq.pollLast();
                children--;
            }
            dq.offerLast(cur);
            path.removeLast();
            size--;
        }
    }
}

三 、尾声

        最近题写的不多,题解就更少了。但是我想把为数不多的题解写的有趣一些,因此加了一些表情包,希望大家喜欢。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少๑渊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值