二叉搜索树,穷举(全排列)

简介: 二叉搜索树,穷举(全排列)

力扣230.二叉搜索树中第K小的元素

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    int count=Integer.MAX_VALUE;
    int ret=0;
    //可以尝试中序遍历,然后用数组去存储
    //两个全局变量,用中序遍历,然后再去计数
    public int kthSmallest(TreeNode root, int k) {
        count=k;
       dfs(root);
     return ret;
    }
 public void dfs(TreeNode root){
        //处理了剪枝的情况
    if(root==null||count==0){
        //提前退出的意思
            return ;
            }
    dfs(root.left);
    //修改计数器,注意一个不好理解的地方,count--写一个就够,他不是循环,两个改变就要写两个,要考虑递归的思想,我本人确实现在还没有那个思想,但是会慢慢努力
    count--;
    //剪枝
    if(count==0){
        ret=root.val;
    }
    if(count==0){
     return ;}
//右子树重新递归的时候,会走那个count--,因为假如没有左子树,那么count 不变
    dfs(root.right);
    }
}

力扣257.二叉树的所有路径

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
     List <String>ret;
    public List<String> binaryTreePaths(TreeNode root) {
           dfs(root,new StringBuffer()); //传递的相当于是一个全局的StringBuffer
           return ret;
 
    }
    //回溯:恢复现场,只要有递归,必然就有回溯,简单题中
    public void dfs(TreeNode root,StringBuffer _path){
//定义个变量,这样他回溯的时候,他不会影响那个_path,
        StringBuffer path=new StringBuffer(_path);
//path加入root.val相当于保存当前节点的值
        path.append(Integer.toString(root.val));
        if(root.left==null&&root.right==null){
//到了叶子结点,那么路径的就基本已经出来了,所以要把他添加到ret里面,然后
           ret.add(path.toString());
           return;
        }
//path加入这个->箭头,所有的修改都是在path这个地方修改
        path.append("->");
//这里传递的是path,而不是_path,因为_path已经存储了。而path没有存储,path还停留在根节点应该有的地方
      if(root.left!=null) dfs(root.left,path);
      if(root.right!=null)dfs(root.right,path);
    }
}

回溯的用途就是递归后返回。剪枝的用途就是为了节省时间,如果某种情况不合格,就无须遍历其他节点。

力扣46.全排列

如果全部使用for循环会很难解决,所以选择使用递归。

1.画出决策树,越详细越好(决策树:如何不从不漏的全部遍历一遍)

  • res.add(list)为浅拷贝,后续list内容的变化会导致res的变化,在原来地址改变数据,内容肯定会被改变
  • res.add(new ArrayList(list))为深拷贝,对象中开辟一个新地址,存放的内容为list链表,所以后续不会被影响。

解释了什么是深拷贝,什么是浅拷贝

记住一件事,写算法,dfs不要套模版,套模版只会局限你的思维,这次我是很深的感受到,套代码这几个题,我都没有很好的思考出来。

class Solution {
    List<List<Integer>> ret=new ArrayList<>();
    List<Integer>path=new ArrayList<>();
    boolean[]check;
    public List<List<Integer>> permute(int[] nums) {
     check=new boolean[nums.length];
//这一步也不好想,为什么只穿一个数组,我刚开始是想传一个数组,再加上一个i,j,k标记到了第几个数字,我们只需判断path的长度是否等于nums的长度即可
     dfs(nums);
     return ret;
    }
public void dfs(int []nums){
    //出口
    if(path.size()==nums.length){
        //深拷贝,不影响path这个东西。
    ret.add(new ArrayList<>(path));
    return ;}
    for(int i=0;i<nums.length;i++){
        if(check[i]==false){
            //添加
            path.add(nums[i]);
            //标记一遍
            check[i]=true;
            //dfs进入下一层
            dfs(nums);
            //回溯->恢复现场 当dfs回来之后,把check恢复原来位置
            check[i]=false;
            //回溯减少一个,
            path.remove(path.size()-1);
        }
    }
    }
}

力扣78.子集

步骤一:先画决策树

2.设计代码

全局变量

dfs()

细节问题

class Solution {
    List<List<Integer>>ret=new ArrayList<>();
    List<Integer>path=new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
    dfs(nums,0);
    return ret;
    }
//整个代码实现,我认为最难的部分在于怎么去写选和不选部分
    public void dfs(int[]nums,int i){
    if(i==nums.length){
        ret.add(new ArrayList<>(path));
        return;
    }
     
    //选,如果你要选,就要先添加,然后进入递归
    path.add(nums[i]);
    dfs(nums,i+1);
 
//    path.remove(nums[i+1]);
//这里去除也是细节,为神马不是上面那个去除,因为上面的去除,面临越界的事情,但是假如你是原有的path.remove就会去除下标为size-1这个位置的结果,因为他是数组。
path.remove(path.size()-1);
    //不选
    dfs(nums,i+1);
    }
}

他是根据下标删除

解法2:我们for循环遍历的是下标

class Solution {
    List<List<Integer>>ret=new ArrayList<>();
    List<Integer>path=new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
    dfs(nums,0);
    return ret;
    }
public void dfs(int[]nums,int pos){
   //这一步很细节,开始在想,他怎么才能是空,后来发现把path当成别的东西了,假如一开始添加他就,那他就是一个空的.
   ret.add(new ArrayList<>(path));
   //i=pos,这一步是为了让他到达pos的地方,也就是到达了第几层
  // 这个for表示的是让决策树横着走
   for(int i=pos;i<nums.length;i++){
      path.add(nums[i]);
//注意这里是i+1,还是pos+1,因为他的i从i的下一个位置开始依次枚举,假如是pos会出现什么结果呢,假如说是pos,当i从1变成2的时候pos是不改变的,换句话说,他还会再往下重复走一块,为什么是i+1,就是因为不让他重复走,这个dfs是表示让决策树往下走,假如是只有2的话,那个i=0,然后pos=1,ret加入这个2,此时i页=1,path又去加这个i,此时他就是22,然后再次进入,那么就ret 就进入了22,所以pos是不正确的,那么假如是i的话,就会往下递归正常的顺序。
      dfs(nums,i+1);
      path.remove(path.size()-1);
   }
    }
}


相关文章
|
2月前
|
机器学习/深度学习 存储 算法
【算法训练-回溯算法 一】【排列问题】全排列、全排列II
【算法训练-回溯算法 一】【排列问题】全排列、全排列II
64 0
|
27天前
|
机器学习/深度学习 存储 算法
LeetCode 题目 95:从递归到动态规划实现 不同的二叉搜索树 II
LeetCode 题目 95:从递归到动态规划实现 不同的二叉搜索树 II
|
27天前
|
存储 算法 数据可视化
LeetCode 题目 96:从动态规划、递归到卡塔兰数实现不同的二叉搜索树
LeetCode 题目 96:从动态规划、递归到卡塔兰数实现不同的二叉搜索树
|
1月前
|
算法
基础算法-去重字符串,辗转相除法,非递归前序遍历二叉树题型分析
基础算法-去重字符串,辗转相除法,非递归前序遍历二叉树题型分析
|
2月前
|
算法 C++
C++019-C++暴力枚举
C++019-C++暴力枚举
C++019-C++暴力枚举
【剑指offer】-二叉搜索树的后序遍历序列-23/67
【剑指offer】-二叉搜索树的后序遍历序列-23/67
剑指offer 34. 二叉搜索树的后序遍历序列
剑指offer 34. 二叉搜索树的后序遍历序列
43 0
Day41—— 343. 整数拆分 96.不同的二叉搜索树 (动规)
Day41—— 343. 整数拆分 96.不同的二叉搜索树 (动规)
85 0
|
算法
【刷算法】判断二叉搜索树的后序遍历序列的递归实现和非递归实现
【刷算法】判断二叉搜索树的后序遍历序列的递归实现和非递归实现
101 0
【LeetCode剑指offer】二叉搜索树的最近公共祖先(迭代or递归)
求两个节点的最近公共祖先的题目我们做过,但是这题是二叉搜索树BST,并且本题中所有节点的数值都是不同的,所以可以根据BST的数值特点进行判断,即左子树的所有节点都比当前节点小,右子树的所有节点都比当前节点数值大。
98 0
【LeetCode剑指offer】二叉搜索树的最近公共祖先(迭代or递归)