以Java形式用回溯来解决排列-组合问题

以Java形式用回溯来解决排列-组合问题

前言

本文章原题来源于LeetCode并已经通过测试; 使用的Java语言,代码中借用了LinkedList类,其用法本文章不进行讲解,若在其用法和原理上有疑问望读者自行查询

组合

组合问题类型一

有如下的问题:

例子:
在这里插入图片描述

废话少说,代码先上:

class Solution {
    List<List<Integer>> lists=new LinkedList();
    LinkedList list=new LinkedList();
    int count=0;
    public List<List<Integer>> combine(int n, int k) {
        back(n,k,1);
        return lists;
    }
    void back(int n,int k,int start){
        if(count==k){
            lists.add(new LinkedList(list));
            return;
        }
        for(int i=start;i<=n;i++){
            list.add(i);
            count++;
            back(n,k,i+1);
            list.removeLast();
            count--;
        }
    }
}

大家或许会在

lists.add(new LinkedList(list));

back(n,k,i+1);
list.removeLast();

上有问题,不要着急,让我细细为大家解释。
首先我们先理解简单来看看题目,需要返回由k个不同数字且在1~n上的组合,注意是组合也就是说{1,4}和{4,1}是同一个,题目看起来并不复杂。
再开始理解代码,我们先看到我们首先定义的三个成员变量lists、list和count;其作用分别为存储结果;进行元素的组合和记录list中的元素个数;back方法就是我们要开始进行递归的方法,其终止条件就是当count的大小等于k时,就代表list中存储的数值已经是一个满足条件的组合序列了,那么我们就将其加入到lists中,注意这里需要重新定义一个LinkedList对象。这是当list已经满足条件的情况。

我们开始跟着程序步骤走,就以上述例子来说;由combine方法进入back方法,start表示i从数字几开始,题目是由1开始,所以combine中start传值为1;进入第一个for循环,list先为[1],count+1;之后进入第二个back方法,start为i+1,此时count大小为1,不满足k大小;则进入for循环,此时i为2,list中数值就为[1,2],count+1;进入第三个back方法,此时count满足k大小,将list中的数值赋值到lists中,退出第三个back方法,执行list.removeLast(); (这是这个类自带的方法,意思是去除最后一个元素),也就是说此时的list变为[1],同时这也是为什么要重新new一个新的LinkedList对象,否则的话lists中的数值会跟着变 ;继续走count跟着减少一个,此时还在第二个back方法中,此时已经走了一次for循环,那么i+1等于3,list就变成[1,3]进行下次的递归。直到第二次for的循环结束,返回到第一个back方法,list变为[],count减为0;进入第二次for循环,i为2,list变为[2]… 后续的过程想必也能推断出来了,题目中已经说了可以按照任意顺序给出结果,这里的结果是:

[[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]

组合问题类型二

有如下的问题:
在这里插入图片描述
给出的例子:
在这里插入图片描述
这里先不上代码,先听思路。先分析题目,给出一个数组,其中的元素可以无限次的使用,那么我们是不是可以先取出第一个元素并让他不断的加自己直到等于target或大于target。当出现了结束情况,就开始加上第二个元素,同时也是不断的加上第二个元素直到等于target或大于target。可以看到本质其实也就是穷举

上代码:

class Solution {
    List<List<Integer>> lists=new LinkedList();
    LinkedList list=new LinkedList();
    int sum=0;
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        back(candidates,target,0);
        return lists;
    }
    void back(int candidates[],int target,int start){
        if(sum==target){
            lists.add(new LinkedList(list));
            return;
        }
        if(sum>target){
            return;
        }
        for(int i=start;i<candidates.length;i++){
            sum+=candidates[i];
            list.add(candidates[i]);
            back(candidates,target,i);
            sum-=candidates[i];
            list.removeLast();
        }
    }
}

我想你已经能够明白上一题的问题了,那么上一题说的东西我这里就不知多说了;同样也是用lists来记录结果,list来记录组合,而用sum来进来list元素的和,递归的终止条件就是sum>=target。与上面不同的是back方法中的start的传值不是i+1而是i了,这里也就是让自身不断的加自己,直到count>=target,for循环就是加上第二个元素
注意点:这里首先需要将数组candidates中的元素按照从小到大进行顺序排列,借用了Arrays类的静态方法sort,默认升序排列

排列

先看问题:
在这里插入图片描述
例子如下:
在这里插入图片描述
先上代码:

class Solution {
    List<List<Integer>> lists=new LinkedList();
    LinkedList list=new LinkedList();
    boolean used[];
    int count=0;
    public List<List<Integer>> permute(int[] nums) {
        used=new boolean[nums.length];
        back(nums);
        return lists;
    }
    void back(int[] nums){
        if(count==nums.length){
            lists.add(new LinkedList(list));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(used[i]){
                continue;
            }
            list.add(nums[i]);
            count++;
            used[i]=true;
            back(nums);
            list.removeLast();
            used[i]=false;
            count--;
        }
    }
}

这里的大致都与解决上面的组合问题的思路差不多,大同小异。
排列中元素是具有顺序的,也就是说我们的进行多次来回的进行排列,每次都需要从头开始进行排列,会遇到将同一元素加入到list中,借用used数组可以很好避免使用重复元素。
这里我就说说used数组的用法,used大小设为与数组大小一致,第i个元素对应着nums数组上第i个元素是否已经被使用了,当第i个元素已经排列到list中去了,就continue去进下一个for循环。

这里跟着我走一遍,按照上面的例子;list先加一次变为[1],count跟着加一,used[0]=True表示第一个元素已经加入进来了,进入第二个back方法,未满足终止条件,i=0,used[0]为真,进入第二个for循环,一系列操作后list=[1,2],再进入第三个back方法,最后进入第四个back方法时为[1,2,3]满足终止条件,退到第三个back方法会去除最后一个元素,因为该例子最多为三个元素就跟着退出到第二个back方法,去除最后一个元素由[1,2]变为[1],进入第二个back方法的第三次for循环变为[1,3]再之后继续进行递归,大致运行过程就是如此

总结

这里的思路是在下通过看其他博主学过来的,旨在记录与分享自己的学习路程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值