代码随想录训练营Day24、25、27、28、29:Leetcode 77、216、17、39、40、131、93、78、90、491、46、47!

Leetccode77:

题目描述:

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

代码及注释详解:

vector<vector<int>>ans;
    vector<int>v;
    vector<vector<int>> combine(int n, int k) {
        dfs(0, 1, k, n);
        return ans;
    }

    //i:已经添加了几个元素了
    //m:从m开始向后选择
    //k:目标枚举个数
    //n:最大元素值
    void dfs(int i, int m, int k, int n) {
        //已经找到了一个组合
        if (i == k) {
            ans.push_back(v);

            return;
        }

        //当前位置选择元素
        for (int j = m; j <= n; j++) {
            v.push_back(j);
            //向后递归
            dfs(i + 1, j + 1, k, n);
            //回溯
            v.pop_back();
        }
    }

Leetccode216:

题目描述:

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

  • 只使用数字1到9
  • 每个数字 最多使用一次 

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

代码及注释详解:

 vector<vector<int>>ans;
    vector<int>temp;
    vector<vector<int>> combinationSum3(int k, int n) {
        dfs(0, 1, k, n);
        return ans;
    }

    //i:第几个元素了
    //j:从哪个元素可以开始选择
    void dfs(int i, int j, int k, int n) {
        if (i == k) {
            //是一个完整的组合,判断元素总和是否达到要求
            int sum = 0;
            for (int j = 0; j < k; j++) {
                sum += temp[j];
            }
            if (sum == n)
                ans.push_back(temp);

            return;
        }

        //枚举当前层每一种情况
        for (int select = j; select <= 9 && (9 - select + 1) >= (k - i); select++) {
            temp.push_back(select);
            //递归到下一层
            dfs(i + 1, select + 1, k, n);
            //回溯
            temp.pop_back();
        }

    }

Leetccode17:

题目描述:

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

代码及注释详解:

//使用二维数组存储方便使用
    char c[10][4] = {
            {0},
            {0},
            {'a','b','c'},
            {'d','e','f'},
            {'g','h','i'},
            {'j','k','l'},
            {'m','n','o'},
            {'p','q','r','s'},
            {'t','u','v'},
            {'w','x','y','z'}
    };
    vector<string>ans;

    //i:到那个数字下标了
    void dfs(int i, string digits, string temp) {
        //此时说明到字符串末尾了,结束搜索
        if (!digits[i]) {
            //加入答案
            ans.push_back(temp);
            return;
        }

        //转成数字
        int k = digits[i] - '0';

        //循环遍历该数字对应的字母
        for (int j = 0; j < 4 && c[k][j]; j++) {
            //对每个字母进行递归搜索
            dfs(i + 1, digits, temp + c[k][j]);
        }

    }

    vector<string> letterCombinations(string digits) {
        if (digits.empty())return ans;
        int len = digits.length();
        dfs(0, digits, "");

        return ans;
    }

Leetccode39:

题目描述:

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

代码及注释详解:

vector<vector<int>>ans;
    vector<int>temp;

    //i:当前可选择的最小下标
    //sum:走过的路径总和
    //tar目标和
    //n数组大小
    //candidates数组
    void dfs(int i, int sum, int tar, int n, vector<int>& candidates) {
        //找到满足和为tar的路径了
        if (sum == tar) {
            //加入路径,结束搜索
            ans.push_back(temp);
            return;
        }

        //遍历枚举可以当前层可以走的所有路径
        for (int j = i; j < n; j++) {
            //如果大于目标值说明该路径不能走,直接跳过
            if (sum + candidates[j] > tar)continue;

            //否则可以走
            temp.push_back(candidates[j]);
            //往下递归
            dfs(j, sum + candidates[j], tar, n, candidates);
            temp.pop_back();
        }

    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        dfs(0, 0, target, candidates.size(), candidates);
        return ans;
    }

Leetccode40:

题目描述:

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次 。

注意:解集不能包含重复的组合。 

代码及注释详解:

//存储答案的数组
    vector<vector<int>>ans;
    //存储当前路径的答案数组
    vector<int>temp;
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        //排序方便剪枝优化
        sort(candidates.begin(), candidates.end());
        dfs(0, 0, target, candidates, candidates.size());
        return ans;
    }


    //dfs每个参数解释
    //index:代表当前可以选择元素的最小下标,刚开始可以选[0,n)
    //sum:表示当前总和
    //target:目标总和
    //元素数组
    //n:数组大小
    void dfs(int index, int sum, int target, vector<int>& candidates, int n) {
        //找到目标路径
        if (sum == target) {
            ans.push_back(temp);
            return;
        }

        //没有找到继续往下找
        for (int i = index; i < n; i++) {
            //剪枝
            //如果能选择的最小元素+sum都大于目标值,之后的递归都不可能满足要求
            if (sum + candidates[i] > target)break;

            //去除重复组合,这个for循环相当于我们在枚举子孩子,如果枚举到两个相同的子孩子则需要去重
            //举个例子!!!
            // [1 1 2] 找3的组合(没有重复组合 )
            //       根 
            //  1     1(这个1就相当于重复的子孩子)    2
            // 1 2   2
            //2
            if (i > index && candidates[i] == candidates[i - 1])continue;

            //递归
            temp.push_back(candidates[i]);
            dfs(i + 1, sum + candidates[i], target, candidates, n);
            //回溯
            temp.pop_back();
        }
    }

Leetccode131:

题目描述:

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 

回文串

 。返回 s 所有可能的分割方案。

代码及注释详解:

 vector<vector<string>>ans;
    vector<string>path;
    vector<vector<string>> partition(string s) {
        dfs(0, s);
        return ans;
    }

    //left当前要分割的起始下标
    void dfs(int left, string s) {
        //没有需要分割的部分了,切割完成,并且每一部分都为回文子串
        if (left == s.size()) {
            ans.push_back(path);
            return;
        }

        //[left,right]切割字串区间,从一个开始切割
        int right = left;

        //只要小于size,right就能一直向后扩展
        while (right < s.size()) {
            //判断当前区间是否为回文串
            int i = left, j = right;
            bool k = true;
            while (i < j) {
                if (s[i] != s[j]) {
                    k = false;
                    break;
                }
                i++; j--;
            }
            //如果是回文串
            if (k) {
                path.push_back(s.substr(left, right - left + 1));
                //递归搜索下一个区间
                dfs(right + 1, s);
                path.pop_back();
            }
            //继续增加长度
            right++;
        }
    }

Leetccode93:

题目描述:

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。

  • 例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245""192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。

给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

代码及注释详解:

vector<string>ans;

    vector<string> restoreIpAddresses(string s) {
        dfs(0, 0, "", s);
        return ans;
    }

    //num表示枚举到第几个整数了 num范围是{0,1,2,3}
    //left代表当前截断的起点
    //path保存之前枚举的结果
    //s即字符串
    void dfs(int num, int left, string path, string s) {
        //枚举结束,得到了一个IP地址
        if (num == 4) {
            //如果该IP地址完整,即使用了字符串里面的全部数字
            if (left == s.size()) {
                //去掉最后一个'.'字符
                //因为每次添加一个整数都是path+"整数."后面跟一个字符'.'
                path[path.size() - 1] = '\0';
                ans.push_back(path);
            }

            return;
        }

        //从当前开始依次枚举
        //[left,left+0],[left,left+1],[left,left+2]这个三个子字符串作为整数
        for (int i = 0; i < 3; i++) {
            int right = left + i;
            //剪枝,举个例子
            //第一个整数只选2,"2."
            //剩下三个整数无论怎么选,最大只能"2.552.551.113"也用不完所有数
            //因此无法满足直接跳过该情况
            if ((4 - num - 1) * 3 < s.size() - right - 1)continue;

            //防止越界
            if (right >= s.size())break;

            //如果该整数起点为0,则只能选0
            if (s[left] == '0') {
                dfs(num + 1, left + 1, path + "0.", s);
                //跳出循环
                break;
            }
            //正常情况
            else {
                //获取所要整数
                int data = stoi(s.substr(left, right - left + 1));
                //判断是否合法
                if (data <= 255)
                    dfs(num + 1, right + 1, path + s.substr(left, right - left + 1) + ".", s);
            }
        }


    }

Leetccode78:

题目描述:

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的

子集

(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

代码及注释详解:

//总子集集合
    vector<vector<int>>ans;
    //当前子集
    vector<int>path;
    vector<vector<int>> subsets(vector<int>& nums) {
        //首先添加空集
        //空集是所有集合的子集
        ans.push_back(path);
        //枚举所有子集
        dfs(0, nums);
        return ans;
    }

    //left,当前层可以添加的最小下标元素
    void dfs(int left, vector<int>& nums) {
        if (left == nums.size()) {

            return;
        }


        //遍历当前层可以添加的所有元素
        for (int i = left; i < nums.size(); i++) {
            //当前子集每添加一个元素都是一个新的子集
            path.push_back(nums[i]);

            ans.push_back(path);
            //递归搜索下一层
            dfs(i + 1, nums);
            path.pop_back();
        }
    }

Leetccode90:

题目描述:

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的 

子集

(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

代码及注释详解:

vector<vector<int>>ans;
    vector<int>path;
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        //由于该集合可能存在重复元素,排序是为了方便去重
        sort(nums.begin(), nums.end());
        ans.push_back(path);
        dfs(0, nums);
        return ans;
    }
    //
    void dfs(int left, vector<int>& nums) {
        if (left == nums.size()) {
            return;
        }

        for (int i = left; i < nums.size(); i++) {
            //当前层已经添加过该元素了,所以应该去重
            if (i > left && nums[i] == nums[i - 1])continue;
            path.push_back(nums[i]);
            ans.push_back(path);
            dfs(i + 1, nums);
            path.pop_back();
        }
    }

Leetccode491:

题目描述:

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

代码及注释详解:

vector<vector<int>>ans;
vector<int>path;

vector<vector<int>> findSubsequences(vector<int>& nums) {

    dfs(0, nums);
    return ans;
}


void dfs(int left, vector<int>& nums) {
    if (left == nums.size()) {
        return;
    }
    //树层去重 m3之前题目都有提到的老弟
    unordered_set<int>uset;

    for (int i = left; i < nums.size(); i++) {
        if (left > 0 && nums[i] < nums[left - 1])continue;
        if (uset.find(nums[i]) != uset.end())continue;
        uset.insert(nums[i]);

        path.push_back(nums[i]);
        if (path.size() > 1)ans.push_back(path);
        dfs(i + 1, nums);
        path.pop_back();
    }

}

Leetccode46:

题目描述:

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

代码及注释详解:

vector<int>temp;
vector<vector<int>>ans;


//2^n到n的映射
unordered_map<int, int>umap;

vector<vector<int>> permute(vector<int>& nums) {
    for (int i = 0; i <= nums.size(); i++) {
        umap[1 << i] = i;
    }
    
    f(0, nums.size(), nums, (1 << (nums.size() + 1)) - 2);

    return ans;
}

//i:选到哪个位置了,总共有[0,m-1]m个位置
//key是二进制状态码,状态压缩 1110代表1、2、3这个几个为都没有被使用过
void f(int i, int m, vector<int>& nums, int key) {
    //选完了
    if (i == m) {
        ans.push_back(temp);
        return;
    }

    //使用状态码快速找到可以使用的元素下标
    for (int k = key; k; k ^= (k & -k)) {
        int o = k & -k;

        //2^n到n的映射
        //j表示选第几个元素
        int j = umap[o];


        //第n个元素 下标对应n-1
        temp.push_back(nums[j - 1]);
        //往下搜索
        f(i + 1, m, nums, key - o);
        temp.pop_back();

    }
}

Leetccode47:

题目描述:

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

代码及注释详解:

vector<vector<int>>ans;
vector<int>path;
int vis[10];
vector<vector<int>> permuteUnique(vector<int>& nums) {
    //排序方便去重
    sort(nums.begin(), nums.end());

    dfs(0, nums);
    return ans;
}

void dfs(int left, vector<int>& nums) {
    if (left == nums.size()) {
        ans.push_back(path);
        return;
    }

    for (int i = 0; i < nums.size(); i++) {
        //该元素已经使用过了
        if (vis[i] == 1)continue;
        //当前层已经枚举过这个子孩子了、去重
        if (i > 0 && nums[i] == nums[i - 1] && vis[i - 1] != 0)continue;
        vis[i] = 1;
        path.push_back(nums[i]);
        dfs(left + 1, nums);
        path.pop_back();
        vis[i] = 0;
    }


}

  • 28
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值