Leetcode刷题-回溯相关

回溯法和深度优先搜索
相同:回溯也是基于深度优先搜索的基础上实现的。
不同回溯是给定条件,在深度遍历的过程中,条件一旦不满足了就回头,下面的分支不再继续,因此通常和剪枝相结合,是顺序性推进的;深度遍历更像是全局搜索

(1)1079活字印刷——回溯法解决子集全排列问题–中等

你有一套活字字模 tiles,其中每个字模上都刻有一个字母 tiles[i]。返回你可以印出的非空字母序列的数目。

注意:本题中,每个活字字模只能使用一次。
在这里插入图片描述

其它知识点:
字符串处理
vector遍历
vector去重

class Solution {
    
    
public:
    vector<string> serials;
    //回溯+递归解决全排列问题
    void dfs(vector<int> current_index, string track, string tiles){
    
    
        //如果三个字符都添加过了就返回
        if(current_index.size() == tiles.size()) return;
        for(int i = 0; i< tiles.size(); i++){
    
    
            //字符串拼接字符可以直接用+
            //当前字符已经添加过了
            if(find(current_index.begin(), current_index.end(), i) != current_index.end()) continue;
            current_index.push_back(i);
            track+=tiles[i];
            serials.push_back(track);
            dfs(current_index, track, tiles);
            //恢复现场
            track.pop_back();
            current_index.pop_back();
        }
        return;
    }

    //删除vector中的重复元素
    void erase(){
    
    
    //1.使用sort对vector排序,sort的第三个参数可以使用一个简单的布尔类型函数进行选择,正序或逆序;
    //2.使用unique将所有的重复元素放到末尾,返回的结果是一个迭代器类型的数据,就像vec.begin()那样。
    //3.erase删除重复的内容,删除的区间是左闭右开
    sort(serials.begin(),serials.end());
    serials.erase(unique(serials.begin(),serials.end()),serials.end());
        
    }

    int numTilePossibilities(string tiles) {
    
    
        if(tiles.size() == 1) return 1;
        string track = "";
        vector<int> current_index;
        dfs(current_index, track, tiles);
        // for(auto it = serials.begin(); it != serials.end();it++){
    
    
        //     cout << (*it) << "->";
        // }
        // cout<< serials.size();
        // cout<< endl;
        erase();
        // for(auto it = serials.begin(); it != serials.end();it++){
    
    
        //     cout << (*it) << "->";
        // }
        // cout<< serials.size();
        return serials.size();
    }
};

(2)17. 电话号码的字母组合–中等

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

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

在这里插入图片描述
在这里插入图片描述

其它知识点:
map初始化
字符串拼接字符

class Solution {
    
    
public:
    //map初始化 
    map<char, string> numToChar = {
    
    
        {
    
    '2', "abc"}, {
    
    '3', "def"}, {
    
    '4', "ghi"}, {
    
    '5', "jkl"}, {
    
    '6', "mno"}, {
    
    '7', "pqrs"}, {
    
    '8', "tuv"}, {
    
    '9', "wxyz"}
    };

    vector<string> result;

    void dfs(int i, string combination, string digits){
    
    
        //将该按键对应的字母遍历添加
        for(int j = 0; j < numToChar[digits[i]].size(); j++){
    
    
            combination += numToChar[digits[i]][j];
            if(i == digits.size()-1){
    
    
                result.push_back(combination);
            }
            dfs(i+1, combination, digits);
            //恢复现场
            combination.pop_back();
        
        }
    }

    vector<string> letterCombinations(string digits) {
    
    
        string combination = "";
        //从第一个数字开始,添加对应的字符
        dfs(0, combination, digits);
        return result;
    }
};

(3)39–数组总和–中等

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

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

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

在这里插入图片描述

其它知识点:
排序
剪枝

class Solution {
    
    
public:

    vector<vector<int>> results;

    void dfs(vector<int> result_tmp, int sum, vector<int> candidates, int startid, int target){
    
    
        // for(auto it = result_tmp.begin(); it != result_tmp.end(); it++){
    
    
        //     cout<< *it << "->";
        // }
        // cout<<"sum = " << sum << " startid = "<< startid <<endl;
        //依次取各个数字
        //cout<< startid << endl;
        for(int i = startid; i< candidates.size(); i++){
    
    
            //如果当前字符已经超了,就不需要继续添加后续更大的了,直接返回
            sum = sum + candidates[i];
            result_tmp.push_back(candidates[i]);

            // for(auto it = result_tmp.begin(); it != result_tmp.end(); it++){
    
    
            //         cout<< *it << "->";
            // }
            // cout<< " sum="<<sum<<endl;

            if(sum > target) {
    
    
                //cout<<"overflowed"<<endl;
                return;
            }
            
            else if(sum == target){
    
    
                results.push_back(result_tmp);
                return;
            }
            //此处从i+1开始,序号只增不减,防止重复数据
            else{
    
    
                dfs(result_tmp, sum, candidates, i+1, target);
            }

            

            //小于目标数值可以重复添加
            int addTimes = 1;
            while(sum < target){
    
    
                //cout<< " "<< candidates[i] ;
                sum = sum + candidates[i];
                result_tmp.push_back(candidates[i]);
                addTimes++;

                // for(auto it = result_tmp.begin(); it != result_tmp.end(); it++){
    
    
                //     cout<< *it << "->";
                // }
                // cout<< " sum="<<sum<<endl;

                //如果恰好相等
                if(sum == target) {
    
    
                    //cout<< "add"<<endl;
                    results.push_back(result_tmp);
                    //退出当前层次循环,取下一个数字
                    break;
                }

                else if (sum < target){
    
    
                    dfs(result_tmp, sum, candidates, i+1, target);
                    
                }

                else break;
            }

            //恢复现场
            //cout<<"result_tmp.size = " << result_tmp.size()<<" start poping, addTime="<< addTimes<< endl;
            while(addTimes > 0) {
    
    
                result_tmp.pop_back();
                sum = sum - candidates[i];
                addTimes--;
                //cout<< "result_tmp.size = " << result_tmp.size() << " addTime = " << addTimes << endl;
            }
        }cout<< endl;
    }

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
    
    

        sort(candidates.begin(), candidates.end());
        // cout<<"candidates: ";
        // for(auto it = candidates.begin(); it != candidates.end(); it++){
    
    
        //     cout<< *it << "->";
        // }
        // cout<< endl;
        dfs({
    
    }, 0, candidates, 0, target);
        return results;
    }
};

(4)78子集–中等

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

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

在这里插入图片描述

法一:递归

class Solution {
    
    
public:
    //使用递归和回溯的思想实现
    vector<vector<int>> result;

    void generate(vector<int> nums, vector<int> tmp,int i){
    
    
        if(i>= nums.size()) return;
        //不添加当前数字
        generate(nums, tmp, i+1);
        tmp.push_back(nums[i]);
        //添加当前数字,只有集合更新了,才需要添加到result集合里面
        result.push_back(tmp);
        generate(nums, tmp ,i+1);
        return;
    }

    vector<vector<int>> subsets(vector<int>& nums) {
    
    
        vector<int> tmp;
        int i = 0;
        result.push_back(tmp);
        if(nums.size()==0) return result;
        generate(nums,tmp,i);
        return result;

    }
};

在这里插入图片描述
法二:for循环

class Solution {
    
    
public:
    //用for循环的方式实现
    //每个数选与不选,共有2^n种可能
    
    vector<vector<int>> subsets(vector<int>& nums) {
    
    
        vector<vector<int>> result;
        int all = 1<< nums.size();//用一位表示一个数字,1<<nums.size()即为2^n
        for(int i=0; i<all; i++){
    
    
            vector<int> item;
            for(int j=0; j<nums.size(); j++){
    
    
                //相当于100000...(共j个0,二进制)
                //和2^n个i与各位分别相与,如果i!=0,则表示选中该位
                if(i&(1<<j)){
    
    
                    item.push_back(nums[j]);
                }
            }result.push_back(item);
        }
        

        return result;

    }
};

在这里插入图片描述

(5)90子集二–中等

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

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
在这里插入图片描述
与78子集的思想类似,多了判断是否重复的部分

class Solution {
    
    
public:
    vector<vector<int>> result;

    //此处集合要加上引用
    void generate(vector<int> nums, vector<int> tmp, set<vector<int>>& res_set, int i){
    
    
        if(i == nums.size()) return;
        generate(nums, tmp, res_set, i+1);
        tmp.push_back(nums[i]);
        //如果该子集未重复
        if(res_set.find(tmp) == res_set.end()){
    
    
            res_set.insert(tmp);
            result.push_back(tmp);
        }
        generate(nums, tmp, res_set, i+1);
        return;
    }

    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
    
    
        //先对集合进行排序,升序
        vector<int> tmp;
        result.push_back(tmp);
        if(nums.size() == 0) return result;

        sort(nums.begin(), nums.end(), less<int>());
        //确保集合中的各元素是升序的,以防集合1为[1,2,2],集合2为[2,1,2],但集合不判为相等的情况
        int i = 0;
        
        set<vector<int>> res_set;//用于去重,set去重所用时间复杂度为logN
        res_set.insert(tmp);//集合的底层存贮结构为键值对
        generate(nums, tmp, res_set, i);
        return result;
    }
};

在这里插入图片描述

(6)22括号生成–中等

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
在这里插入图片描述

class Solution {
    
    
public:

    void generate(vector<string> &result, string tmp, int left, int right){
    
    
        if(left == 0 && right == 0) {
    
    //左右括号都用完了就返回
            result.push_back(tmp);
            return;
        }
        //每次递归要么放左括号,要么放右括号
        if(left > 0){
    
    
            generate(result, tmp+"(", left-1, right);
        }
        if(right > left){
    
    //但是右括号是有条件的,有了左括号才能放右括号
            generate(result, tmp+")", left, right-1);
        }
    }

    vector<string> generateParenthesis(int n) {
    
    
        int right = n;
        int left = n;
        vector<string> result;
        string tmp;
        generate(result, tmp, right, left);
        return result;

    }
};

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44343355/article/details/130803849