leetcode算法专题训练:八.暴力枚举专题

八.暴力枚举专题


78.子集

题目描述:给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets

解题思路:子集的本质是求 组合 ,此题也实质上是 组合 的变形题,用DFS即可。需要求解出所有解。

时间复杂度:O(n* 2^n),将nums数组的长度看成n,用二进制位的思想组合子集即可算出 时间复杂度

空间复杂度:O(N)

class Solution {
    vector<vector<int>> ans;
public:
    void DFS(vector<int>& nums, int start, vector<int>& path)
    {
        ans.push_back(path);
        for (int i = start; i < nums.size(); i++)
        {
            path.push_back(nums[i]);
            DFS(nums, i + 1, path);
            path.pop_back();
        }
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        if (nums.empty())
            return {};
        vector<int> path;
        DFS(nums, 0, path);
        return ans;
    }
};




90.子集2

题目描述:
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets-ii

解题思路:在 子集1 的基础上进行剪枝即可。

时间复杂度:O(N * 2^n)

空间复杂度:O(N)

class Solution {
    vector<vector<int>> ans;
public:
    void DFS(vector<int>& nums, int start, vector<int>& path)
    {
        ans.push_back(path);
        for (int i = start; i < nums.size(); i++)
        {
            if (start<i && nums[i]==nums[i-1])
                continue;
            path.push_back(nums[i]);
            DFS(nums, i + 1, path);
            path.pop_back();
        }
    }

    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        if (nums.empty())
            return {};
        sort(nums.begin(), nums.end(), std::less<int>());
        vector<int> path;
        DFS(nums, 0, path);
        return ans;
    }
};



77.组合

题目描述:给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combinations

解题思路:DFS,该题与全排列的不同之处在于组合的数字要求递增,那么也就是在DFS中需要设置起始位start,并不断往后回溯。

时间复杂度:O(2^K)

空间复杂度:O(K)

class Solution {
   vector<vector<int>> ans;
public:
    void DFS(int n, int k, int start, vector<int>& path)
    {
        if (path.size() == k)
        {
            ans.push_back(path);
            return;
        }

        for (int i = start; i <= n; i++ )
        {
            path.push_back(i);
            DFS(n, k, i+1, path);
            path.pop_back();
        }
    }


    vector<vector<int>> combine(int n, int k) {
        if (n<=0 || k<=0)
            return {};
        vector<int> path;
        DFS(n, k, 1, path);
        return ans;
    }
};



39.组合总合

题目描述:给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum

解题思路:DFS法,需要设置辅助变量path记录路径,以及每次遍历的起始点start。

时间复杂度:O(N!)

空间复杂度:O(N)

class Solution {
    vector<vector<int>> ans;
public:
    
    void DFS(vector<int>& nums, int start, int target, vector<int>& path)
    {
        if (0 == target)
        {
            ans.push_back(path);
            return;
        }

        for (int i = start; i < nums.size(); ++i)
        {
            if (target < nums[i])
                return;
            path.push_back(nums[i]);
            DFS(nums, i, target - nums[i], path);
            path.pop_back();
        }
    }

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end(), std::less<int>());
        vector<int> path;
        DFS(candidates, 0, target, path);
        return ans;
    }
};



40.组合总合2

题目描述:给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-ii

解题思路:用DFS法解决,需要设置辅助变量path记录路径,并设置起始点start。

时间复杂度:O(N!)

空间复杂度:O(N)

class Solution {
    vector<vector<int>> ans;
public:
    
    void DFS(vector<int>& nums, int start, int target, vector<int>& path)
    {
        if (0 == target)
        {
            ans.push_back(path);
            return;
        }

        for (int i = start; i < nums.size(); ++i)
        {
            if (target < nums[i])
                return;
            if (i>start && nums[i]==nums[i-1])
                continue;
            path.push_back(nums[i]);
            DFS(nums, i + 1, target - nums[i], path);
            path.pop_back();
        }
    }

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end(), std::less<int>());
        vector<int> path;
        DFS(candidates, 0, target, path);
        return ans;
    }
};





46.全排列

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations

解题思路:DFS法,设置path变量用来存放走过的路径,visited用来保存变量是否已经访问过。

时间复杂度:O(N!)

空间复杂度:O(N)

class Solution {
   vector<vector<int>> ans;
public:
    void DFS(vector<int>& nums, vector<int>& path, vector<int>& visited)
    {
        if (path.size() == nums.size())
        {
            ans.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++)
        {
            if (1 == visited[i])
                continue;
            visited[i] = 1;
            path.push_back(nums[i]);
            DFS(nums, path, visited);
            path.pop_back();
            visited[i] = 0;
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        if (nums.empty())
            return {};
        vector<int> path, visited(nums.size(), 0);
        DFS(nums, path, visited);
        return ans;
    }
};



47.全排列2

题目描述:给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations-ii

解题思路:相比于上一题全排列,只需要先进行一次排列,并在过程中剪枝即可。

时间复杂度:O(NlogN + N!)

空间复杂度:O(N)

class Solution {
    vector<vector<int>> ans;
public:
    void DFS(vector<int>& nums, vector<int>& path, vector<int>& visited)
    {
        if (path.size() == nums.size())
        {
            ans.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++)
        {
            if (1 == visited[i])
                continue;
            if (0<i && nums[i]==nums[i-1] && !visited[i-1])//剪枝
                continue;
            visited[i] = 1;
            path.push_back(nums[i]);
            DFS(nums, path, visited);
            path.pop_back();
            visited[i] = 0;
        }
    }

    vector<vector<int>> permuteUnique(vector<int>& nums) {
        if (nums.empty())
            return {};
        sort(nums.begin(), nums.end(), std::less<int>());
        vector<int> path, visited(nums.size(), 0);
        DFS(nums, path, visited);
        return ans;
    }
};



60.排列序列

题目描述:给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
“123”
“132”
“213”
“231”
“312”
“321”
给定 n 和 k,返回第 k 个排列。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-sequence

  • 解法一

解题思路:按照求全排列的思路进行计算,该方法超时

时间复杂度:O(N*N!)

空间复杂度:O(N)

class Solution {
    int cnt = 0;
    string ans;
public:
    void DFS(string& s, int k, string& path, vector<int>& visited)
    {
        if (path.size() == s.size())
        {
            cnt++;
            if (cnt == k)
                ans = path;
            return;
        }

        for (int i = 0; i < s.size(); i++)
        {
            if (visited[i])
                continue;
            visited[i] = 1;
            path.push_back(s[i]);
            DFS(s, k, path, visited);
            path.pop_back();
            visited[i] = 0;
        }
    }

    string getPermutation(int n, int k) {
        if (n<=0 || k<=0)
            return string();
        string s, path;
        for (int i = 1; i <= n; i++)
            s.push_back(i+'0');
        vector<int> visited(n, 0);
        DFS(s, k, path, visited);
        return ans;
    }
};

  • 解法二

解题思路:在上一方法的基础上进行优化,用DFS递归的方法来进行无回溯的一次遍历,由于中间需要计算阶乘,所以时间复杂度为O(N^2)。

时间复杂度:O(N^2)

空间复杂度:O(N)


class Solution {
    string ans;
public:
    int getFactorial(int num)
    {
        int ans = 1;
        for (int i = 1; i <= num; ++i)
            ans *= i;
        return ans;
    }

    void DFS(string& s, vector<int>& visited, string& path, int& k)
    {
        if (0 == k)
            return;
        if (path.size()==visited.size() && 1==k)
        {
            ans = path;
            k = 0;
            return;
        }
        int num = getFactorial(s.size() - path.size());
        if (k > num)
        {
            k -= num;
            return;
        }
        
        for (int i = 0; i < s.size(); ++i)
        {
            if (visited[i])
                continue;
            visited[i] = 1;
            path.push_back(s[i]);
            DFS(s, visited, path, k);
            path.pop_back();
            visited[i] = 0;
        }
    }

    string getPermutation(int n, int k) {
        string s;
        for (int i = 1; i <= n; ++i)
            s.push_back(i + '0');
        vector<int> visited(n, 0);
        string path;
        DFS(s, visited, path, k);
        return ans;
    }
};


31.下一个排列

题目描述:实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/next-permutation

解题思路:两次遍历再加上局部反转即可;第一次遍历从rbegin出发找到第一个降序的数nums[i],第二次遍历从rbegin出发找到第一个大于nums[i]的数,交换后从i的位置往后进行反转。具体代码为两个for if 即可搞定。

时间复杂度:O(N)

空间复杂度:O(1)

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int l = nums.size()-2;
        for (; l >= 0 && nums[l] >= nums[l+1]; --l);
        if (l < 0)
        {
            std::reverse(nums.begin(), nums.end());
        }else
        {
            int r = nums.size() - 1;
            while (nums[l] >= nums[r])
                --r;
            swap(nums[l], nums[r]);
            std::reverse(nums.begin() + l + 1, nums.end());
        }
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值