回溯算法

回溯算法其实就是递归,了解递归的朋友一般就非常容易理解回溯算法

 

回溯算法一般有三部分构成

1.初始传参数

2.回溯函数,满足条件 返回对应的值

3.确定终止条件

 

例题1全排列  力扣46题

给定一个 没有重复 数字的序列,返回其所有可能的全排列。

输入: [1,2,3]
输出:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

 

class Solution {
   public  List<List<Integer>> permute(int[] nums) {


        List<List<Integer>> res = new ArrayList<>();

        Deque<Integer> deque = new ArrayDeque<>();

        int len = nums.length;
        if (nums.length == 0){
            return res;
        }

        int nowIdenx = 0;
        //1.初始传参数
        backtrack(nowIdenx,deque,nums,res,len);

        return res;
    }
    
    //2.回溯函数,满足条件 返回对应的值
    private  void backtrack(int nowIdenx, Deque<Integer> deque, int[] nums, List<List<Integer>> res, int len) {

        //3.确定终止条件
        if (deque.size() == len){
            res.add(new ArrayList<Integer>(deque));
            return;
        }


        for (int i = 0; i<nums.length ; i++){

            if (deque.contains(nums[i])){
                continue;
            }

            deque.addLast(nums[i]);
            backtrack(i,deque,nums,res,len);
            deque.removeLast();

        }

    }

}

 

解析:递归可以看成一种进栈出栈(堆栈,数据结构说堆栈和栈是一个意思)的操作,先进后出:用图表示,因为比较好理解,下次看直接就能想起来

然后进入下一层递归函数,进入递归函数中是判断是否满足终止条件,这里不满足,然后进入for循环中,这里i是从0开始的,为了避免重复,这里有一个判断条件,deque中是否存在nums[i],因为不能重复,这里deque双向队列,用来当stack用了,直接用stack也行,个人习惯,因为nums【0】已经存在了,所以i++,i=1;条件成立,先把i放入栈(deque表示的栈)中,再次进入backstack函数中;

同理:i++,deque.addLast(3);

再次进入backtrack(这个英文是回溯的意识)函数中,这里deque.size==len(条件)时,return:结束这次backtrack函数,这里要说的是,结束时去哪里,其实就是递归的基础,


        for (int i = 0; i<nums.length ; i++){

            if (deque.contains(nums[i])){
                continue;
            }
            //因为nums[2]进入了backtrack函数
            deque.addLast(nums[i]);
            //backtrack(i,deque,nums,res,len);
            //这里我们把函数展开;nums[2]==3进入backtrack函数
            
        //3.确定终止条件  条件成立,res添加条件满足的结果,看图
        if (deque.size() == len){
            res.add(new ArrayList<Integer>(deque));
            //return;
        }            
            //因为上一个backtrack 已经return了,所以会走到这里
            deque.removeLast();
            //从栈中移除nums[2]这个数字

        }

下面该怎么走呢?其实很简单,随着第三次backtrack函数的结束,递归跳的第二次backstack中,这是i还是等于num[1],因为

        //3.确定终止条件
        if (deque.size() == len){
            res.add(new ArrayList<Integer>(deque));
            return;
        }


        for (int i = 0; i<nums.length ; i++){

            if (deque.contains(nums[i])){
                continue;
            }
            //这时候i等于1,以为上一个backtrack函数的结束成功
            deque.addLast(nums[i]);
            backtrack(i,deque,nums,res,len);
            //因为backtrack函数的成功结束,deque(栈)中的2也会被弹出
            deque.removeLast();

        }

聪明的你,应该已经明白了,这道题还可以剪枝 

下面就是一道例题;力扣77题 组合  这个题用到了剪枝,for需要哪里

给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

示例:输入: n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

class Solution {

    public  List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> res = new ArrayList<>();


        if (n<k || k<=0){
            return res;
        }

        Deque<Integer> deque = new ArrayDeque<>();

        int nowIndex = 1;

        backtoto(n,k,nowIndex,deque,res);

        return res;

    }

    private  void backtoto(int n, int k, int nowIndex, Deque<Integer> deque, List<List<Integer>> res) {

        if (deque.size()==k){
            res.add(new ArrayList<>(deque));
            return;
        }
         //这里就用到了剪枝

        for (int i = nowIndex;i <= n-(k-deque.size())+1;i++){
            deque.addLast(i);
            backtoto(n,k,i+1,deque,res);
            deque.removeLast();


        }
    }
}

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值