代码随想录算法训练营第二十五天|非递减子序列、全排列、重新安排行程、N皇后、解数独

非递减子序列 leetcode 491

树层去重,树枝不需要去重。因为同一层如果出现相同的数,前面的数已经把所有情况取完了。

写法一:unordered_set去重 O(n*2^n) 递归树时间复杂度2^n,在每层中set去重时间复杂度为n。

每一层递归都重新创建一个set记录本层出现过的元素,所以在递归之后set不需要进行重塑。

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void backtracking(vector<int>& nums,int startIndex){
        if(path.size()>1) res.push_back(path);
        unordered_set<int> set;
        if(startIndex>=nums.size()) return;
        for(int i=startIndex;i<nums.size();i++){
            if(!path.empty()&&nums[i]<path.back()||set.find(nums[i])!=set.end()) continue;
            path.push_back(nums[i]);
            set.insert(nums[i]);
            backtracking(nums,i+1);
            path.pop_back();
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        res.clear();
        path.clear();
        backtracking(nums,0);
        return res;
    }
};

写法二:数组去重

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void backtracking(vector<int>& nums,int startIndex){
        if(path.size()>1) res.push_back(path);
        int used[201]={0};
        for(int i=startIndex;i<nums.size();i++){
            if(!path.empty()&&nums[i]<path.back()
            ||used[nums[i]+100]==1) continue;
            path.push_back(nums[i]);
            used[nums[i]+100]=1;
            backtracking(nums,i+1);
            path.pop_back();
        }
    }
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        res.clear();
        path.clear();
        backtracking(nums,0);
        return res;
    }
};

全排列 leetcode 46

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void backtracking(vector<int>& nums,vector<bool>& used){
        if(path.size()==nums.size()){
            res.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();i++){
            if(used[i]==true) continue;
            used[i]=true;
            path.push_back(nums[i]);
            backtracking(nums,used);
            path.pop_back();
            used[i]=false;  
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        res.clear();
        path.clear();
        vector<bool> used(nums.size(),false); 
        backtracking(nums,used);
        return res;
    }
};

出现的错误

递归函数开始后在递归结束的if条件中没有写return;

全排列Ⅱ leetcode 47

与全排列上一题相比多加一个树层去重,因为数组中会出现重复元素,所以先排序,之后树层去重,相邻的相同元素的两条树枝,其中上一条树枝中已经包含了所有情况,减去剩余的一条树枝。

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    void backtracking(vector<int>& nums,vector<bool>& used){
        if(path.size()==nums.size()){
            res.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();i++){
            if(i>0&&nums[i]==nums[i-1]&&used[i-1]==false //树层去重
            ||used[i]==true) continue; //树枝去重
            path.push_back(nums[i]);
            used[i]=true;
            backtracking(nums,used);
            path.pop_back();
            used[i]=false;
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        res.clear();
        path.clear();
        sort(nums.begin(),nums.end());
        vector<bool> used(nums.size(),false);
        backtracking(nums,used);
        return res;
    }
};

nums[i]==nums[i-1]&&used[i-1]==false   同一层树层使用过,经过回溯为false。

nums[i]==nums[i-1]&&used[i-1]==true    同一条树枝使用过。

重新安排行程 leetcode 332

解析

1.使用unordered_map<出发机场,map<到达机场,tickets中包含此到达机场的航班次数>>,其中包含此到达机场的航班次数,就说明该机场还可以飞。

题目要求按字母大小排序,那么使用map存放到达机场的时候就会自己按照字母顺序排序。

 for(const vector<string>& vec:tickets){
            targets[vec[0]][vec[1]]++;
        }

范围for循环初始化targets,记录出发机场以及到达机场,++记录到达机场次数。

2.回溯递归函数的返回值为什么是bool类型?

如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。

3.for(pair<const string,int>& target_map:targets[res[res.size()-1]])

递归中的范围for循环用于遍历,当前结果集中最后一个元素作为出发机场的所有到达机场次数,如果到达机场次数>0表示对应到达机场还没有用完,直接放入结果集,向下递归以到达机场作为出发机场继续for循环遍历下一层。

测试用例

[["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]]

首先将所有车票初始化tickets之后,map中到达机场value的值分别为,JFK=1,SFO=2,ATL=2

主函数中会先将JFK插入到res结果集中,进入递归for循环后,查询JFK作为出发机场的到达机场,分别是SFO和ATL,因为map是经过排序的,所以先遍历ATL(ATL=1),再以ATL作为出发机场遍历到达机场,分别是JFK和SFO,因为map有序所以先遍历JFK(JFK=0),再以JFK作为出发机场遍历到达机场,只有SFO(SFO=1),再以SFO作为出发机场遍历到达机场,只有ATL(ATL=0),再以ATL作为出发机场遍历到达机场,分别是JFK和SFO,因为map有序所以应该先选JFK但是JFK作为到达机场对应value已经为0,所以选择SFO(SFO=0),此时res中JFK-ATL-JFK-SFO-ATL-SFO符合递归结束条件。如下图(虚线地方我认为不会遍历到):

class Solution {
public:
    unordered_map<string,map<string,int>> targets;
    bool backtracking(int ticketNum,vector<string>& res){
        if(res.size()==ticketNum+1) return true;
        for(pair<const string,int>& target_map:targets[res[res.size()-1]]){
            if(target_map.second>0){
                res.push_back(target_map.first);
                target_map.second--;
                if(backtracking(ticketNum,res)) return true;
                res.pop_back();
                target_map.second++;
            }
        }
        return false;
    }
    vector<string> findItinerary(vector<vector<string>>& tickets) {
        targets.clear();
        vector<string> res;
        for(const vector<string>& vec:tickets){
            targets[vec[0]][vec[1]]++;
        }
        res.push_back("JFK");
        backtracking(tickets.size(),res);
        return res;
    }
};

总结

targets[vec[0]][vec[1]]++;

其中++符号是对targets中嵌套的map中对应键的值进行自增操作。

具体解释一下:

targets[vec[0]],首先,使用vec[0]作为外层unordered_map的键来查找或创建一个键值对。如果这个键值对不存在,则会创建一个新的map<string,int>并将其与vec[0]键关联起来。

vec[1],接着,使用vec[1]作为内层map的键来查找或创建一个键值对。如果这个键值对不存在,则会创建一个新的int值并将其与vec[1]键关联起来。

++:最后,对内层map中vec[1]键对应的值执行自增操作。

出现的错误

递归函数中for循环遍历中第一个if语句条件写成了target_map.second==1,同一个机场可能是多个机票的到达机场。

N皇后 leetcode 51

class Solution {
private:
    vector<vector<string>> res;
    void backtracking(int n,int row,vector<string>& chess){
        if(n==row){
            res.push_back(chess);
            return;
        }
        for(int i=0;i<n;i++){
            if(isVaild(row,i,chess,n)){
                chess[row][i]='Q';
                backtracking(n,row+1,chess);
                chess[row][i]='.';
            }
        }
    }
    bool isVaild(int row,int col,vector<string>& chess,int n){
        for(int i=0;i<row;i++){
            if(chess[i][col]=='Q') return false;
        }
        for(int i=0;i<col;i++){
            if(chess[row][i]=='Q') return false;
        }
        for(int i=row-1,j=col-1;i>=0&&j>=0;i--,j--){
            if(chess[i][j]=='Q') return false;
        }
        for(int i=row-1,j=col+1;i>=0&&j>=0;i--,j++){
            if(chess[i][j]=='Q') return false;
        }
        return true;
    }
public:
    vector<vector<string>> solveNQueens(int n) {
        res.clear();
        vector<string> chess(n,string(n,'.'));
        backtracking(n,0,chess);
        return res;
    }
};

总结

1.其中isValid函数中对棋盘行的检查可以去掉,因为递归函数中控制每一行只放一个皇后。

2.string(n,'.');  创建一个字符串长度为n且每一个字符都是“.”。

解数独 leetcode 37

如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。

class Solution {
private:
    bool backtracking(vector<vector<char>>& board){
        for(int i=0;i<board.size();i++){
            for(int j=0;j<board[0].size();j++){
                if(board[i][j]=='.'){
                    for(char k='1';k<='9';k++){
                        if(isValid(i,j,k,board)){
                            board[i][j]=k;
                            bool res=backtracking(board);
                            if(res) return true;
                            board[i][j]='.';
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }
    bool isValid(int row,int col,int val,vector<vector<char>>& board){
        for(int i=0;i<board.size();i++){
            if(board[i][col]==val) return false; //排查列有无相同元素
        }
        for(int i=0;i<board.size();i++){
            if(board[row][i]==val) return false; //排查行有无相同元素
        }
        //找到当前3×3格子第一行第一列在整个格子中的位置,排查当前3×3格子有无重复元素
        int startRow=(row/3)*3;
        int startCol=(col/3)*3;
        for(int i=startRow;i<startRow+3;i++){
            for(int j=startCol;j<startCol+3;j++){
                if(board[i][j]==val) return false;
            }
        }
        return true;
    }
public:
    void solveSudoku(vector<vector<char>>& board) {
        backtracking(board);
        return;
    }
};
  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值