Leetcode39.+Leetcode491. 回溯法之应用(三):寻找组合和+求递增子数列

Leetcode39. Combination Sum

题目

Given a set of candidate numbers (C) (without duplicates) and a target number (T), find all unique combinations in C where the candidate numbers sums to T.
The same repeated number may be chosen from C unlimited number of times.

Note:
All numbers (including target) will be positive integers.
The solution set must not contain duplicate combinations.

For example, given candidate set [2, 3, 6, 7] and target 7,
A solution set is:
[
[7],
[2, 2, 3]
]

解题分析

这道题是很经典的运用回溯法解决的例子。运用回溯法解题,最关键是要找到三个关键点:初始状态、临界状态和迭代过程。我们通过这道题分别对这三个点进行简要地分析。
这道题给出了一个数组,要求我们利用数组中的数字找出所有和为target的情况,允许数组元素重复使用。为了方便找出所有情况,我们先对数组进行排序。
首先我们来考虑初始状态,很显然,在刚开始的时候,vector为空,要寻找的和为target。
再来看看临界情况,同样也很明显,当我们匹配到所要寻找的target为0时,说明此时这种匹配情况成立,将这种情况存进vector中即可。当target小于0时,说明匹配不成立,结束继续往深处遍历的过程。
最后再来看看迭代的过程,当我们选定一个数candidates[i]之后,那么此时匹配的总和为(target-i),由于可以重复,因此仍将从candidates[i]开始匹配。这样一个个数下去,找得到符合要求的数字就存进vector中,否则就返回,将该数pop掉,将下一个数存进数组中重新考虑情况,直到原数组遍历完成为止。

源代码

class Solution {
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> v;
        vector<int> vec;
        sort(candidates.begin(), candidates.end());
        combinate(v, vec, candidates, target, 0);
        return v;
    }

    void combinate(vector<vector<int>>& v, vector<int>& vec, vector<int>& candidates, int target, int start) {
        if (target < 0) {
            return;
        }
        if (target == 0) {
            v.push_back(vec);
            return;
        }
        for (int i = start; i < candidates.size(); i++) {
            vec.push_back(candidates[i]);
            combinate(v, vec, candidates, target - candidates[i], i);
            vec.pop_back();
        }
    }
};

Leetcode491. Increasing Subsequences

题目

Given an integer array, your task is to find all the different possible increasing subsequences of the given array, and the length of an increasing subsequence should be at least 2 .

Example:
Input: [4, 6, 7, 7]
Output: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]

Note:
The length of the given array will not exceed 15.
The range of integer in the given array is [-100,100].
The given array may contain duplicates, and two equal integers should also be considered as a special case of increasing sequence.

解题分析

看到这道题,可能第一感觉是这道题跟回溯法的结合不是很紧密。但其实,我们可以通过上面题目中的例子先来思考一下回溯法的流程。
题目中数组为[4,6,7,7],根据题目的要求(数组递增),我们可以先来这样思考。首先很明显的是[4,6]符合题目的要求,一旦符合要求,就继续往后遍历直到数组遍历完成,因此[4,6,7]和[4,6,7,7]也符合情况。
在数组遍历完成之后,我们就尝试将数组后面的元素pop掉,跳过之前选中的元素,于是有[4,7]符合要求。和上面一样重复遍历的过程,[4,7,7]也满足要求。
依此类推,可以知道[6,7]、[6,7,7]和[7,7]也满足,这样结果就出来了。

通过上面的例子我们可以发现,其实上面的流程就是回溯法的流程,因此和之前一样,找准三个关键点。
首先初始情况下,数组vector为空,从第一个元素开始遍历,这里就没什么好说的了。
然后临界状态,就是当vector元素的数目大于1时,说明符合要求,将其输出。
最后再来看看迭代过程。由于数组要递增,所以在每遍历一个元素的时候,需要将其与vector中最后一个元素进行比较,如果满足大于等于的关系,就将其push到vector中。然后在选中当前元素之后的情况全部考虑完之后,就将其pop掉,考虑后面的元素,继续前面的过程,直到全部情况考虑完毕,这样问题就顺利解决了。
在这里需要注意的是,由于要求不能重复,所以在插入元素的时候,还需要进行判断,这里用了unordered_set,其原理是使用红黑二叉树,时间复杂度为O(logn),可以最大程度提高程序执行的效率。

源代码

class Solution {
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        vector<vector<int>> v;
        vector<int> vec;
        subsequences(v, vec, nums, 0);
        return v;
    }

    void subsequences(vector<vector<int>>& v, vector<int>& vec, vector<int>& nums, int start) {
        if (vec.size() >= 2) {
            v.push_back(vec);
        }
        unordered_set<int> set;
        for (int i = start; i < nums.size(); i++) {
            if ((vec.size() == 0 || nums[i] >= vec.back()) && (set.find(nums[i]) == set.end())) {
                vec.push_back(nums[i]);
                subsequences(v, vec, nums, i + 1);
                vec.pop_back();
                set.insert(nums[i]);
            }
        }
    }
};

以上是我对这两道回溯问题的一些想法,有问题还请在评论区讨论留言~

你可能感兴趣的:(leetcode算法)