• 二叉树搜索序列(回溯)


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

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

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

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

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

     二、解题思路

            1、看示例、了解题意

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

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

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

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

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

    1. class Solution {
    2. // 定义全局的变量,也是函数返回值
    3. List> ans = new ArrayList<>();
    4. public List> BSTSequences(TreeNode root) {
    5. // 定义队列(存的是节点)
    6. List queue = new LinkedList<>();
    7. //定义单次路径
    8. List path = new ArrayList<>();
    9. // 判空处理
    10. if(root == null){
    11. ans.add(new ArrayList<>(path));
    12. return ans;
    13. }
    14. // bfs老套路,先把头结点放进去,不然循环进不去
    15. queue.add(root);
    16. bfs(queue,path);
    17. // 调用后返回结果
    18. return ans;
    19. }
    20. public void bfs(List queue,List path){
    21. }
    22. }

            咋样,有没有清晰一点?

            有?

            难点还在后面

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

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

            

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

                   大致框架是这个:

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

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

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

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

                    

    1. class Solution {
    2. List> ans = new ArrayList<>();
    3. public List> BSTSequences(TreeNode root) {
    4. List queue = new LinkedList<>();
    5. List path = new ArrayList<>();
    6. if(root == null){
    7. ans.add(new ArrayList<>(path));
    8. return ans;
    9. }
    10. queue.add(root);
    11. bfs(queue,path);
    12. return ans;
    13. }
    14. public void bfs(List queue,List path){
    15. if(queue.isEmpty()){
    16. ans.add(new ArrayList<>(path));
    17. return;
    18. }
    19. int size = queue.size();
    20. List copy = new ArrayList<>(queue);
    21. for(int i = 0; i < size; i++){
    22. TreeNode head = queue.get(i);
    23. path.add(head.val);
    24. queue.remove(i);
    25. if(head.left != null)
    26. queue.add(head.left);
    27. if(head.right != null)
    28. queue.add(head.right);
    29. bfs(queue,path);
    30. path.remove(path.size() - 1);
    31. queue = new ArrayList<>(copy);
    32. //queue.remove(queue.size() - 1);
    33. }
    34. }
    35. }

            最后运行的结果如下:

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

             代码贴下去了:

    1. class Solution {
    2. private LinkedList path = new LinkedList<>();
    3. private List> result = new LinkedList<>();
    4. public List> BSTSequences(TreeNode root) {
    5. Deque dq = new LinkedList<>();
    6. if (root != null) {
    7. dq.offer(root);
    8. }
    9. dfs(dq);
    10. return result;
    11. }
    12. public void dfs(Deque dq) {
    13. if (dq.isEmpty()) {
    14. result.add(new ArrayList(path));
    15. return;
    16. }
    17. int size = dq.size();
    18. while(size > 0) {
    19. TreeNode cur = dq.pollFirst();
    20. path.add(cur.val);
    21. int children = 0;
    22. if (cur.left != null) {
    23. children++;
    24. dq.offerLast(cur.left);
    25. }
    26. if (cur.right != null) {
    27. children++;
    28. dq.offerLast(cur.right);
    29. }
    30. dfs(dq);
    31. while (children > 0) {
    32. dq.pollLast();
    33. children--;
    34. }
    35. dq.offerLast(cur);
    36. path.removeLast();
    37. size--;
    38. }
    39. }
    40. }

    三 、尾声

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

     

  • 相关阅读:
    第五单元 用python学习微积分(三十四)泰勒级数
    公众号如何获取视频号下载工具?
    chatgpt赋能python:Python中的逆序数
    vue3简单快速实现主题切换功能
    java工程师常用的java框架Shiro
    SpringCloudAlibaba-Nacos集群
    天软特色因子看板 (2023.09 第09期)
    802.1Qbb
    [ACNOI2022]物品
    外包干了5天,技术明显退步。。。。。
  • 原文地址:https://blog.csdn.net/sjl20021006/article/details/126885828