Leetcode刷题笔记——回溯

 46.全排列

题目描述:

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

解题思路:

穷举的题目,用回溯法。

代码:

public List<List<Integer>> permute(int[] nums){
        List<List<Integer>> result = new LinkedList<>();
        
        ArrayList<Integer> numsArray = new ArrayList<>();
        for(int num: nums)
            numsArray.add(num);

        traceback(numsArray, result, 0);
        return result;
    }

    public void traceback(ArrayList<Integer> nums, List<List<Integer>> result, int first){
        //如果走到了最后,则加入结果数组中
        if(first == nums.size()-1)
            result.add(new ArrayList<>(nums));

        for(int i=first; i<nums.size(); i++){
            //交换nums[first]和nums[i]的值,形成新的排列
            Collections.swap(nums, first, i);
            //递归
            traceback(nums, result, first+1);
            //再次交换nums[first]和nums[i]的值,还原
            Collections.swap(nums, first, i);
        }
    }

代码理解:

算法的基本思想如下图,代码的走向是从上到下从左到右,其实就是用交换的方式遍历所有可能的排列。

47. 全排列2

题目描述:

给定一个可包含重复数字的序列,返回所有不重复的全排列。

解题思路:

其实这是全排列1的一般情况,首先来看一下全排列1的另一种解题思想:

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

        List<List<Integer>> res = new ArrayList<>();
        int[] visited = new int[nums.length];
        backtrack(res, nums, new ArrayList<Integer>(), visited);
        return res;

    }

    private void backtrack(List<List<Integer>> res, int[] nums, ArrayList<Integer> tmp, int[] visited) {
        if (tmp.size() == nums.length) {
            res.add(new ArrayList<>(tmp));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (visited[i] == 1) continue;
            visited[i] = 1;
            tmp.add(nums[i]);
            backtrack(res, nums, tmp, visited);
            visited[i] = 0;
            tmp.remove(tmp.size() - 1);
        }
    }

这个算法有点难理解,它用一个visited数组记录该次遍历已访问过的位置

第一轮一路走到n,将该数组存入结果集;第二轮,remove掉了最后两位,最后两位都是unvisited,但是回溯回来在tmp[nums.length-2]处 i 已经走到了nums[nums.length-1]位,因此数字n被填入temp[nums.length-2]处,数字n-1被填入temp[nums.length-1]处。(有博客说在回溯到tmp[nums.length-2]时只有最后一位是unvisited,是不对的,是因为游标 i 此时已经跳过了第n-1位,所以才将数字n填入n-1位,开单步调试看看变量怎么变得就知道了)

这是没有重复的情况,当有重复数字时,标红的那一行就需要换成如下语句,并且需要先对nums数组进行排序。

if(visited[i] == 1 || (i>0 && nums[i]==nums[i-1] && visited[i-1]==0))
                continue;

 另一个判断条件是,如果nums[i]在nums数组中有排在其前面并且值相等的数,并且在这轮遍历中前面的相同数值的nums[i-1]还没有插入tmp结果集,那么就跳过nums[i],因为如果将nums[i]插入,那么结果会和将nums[i-1]插入时的结果一样,就会造成重复。

 

posted @ 2019-07-03 21:36  好好学习啊—  阅读(284)  评论(0编辑  收藏  举报