回溯几类问题总结

回溯几类问题总结

对于回溯问题,虽然比较抽象,但是总体来说模板都差不多,比较经典的就是排列问题和组合问题,其中又可以细分为可以重复和不可以重复。

组合

对于组合问题,一般都是边找边存结果,此外参数传递一般都是index(用于记录当前遍历的位置),此外子集问题也可以看成是组合问题。

去重

那么在这些类型的问题中,需要结果不能重复,例如数组[1,2,3,4]中,子集[1,2]和[2,1]就是重复的,所以需要去重。

有序数组去重

对于有序数组去重就比较简单,例如[1,2,2,3,4],我们可以采取先算一次,然后再跳跃的方案,也就是这里第一个[1,2,2]可以计算在内,当回朔回来的时候,就需要跳过。例如

        for (int i = index + 1; i < nums.length; i++) {
        	// 先算一次结果
            nowArr.add(nums[i]);
            getRes(nums, i, nowArr);
            // 复原
            nowArr.remove(nowArr.size() - 1);
            // 去重
            while (i + 1 < nums.length && nums[i + 1] == nums[i]) {
                i++;
            }
        }
无序数组去重

当数组是无需的时候,例如[1,4,6,7,6],这时就不能用上面的方法使用while进行跳跃了,这个时候需要维护一个set,记录每次遍历的内容,看是否存在重复
例如

        Set<Integer> checkNum = new HashSet<>();
        for (int i = index; i < nums.length; i++) {
        	// 判断是否重复
            if (checkNum.contains(nums[i])) {
                continue;
            }
            checkNum.add(nums[i]);
            nowList.add(nums[i]);
            getRes(nums, nowList, i + 1);
            // 恢复
            nowList.remove(nowList.size() - 1);
            checkNum.remove(nums[i]);
        }

排列

对于排列问题,和组合问题是不一样的,例如[1,2]和[2,1],对于排列来说是俩种不同的结果,而对于组合来说是一种结果,此外组合问题每次都是维护了一个index,从index开始遍历,但是排列需要每次从头开始遍历。

去重

对于排列的去重,例如[1,2,3],在排列的时候需要避免[1,2,1]这种情况的出现,所以需要记录每次递归的过程中遍历了的元素,所以可以在参数中传递当前遍历过的集合

    public void getRes(int[] nums, List<Integer> nowList, Set<Integer> used) {
    for (int i = 0; i < nums.length; i++) {
            if (used.contains(i)) {
                continue;
            }
            nowList.add(nums[i]);
            used.add(i);
            // 传递当前已经遍历过的数据
            getRes(nums, nowList, used);
            // 恢复
            used.remove(i);
            nowList.remove(nowList.size() - 1);
        }
    }

你可能感兴趣的:(java,算法,数据结构,leetcode)