加载中...

回溯 leetcode

因为所有dfs都可以看成树,所以每个dfs至少有一个参数(高度),并且终止的条件一般是(一个变量等于高度的时候)
回溯即在dfs下面加上一个pop()移除刚进去的元素即可,(vector deque)

需要去重吗?两种方法:排序后数组保存(需要pop) , 不能排序则使用哈希表(一般unordered set,map,数据小还可用数组开a[],弹药分清楚应不应该放在uset)去重(每层都初始化,只记录本层是否前面有没有又过不需要erase,因为进入下一层又会又新的uset)

是排序问题码? 如果是 每次循环从i=0 开始, 如果不是,是组合问题 从i=startindex开始吧
需要排序吗?

for循环起点从那里开始

    是排列问题还是组合问题:排列问题必须是从0开是
          可以重复使用吗?:不可以就是i+1 可以就是i

是不是每个节点都需要记录,还是只需要叶子节点 :关乎于递归结束的设置 是进入递归就直接记录 还是开始点大于等于数组长度就好

需要找到一个就直接返回吗 ? 若是,则dfs返回值用bool(解数独,),进入下一层使用if() 如果为真就返回, 若不是就返回错误!

参考资料
1.
https://leetcode-cn.com/problems/permutations/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liweiw/:

组合数

class Solution {
private:
    vector<vector<int>> result; // 存放符合条件结果的集合
    vector<int> path; // 用来存放符合条件结果
    void backtracking(int n, int k, int startIndex) {//startindex,传递每个开始的地方一定要掌握!!!
        if (path.size() == k) {//终点如果一开始选择了4,会因为长度不够没满而没有放进答案数组里**
            result.push_back(path);
            return;//记得及时返回,不然会往下走一直遍历的 
        }
        for (int i = startIndex; i <= n; i++) {
            path.push_back(i); // 处理节点 横向
            backtracking(n, k, i + 1); // 递归从i+1开始保证不重复,i+1保证了横向遍历
            path.pop_back(); // 回溯,撤销处理的节点
        }
    }
public:
    vector<vector<int>> combine(int n, int k) {
        result.clear(); // 可以不写
        path.clear();   // 可以不写
        backtracking(n, k, 1);
        return result;
    }
};


上面减枝

 void backtracking(int n, int k, int startIndex) {
        if (path.size() == k) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) { // 优化的地方 树宽-(高度-数组个数)+1
//n=4表示从1-4里面选,3;当size=0,即选了0个,那么最多从(3-0)=还需要的元素个数,(n-还需要的个数)等于还能选几个到里面的边界(范围),是肯定会出答案的,
//还需要的数越多,可以选择的范围也越大
//为什么是+1,当n=4,k=4,size=0的时候 循环条件为i<=0,什么进入不了循环
当size=4的时候,n-(k-4)+1,
            path.push_back(i); // 处理节点
            backtracking(n, k, i + 1);
            path.pop_back(); // 回溯,撤销处理的节点
        }
    }

数组总和2 总和等于n dfs的参数 (必须有的树的高度 和宽度,还有 因为需要 记录遍历到的中间值sum 答案)

class Solution {
private:
    vector<vector<int>> result; // 存放结果集
    vector<int> path; // 符合条件的结果
    // targetSum:目标和,也就是题目中的n。
    // k:题目中要求k个数的集合。
    // sum:已经收集的元素的总和,也就是path里元素的总和。
    // startIndex:下一层for循环搜索的起始位置。
    void backtracking(int targetSum, int k, int sum, int startIndex) {
        if (path.size() == k) {//如果长度已经相等 ,必返回,但返回之前发现sum刚好等于目标值
            if (sum == targetSum) result.push_back(path);//还需要放进去
            return; // 如果path.size() == k 但sum != targetSum 直接返回
        }
        for (int i = startIndex; i <= 9; i++) {
            sum += i; // 处理
            path.push_back(i); // 处理
            backtracking(targetSum, k, sum, i + 1); // 注意i+1调整startIndex
            sum -= i; // 回溯
            path.pop_back(); // 回溯
        }
    }

public:
    vector<vector<int>> combinationSum3(int k, int n) {
        backtracking(n, k, 0, 1);
        return result;
    }
};
剪枝
        if (sum > targetSum) { // 剪枝操作,数已经大于就不需要看下面的操作
            return; // 如果path.size() == k 但sum != targetSum 直接返回
        }
        if (path.size() == k) {
            if (sum == targetSum) result.push_back(path);
            return;
        }
        for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) { // 剪枝,数字从小到大找,如果数取了前面的后便就不用取了
            sum += i; // 处理
            path.push_back(i); // 处理
            backtracking(targetSum, k, sum, i + 1); // 注意i+1调整startIndex
            sum -= i; // 回溯
            path.pop_back(); // 回溯
        }
    }

电话号码的字母组合(每个数子选择都是不同元素的组合)

class Solution {
private:
    const string letterMap[10] = {
        "", // 0
        "", // 1
        "abc", // 2
        "def", // 3
        "ghi", // 4
        "jkl", // 5
        "mno", // 6
        "pqrs", // 7
        "tuv", // 8
        "wxyz", // 9
    };//定义对应的字符串,表示这个数组拥有的
public:
    vector<string> result;
    string s;
    void backtracking(const string& digits, int index) {
        if (index == digits.size()) {
            result.push_back(s);//满了就放入结果
            return;
        }
        int digit = digits[index] - '0';        // 将index指向的数字转为int,是哪个数字
        string letters = letterMap[digit];      // 定义字符串赖存放取数字对应的字符集,
        for (int i = 0; i < letters.size(); i++) {
            s.push_back(letters[i]);            // 处理
            backtracking(digits, index + 1);    // 递归,注意index+1,一下层要处理下一个数字了
            s.pop_back();                       // 回溯
        }
    }
    vector<string> letterCombinations(string digits) {
       
        if (digits.size() == 0) {
            return result;
        }
        backtracking(digits, 0);
        return result;
    }
};

组合总和(不同于上面的题,本题没有层数的限制)

如果是一个集合来求组合的话,就需要startIndex,例如:77.组合 (opens new window)
如果是多个集合取组合,各个集合之间相互不影响,那么就不用startIndex,例如:17.电话号码的字母
注意仅仅适用于组合问题

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;

    void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {//需要传入题给数组,目标值,当前sum,初始点
        if (sum > target) {//因为没有对层数限制,只有对和的限制,可以一直递归,递归终止的两个情况,sum>目标值或sum==目标值
            return;
        }
        if (sum == target) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < candidates.size(); i++) {//从起点开始
            sum += candidates[i];
            path.push_back(candidates[i]);
            backtracking(candidates, target, sum, i); //对于可重复这点: 不用i+1了,表示可以重复读取当前的数
            sum -= candidates[i];
            path.pop_back();
        }
    }
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        result.clear();
        path.clear();
        backtracking(candidates, target, 0, 0);
        return result;
    }
};

数组总和二

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;//
    void backtracking(vector<int>& candidates, int target, int sum, int startIndex, vector<bool>& used) {//多了一个数组,为什么是这几个参数,因为全局变量path和result是自己弄的,但是这个candidate和sum是题目给的,不是全局变量,需要传进去否则过不了,sum可以放出去
        if (sum == target) {//
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {//&&的右边是进行了剪枝,因为数组已经排序了
        你只能在答案数组的不同位置有同一个数,不能在同一个位置有不同的数字相同数,这样可能会导致和已经放入数组的答案重复!
    即同一层相同的数证明是第一个分支的真子集
            if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {

//i>0是因为i-可能会造成越界SEGV on unknow address
// 如果和前面一个数相等,而且used[i - 1] == true,说明同一树支candidates[i - 1]使用过,
也就是说,剪枝发生在:同一层数值相同的结点第 22、33 ... 个结点,因为数值相同的第 11 个结点已经搜索出了包含了这个数值的全部结果,同一层的其它结点,候选数的个数更少,搜索出的结果一定不会比第 11 个结点更多,并且是第 11 个结点的子集。

 // used[i - 1] == false,说明左边的candidates[i - 1]使用过, 要对同一树层使用过的元素进行跳过
                continue;
            //左边一个树枝,使用了candidates[i - 1],只有当used为真才是上一层用过的,否则就是同一层左边的人用过的(因为前面的数必然相等且使用过使用的),
            }
            sum += candidates[i];
            path.push_back(candidates[i]);
            used[i] = true;//告诉下一层用了
            backtracking(candidates, target, sum, i + 1, used); // 和39.组合总和的区别1,这里是i+1,每个数字在每个组合中只能使用一次
            used[i] = false;
            sum -= candidates[i];
            path.pop_back();
        }
    }

public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<bool> used(candidates.size(), false);//与上面的题目不同,保存一个used数组用于记录用过的数字的下标,达到去重
        // 首先把给candidates排序,让其相同的元素都挨在一起。
        sort(candidates.begin(), candidates.end());
        backtracking(candidates, target, 0, 0, used);
        return result;
    }
};


使用过分两个维度,一个是横着的,一个是竖着的

分割回文字符串

vector<vector<string>> res;
    vector<string> v;
    void dfs(string &s,int startval){
        if(startval>=s.size()){//进行分割的分割点大于等于要分割的长度说明没得好割了
            res.push_back(v);
        return ;
        }   
        for(int i=startval;i<s.size();i++){//
            if(is(s,startval,i)){//s,0,0表示一个字符字符
                string sub=s.substr(startval,i-startval+1);//函数表示s.substr(pos,len)截取的是string,包含s中从pos开始的len个字符的拷贝
                
                v.push_back(sub);
                dfs(s,i+1);
                v.pop_back();
            }else continue ;
        }
    }
    bool is(string&s ,int start,int end){
        for(int i=start,j=end;i<j;i++,j--){
            if(s[i]!=s[j])return false;

        }return true;

    }
    vector<vector<string>> partition(string s) {
        if(!s.size()) return res;        
        dfs(s,0);
        return res;

          }

复原ip地址 传入(字符串,开始的位置,逗号数量(终止条件)),结果是在原来的字符串上进行修改

startIndex一定是需要的,因为不能重复分割,记录下一层递归分割的起始位置。
本题我们还需要一个变量pointNum,记录添加逗点的数量。
只能分四段,所以递归终止条件是 逗号数量=3的时候,而且如果第四段区间的合法,那么还将其放入结果集中

class Solution {
private:
    vector<string> result;// 记录结果
    // startIndex: 搜索的起始位置,pointNum:添加逗点的数量
    void backtracking(string& s, int startIndex, int pointNum) {
        if (pointNum == 3) { // 逗点数量为3时,分隔结束
            if (isValid(s, startIndex, s.size() - 1)) {//判断第四段子字符串是否合法,如果合法就放进result中
                result.push_back(s);
            }
            return;
        }
        for (int i = startIndex; i < s.size(); i++) {
            if (isValid(s, startIndex, i)) { // 判断 [startIndex,i] 这个区间的子串是否合法,合法就进行下一个区间的处理
                s.insert(s.begin() + i + 1 , '.');  // insert(iterator pos,s)在pos位置的前面一个位置插入一个字符串,这里是在i(i一直是从startindex开始)的后面插入一个逗点,表示处理了
                pointNum++;
                backtracking(s, i + 2, pointNum);   // 将插入逗点之后的串传入,另外下一个子串的起始位置为i+2逗号加1了,但是i还是原来的i,指向那个逗号前面
                pointNum--;                         // 回溯
                s.erase(s.begin() + i + 1);         // erase(pos,n)删除从pos开始的后面的n个字符erase(iterator pos)删除迭代器指向的地方的东西   这里是回溯删掉逗点
            } else break ; // 因为是从左向右截断的,一般都会合法的,当出现不合法时,说明后面的怎么截断都会失败,直接结束本层循环,这里写break是为了更好的理解循环,其实可以写成return
        }
    }
    // 判断字符串s在左闭又闭区间[start, end]所组成的数字是否合法
    bool isValid(const string& s, int start, int end) {
        if (start > end) {
            return false;
        }
        if (s[start] == '0' && start != end) { //弱国这个区间不是只有一个字符那么 那么第一位为0即0开头的数字不合法
                return false;
        }
        int num = 0;
        for (int i = start; i <= end; i++) {
            if (s[i] > '9' || s[i] < '0') { // 遇到非数字字符不合法 这里包括了数字是负数的情况
                return false;
            }
            num = num * 10 + (s[i] - '0');
            if (num > 255) { // 如果大于255了不合法
                return false;
            }
        }
        return true;
    }
public:
    vector<string> restoreIpAddresses(string s) {
        result.clear();
        if (s.size() > 12) return result; //, 算是剪枝了
        backtracking(s, 0, 0);//传入字符串,开始位置,逗号点数
        return result;
    }
};

子集

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {//只需要传入开始的点
        result.push_back(path); // 收集子集,与上面不同,因为要收集的是每个树结点,而不是叶子结点,每次进入的时候就可以放入结果,先收集下面再判断,因为上一层没有搜集
        if (startIndex >= nums.size()) { // 加入长度为1,那么开始的位置需要是0,如果也为1那么搜索应该是停止的,终止条件可以不加,
            return;//当开始的位置大于原来数组的长度就可以返回,当作搜完
        }
        for (int i = startIndex; i < nums.size(); i++) {
            path.push_back(nums[i]);
            backtracking(nums, i + 1);//i+1下一层,
            path.pop_back();
        }
    }
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        
        backtracking(nums, 0);
        return result;
    }
};


自己二

private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex, vector<bool>& used) {
        result.push_back(path);
        for (int i = startIndex; i < nums.size(); i++) {
            // used[i - 1] == true,说明同一树支candidates[i - 1]使用过
            // used[i - 1] == false,说明同一树层candidates[i - 1]使用过
            // 而我们要对同一树层使用过的元素进行跳过
            if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
                continue;
            }
            path.push_back(nums[i]);
            used[i] = true;
            backtracking(nums, i + 1, used);
            used[i] = false;
            path.pop_back();
        }
    }

public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        result.clear();
        path.clear();
        vector<bool> used(nums.size(), false);
        sort(nums.begin(), nums.end()); // 去重需要排序
        backtracking(nums, 0, used);
        return result;
    }

递增子序列 去重,子集,不能重复使用

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {

        if (path.size() > 1) {
            result.push_back(path);
            // 注意这里不要加return,要取树上的节点
        }
        unordered_set<int> uset; // 使用set对本层元素进行去重,如果放在全局变量那么每次都会insert递归先增加一个记录满足不了,知道搜到尽头才擦去这个记录
        for (int i = startIndex; i < nums.size(); i++) {
            if ((!path.empty() && nums[i] < path.back())//使用path.back()前必须使用.empty判断非空。
                    || uset.find(nums[i]) != uset.end()) {//如果路不为空而且将要放入的元素小于路径中的最后一个元素,或者找到这个元素,都说明同一层用过
                    continue;
            }
            uset.insert(nums[i]); // 记录这个元素在本层用过了,本层后面不能再用了,插入这个元素
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        
        backtracking(nums, 0);
        return result;
    }
};

上面的优化版本使用数组左哈希表

void backtracking(vector<int>& nums, int startIndex) {
        if (path.size() > 1) {
            result.push_back(path);
        }
        int used[201] = {0}; // 这里使用数组来进行去重操作,题目说数值范围[-100, 100]开201,这里面还有个0
        for (int i = startIndex; i < nums.size(); i++) {
            if ((!path.empty() && nums[i] < path.back())
                    || used[nums[i] + 100] == 1) {//num[i+100]去除反方的选手
                    continue;
            }
            used[nums[i] + 100] = 1; // 记录这个元素在本层用过了,本层后面不能再用了
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }

全排列 :叶子节点记录, 一个元素在一个组合只能出现一次需要进行used去重,需不要要在递归过程中放置变量i

 vector<vector<int>> result;
    vector<int> path;
    void backtracking (vector<int>& nums, vector<bool>& used) {
        // 此时说明找到了一组
        if (path.size() == nums.size()) {//数组长度相同找到叶子节点
            result.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (used[i] == true) continue; // path里已经收录的元素,直接跳过
            used[i] = true;
            path.push_back(nums[i]);
            backtracking(nums, used);
            path.pop_back();
            used[i] = false;
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {        
        vector<bool> used(nums.size(), false);//创建了一个数组后,直接括号包起来就行了,(元素个数,放置的值)
        backtracking(nums, used);
        return result;
    }

重复数组全排列二

    vector<vector<int>> result;
        vector<int> path;
    
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        
        vector<bool> used(nums.size(), false);//创建了一个数组后,直接括号包起来就行了,(元素个数,放置的值)
        sort(nums.begin(),nums.end());
        backtracking(nums, used);
        return result;  
    }
    
    void backtracking (vector<int>& nums, vector<bool>& used) {
        // 此时说明找到了一组
        if (path.size() == nums.size()) {//数组长度相同找到叶子节点
            result.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (used[i] == true||i>0&&nums[i]==nums[i-1]&&!used[i-1]) continue; // path里已经收录的元素,直接跳过
            //比上面多了一个情况 当值相等而且前面一个数字已经用过的时候 也会跳过
            used[i] = true;
            path.push_back(nums[i]);
            backtracking(nums, used);
            path.pop_back();
            used[i] = false;
        }

重新安排路程

一个机场映射多个机场,机场之间要靠字母序排列,一个机场映射多个机场,可以使用std::unordered_map,如果让多个机场之间再有顺序的话,就是用std::map 或者std::multimap 或者 std::multiset。
使用unordered_map<string, multiset> targets删除元素会受到,迭代器会不见
intunordered_map<string, map<string, int>> targets只要int存在就说明可飞,保留了迭代器顺便还等效于删除了操作

如果“航班次数”大于零,说明目的地还可以飞,如果如果“航班次数”等于零说明目的地不能飞了,而不用对集合做删除元素或者增加元素的操作。
相当于说我不删,我就做一个标记!

五张票六个地点

class Solution {
private:
// unordered_map<出发机场, map<到达机场, 航班次数>> targets
unordered_map<string, map<string, int>> targets;//使用一个哈希图存放题目给的数据,使用map让终点在放进去就自动排序了
bool backtracking(int ticketNum, vector<string>& result) {//返回值的意思是找到了就不用找了,dfs参数是还有票数,答案数组(一定要是传引入)
    if (result.size() == ticketNum + 1) {
        return true;
    }
    for (pair<const string, int>& target : targets[result[result.size() - 1]]) {//以result最后一个点为起点,
//迭代targets[结果集的最后一个]获得以这个点为起点的所有机票,必须是&传引用这样后面才能操作,因为key值不能修改,所以还需要再string前面加const
        if (target.second > 0 ) { // 如果能飞才飞,不能飞的target,此时target表示目的地
            result.push_back(target.first);//能飞放入终点
            target.second--;
            if (backtracking(ticketNum, result)) return true;//bool唯一作用当找到了 就直接返回,因为没有准备专门存放答案的字符串数组,当然你可以准备一个字符串数组
            result.pop_back();
            target.second++;
        }
    }
    return false;
}
public:
    vector<string> findItinerary(vector<vector<string>>& tickets) {//这里一定要是
        targets.clear();
        vector<string> result;//结果是一个string型 的数组
        for (const vector<string>& vec : tickets) {//这里的for,让vec等于vector里每个元素即["JFK","SFO"]
//unordered_map<string, map<string, int>> targets;//使用一个哈希图存放题目给的数据
            targets[vec[0]][vec[1]]++; // 记录映射关系,vec[0]是起飞点字符串,vec[1]是终点字符串
 //逻辑targets[key]=value即 map<string,int>  targets[key][key]=value即 int
        }
        result.push_back("JFK"); // 起始机场
        backtracking(tickets.size(), result);//因为ticket+1数量等于结果的长度
        return result;
    }
};

使用void 的dfs,这么做会导致超时!!!没有找到就及时返回!!!所以还是需要bool

class Solution {
private:
vector<string> res;
// unordered_map<出发机场, map<到达机场, 航班次数>> targets
unordered_map<string, map<string, int>> targets;
void backtracking(int ticketNum, vector<string>& result) {//传引用直接操作地址,否则会逐个复制,效率太低
   if (result.size() == ticketNum + 1) {
       if(!res.size())//只有第一次的时候记录
       res=result;cout<<"找到结果"<<"";
       return ;
   }
   for (pair<const string, int>& target : targets[result[result.size() - 1]]) {
       if (target.second > 0 ) { // 记录到达机场是否飞过了
           result.push_back(target.first);cout<<"放入"<<target.first<<" ";
           target.second--;
           backtracking(ticketNum, result); 
           result.pop_back();cout<<"取出"<<target.first<<" ";
           target.second++;
       }
   }
   return ;
}


};

n皇后

class Solution {
private:
vector<vector<string>> result;
// n 为输入的棋盘大小
// row 是当前递归到棋牌的第几行了
void backtracking(int n, int row, vector<string>& chessboard) {//参数 棋盘还有本层是第几行,总共n行(n皇后),为了递归终止的条件
    if (row == n) {
        result.push_back(chessboard);//到了最后一个就是答案啊了
        return;
    }
    for (int col = 0; col < n; col++) {//从0开始
        if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
            chessboard[row][col] = 'Q'; // 放置皇后
            backtracking(n, row + 1, chessboard);
            chessboard[row][col] = '.'; // 回溯,撤销皇后
        }
    }
}
bool isValid(int row, int col, vector<string>& chessboard, int n) {参数 第几行第几列,棋盘,n皇后
    int count = 0;
    // 检查列
    for (int i = 0; i < row; i++) { // 这是一个剪枝
        if (chessboard[i][col] == 'Q') {检查本行上面每一行的本列元素是不是q这个值
            return false;
        }
    }
    // 检查 45度角是否有皇后
    for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) {//这里的条件一定要写成是&&且否则数组越界
        if (chessboard[i][j] == 'Q') {//检查本行本列左上角的元素是不是放置了皇后
            return false;
        }
    }
    // 检查 135度角是否有皇后
    for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {//检查本行本列右上角是不是放置了皇后,这里条件一定要写成是且&&
        if (chessboard[i][j] == 'Q') {
            return false;
        }
    }
    return true;
}
public:
    vector<vector<string>> solveNQueens(int n) {
        result.clear();
        std::vector<std::string> chessboard(n, std::string(n, '.'));//新建一个棋盘全部是填号了点的
        backtracking(n, 0, chessboard);//从0开始
        return result;
    }
};

解数独 二维递归 两个for循环里放着一个递归

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] != '.') continue;//不是点是数字才需要填充,
            for (char k = '1'; k <= '9'; k++) {     // (i, j) 这个位置放k是否合适
                if (isValid(i, j, k, board)) {
                    board[i][j] = k;                // 放置k
                    if (backtracking(board)) return true; // 如果找到合适一组立刻返回
                    board[i][j] = '.';              // 回溯,撤销k
                }
            }
            return false;                           // 9个数都试完了,都不行,那么就返回false
        }
    }
    return true; // 遍历完没有返回false,说明找到了合适棋盘位置了,一i的那个是这个
}
bool isValid(int row, int col, char val, vector<vector<char>>& board) {
    for (int i = 0; i < 9; i++) { // 判断行里是否重复,
        if (board[row][i] == val) {//不要使用i-1
            return false;
        }
    }
    for (int j = 0; j < 9; j++) { // 判断列里是否重复
        if (board[j][col] == val) {
            return false;
        }
    }
    int startRow = (row / 3) * 3;//这样无论是不是都可以找到所在九宫格的位置第一个格子的坐标
    int startCol = (col / 3) * 3;
    for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复
        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);
    }
};


posted @ 2021-12-13 13:11  liang302  阅读(117)  评论(0编辑  收藏  举报