[算法] 深搜整理

深搜

之前在leetcode上刷题一直都对这个知识点比较模糊,最近,集中写了几道深搜的题目,将自己的思路和题目做了整理写下此篇博客。博客很多题目是网上提供的一些深搜题目,另外一些是leetcode上关于深搜的题目。

六角填数

如下图所示六角形中,填入1~12的数字。使得每条直线上的数字之和都相同。 图中,已经替你填好了3个数字,请你计算星号位置所代表的数字是多少?
[算法] 深搜整理_第1张图片
针对下面代码,将整个六角形的每个元素加上标号,标号的规则是从上往下,如下图:
[算法] 深搜整理_第2张图片
最终12个点的结果:1 8 9 2 7 10 12 6 5 4 11 3

#include 
#include 

using namespace std;

class Solution {
private:
    int n;
    int ff;
    vector Star; // 记录每个点的值
    vector visit; // 记录当前值是否被用过
public:
    Solution(int n_, int find_index) {
        // n为点的个数,ff为我们最终需要找的点的序号(序号从1开始,到12截至,包括12)
        n = n_;
        ff = find_index;
        for (int i = 0; i <= n; i++) {
            Star.push_back(0);
            visit.push_back(false);
        }
        Star[1] = 1;
        Star[2] = 8;
        Star[12] = 3;
        visit[1] = true;
        visit[8] = true;
        visit[3] = true;
    };

    void dfs(int i);

    void check();

    void printInfo();
};

void Solution::printInfo() {
    cout << "Result: " << Star[ff] << endl;
}

void Solution::check() {
    vector tmp;
    tmp.push_back(Star[1] + Star[3] + Star[6] + Star[8]);
    tmp.push_back(Star[1] + Star[4] + Star[7] + Star[11]);
    tmp.push_back(Star[8] + Star[9] + Star[10] + Star[11]);
    tmp.push_back(Star[2] + Star[3] + Star[4] + Star[5]);
    tmp.push_back(Star[2] + Star[6] + Star[9] + Star[12]);
    tmp.push_back(Star[5] + Star[7] + Star[10] + Star[12]);
    for (int i = 1; i < 6; i++) {
        if (tmp[i] != tmp[i - 1])
            return;
    }
    printInfo();
}

void Solution::dfs(int i) {
    if (i == n + 1) {
        check();
    }
    if (i == 1 || i == 2 || i == 12) {
        dfs(i + 1);
    }
    for (int j = 1; j < n + 1; ++j) {
        if (!visit[j]) {
            visit[j] = true;
            Star[i] = j;
            dfs(i + 1);
            visit[j] = false;
        }
    }
}


int main() {
    Solution s(12, 6);
    s.dfs(1);
}

复原IP地址(Leetcode)

给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式。

#include 
#include 
#include 

using namespace std;

class Solution {
public:
    bool check(vector strs, string s) {
        for (int i = 0; i < 4; i++) {
            if (stoi(strs[i]) > 255 || (stoi(strs[i]) == 0 && strs[i] != "0") ||
                (strs[i][0] == '0' && stoi(strs[i]) != 0))
                return false;
        }
        return true;
    }

    void dfs(int step, int begin, const string &s, vector &strs, vector &result) {
        if (step == 4) {
            if (begin == s.size() && check(strs, s)) {
                string strRes = "";
                for (int i = 0; i < 3; i++) {
                    strRes.append(strs[i]);
                    strRes.append(".");
                }
                strRes.append(strs[3]);
                result.push_back(strRes);
            }
        }
        for (int i = 1; i <= 3; i++) {
            if (begin + i <= s.size()) {
                strs.push_back(s.substr(begin, i));
                dfs(step + 1, begin + i, s, strs, result);
                strs.pop_back();
            }
        }
    }

    vector restoreIpAddresses(string s) {
        vector result;
        if (s.size() > 12 || s.size() < 4)
            return result;
        vector strs;
        dfs(0, 0, s, strs, result);
        return result;
    }
};

int main(){
    Solution s;
    string ss = "25525511135";
    vector strs = s.restoreIpAddresses(ss);
    for (int i = 0; i < strs.size(); ++i) {
        cout<< strs[i]<< endl;
    }
}

结果为:
255.255.11.135
255.255.111.35

N皇后问题

在N*N格的国际象棋上摆放N个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法

#include 
#include 

using namespace std;

class Solution {
private:
    vector visited;
    vector checkerboard;
    int count = 0; //存放共有多少种放法
    vector> record; // 存放不同方法
public:
    void solverNQueens(int);

    void dfs(int, int, vector &, vector &);

    bool check(const vector &);

    void printInfo();
};

void Solution::printInfo() {
    cout << "count: " << count << endl;
    for (int i = 0; i < record.size(); i++) {
        for (int j = 0; j < record[i].size(); j++) {
            cout << record[i][j] << " ";
        }
        cout << endl;
    }
}

bool Solution::check(const vector &checkerboard) {
    // 检查是否有同行,同列, 对角线
    for (int i = 0; i < checkerboard.size(); ++i) {
        for (int j = i + 1; j < checkerboard.size(); ++j) {
            if (checkerboard[i] == checkerboard[j] || (abs(checkerboard[i] - checkerboard[j]) == abs(i - j)))
                return false;
        }
    }
    return true;
}

void Solution::dfs(int begin, int n, vector &visit, vector &checkerboard) {
    if (begin == n) {
        if (check(checkerboard)) {
            count++;
            record.push_back(checkerboard);
        }
        return;
    }
    for (int i = 0; i < n; i++) {
        if (!visit[i]) {
            visit[i] = true;
            checkerboard[begin] = i;
            dfs(begin + 1, n, visit, checkerboard);
            visit[i] = false;
        }
    }
}

void Solution::solverNQueens(int n) {
    for (int i = 0; i < n; i++) {
        visited.push_back(false);
        checkerboard.push_back(-1);
    }
    dfs(0, n, visited, checkerboard);
}

int main() {
    Solution s;
    s.solverNQueens(9);
    s.printInfo();
}


子集(Leetcode)

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

输入: nums = [1,2,3]
输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]

class Solution {
public:
    void dfs(int begin, const vector& nums, vector& subRes, vector> &res){
        for(int i=begin; i> subsets(vector& nums) {
        vector> res;
        vector subRes;
        res.push_back(subRes);
        dfs(0, nums, subRes, res);
        return res;
    }
};

PS:觉得这篇博客这题讲的特别好
一开始做这个题我没想到用dfs,因为通常使用dfs都是按某种规则遍历一个数组,然后判断是否符合要求。这个题没法确定一个准确的跳出条件。上述博客讲的思路是:我们添加一个新的元素就是一个新的子集,然后添加这个元素序号后面的元素,这样就可以保证不出现重复,当一层dfs结束,就好pop出一个元素,然后继续。

子集2 (Leetcode)

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

示例:
输入: [1,2,2]
输出: [[2], [1], [1,2,2], [2,2], [1,2], [] ]

class Solution {
public:
    void dfs(int begin, const vector &nums, vector &tmp, vector> &res){
        for(int i=begin; i> subsetsWithDup(vector& nums) {
        sort(nums.begin(), nums.end());
        vector tmp;
        vector> res;
        res.push_back(tmp);
        dfs(0, nums, tmp, res);
        return res;
    }
};

PS: 这题和上面题的区别就是给定数组里有重复元素,所以我们需要将数组排序,并加一个判断 :

while(i

这个判断可以确保在删去元素后,再取元素的时候,不要取和刚刚取过的元素相等的元素

带分数

100 可以表示为带分数的形式:100 = 3 + 69258 / 714 , 还可以表示为:100 = 82 + 3546 / 197
注意特征:带分数中,数字1~9分别出现且只出现一次(不包含0)。
类似这样的带分数,100 有 11 种表示法。

题目要求:
从标准输入读入一个正整数N (N<1000*1000)
程序输出该数字用数码1~9不重复不遗漏地组成带分数表示的全部种数。

#include 
#include 
#include 

using namespace std;

class Solution {
private:
    int count = 0;
    vector visit;
    vector> record;


public:
    void solverFraction(int);

    void printResult();

    void dfs(int, const int &, vector &, vector> &);

    void check(int, vector &, vector> &);

    int sum(int, int, const vector &);
};

void Solution::printResult() {
    for (int i = 0; i < record.size(); i++) {
        for (int j = 0; j < record[i].size(); j++) {
            cout << record[i][j] << " ";
        }
        cout << endl;
    }
    cout << "count: " << count << endl;
}

int Solution::sum(int begin, int end, const vector &tmp) {
    int total = 0;
    for (int i = begin; i < end; i++) {
        total = total * 10 + tmp[i];
    }
    return total;
}

void Solution::check(int target, vector &tmp, vector> &res) {
    int x, y, z;
    for (int i = 1; i < 8; i++) {
        for (int j = i + 1; j < 9; j++) {
            x = sum(0, i, tmp);
            y = sum(i, j, tmp);
            z = sum(j, 9, tmp);
            if ((target - x) * z == y) {
                count++;
                vector tt = {x, y, z};
                record.push_back(tt);
            }
        }
    }
}

void Solution::dfs(int begin, const int &n, vector &tmp, vector> &result) {
    if (begin == 9) {
        check(n, tmp, result);
        return;
    }
    for (int i = 0; i < 9; i++) {
        if (!visit[i]) {
            visit[i] = true;
            tmp.push_back(i + 1);
            dfs(begin + 1, n, tmp, result);
            tmp.pop_back();
            visit[i] = false;
        }
    }

}

void Solution::solverFraction(int n) {
    for (int i = 0; i < 9; i++) {
        visit.push_back(false);
    }
    vector tmp;
    vector> res;
    dfs(0, n, tmp, res);
}

int main() {
    int n;
    while (cin >> n) {
        Solution s;
        s.solverFraction(n);
        s.printResult();
    }
}


结果:
100
3 69258 714
81 5643 297
81 7524 396
82 3546 197
91 5742 638
91 5823 647
91 7524 836
94 1578 263
96 1428 357
96 1752 438
96 2148 537
count: 11

PS:这块刚开始写的有问题,在dfs迭代时候,begin+1写成了i+1,这两者有什么区别呢?目前发现,用i+1 tmp数组的长度可能不是9。i+1适合子集那种问题(生成的结果数组长度不一致,不需要visit数组维护的),begin+1适合结果数组长度固定的。
[算法] 深搜整理_第3张图片

组合(Leetcode)

给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[[2,4], [3,4],[2,3],[1,2], [1,3], [1,4]]

class Solution {
public:
    void dfs(int begin, int end, const vector& nums, vector& tmp, vector>& res){
        if(tmp.size() == end){
            res.push_back(tmp);
            return;
        }
        for(int i=begin; i> combine(int n, int k) {
        vector nums;
        for(int i=1; i<=n; i++){
            nums.push_back(i);
        }
        vector> res;
        vector tmp;
        dfs(0, k, nums, tmp, res);
        return res;
    }
};

全排列(Leetcode)

给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]

class Solution {
public:
    void dfs(const vector &nums,vector> & res, vector & subRes, vector &visited, int begin){
        if(begin == nums.size()){
            res.push_back(subRes);
            return;
        }
        for(int i=0; i> permute(vector& nums) {
        vector> res;
        vector subRes;
        vector visited(nums.size(), false);
        dfs(nums, res, subRes, visited, 0);
        return res;
    }
};

全排列2 (Leetcode)

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

示例:
输入: [1,1,2]
输出:
[ [1,1,2], [1,2,1], [2,1,1] ]

class Solution {
public:
    void dfs(const vector &nums,vector> & res, vector & subRes, vector &visited, int begin){
        if(begin == nums.size()){
            res.push_back(subRes);
            return;
        }
        for(int i=0; i> permuteUnique(vector& nums) {
        vector> res;
        vector subRes;
        vector visited(nums.size(), false);
        sort(nums.begin(), nums.end());
        dfs(nums, res, subRes, visited, 0);
        return res;
    }
};

组合总和(Leetcode)

// 有难度,需要复习

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。

示例 1:
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[ [7], [2,2,3] ]

示例 2:
输入: candidates = [2,3,5], target = 8,
所求解集为:
[ [2,2,2,2], [2,3,3], [3,5] ]

class Solution {
public:
    void dfs(int begin, const vector & condidates, vector & subRes, vector> & res, int target, int total){
        if(total == target){
            res.push_back(subRes);
            return;
        }
        else if(total > target)
            return;
        
        for(int i=begin; i> combinationSum(vector& candidates, int target) {
        vector subRes;
        vector> res;
        sort(candidates.begin(), candidates.end());
        dfs(0, candidates, subRes, res, target, 0);
        return res;
    }
};

组合总和2(Leetcode)

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

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

说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。

示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]

示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[ [1,2,2], [5] ]

class Solution {
public:
    void dfs(int begin, const vector & candidates, vector & subRes, vector> & res, int total, int target){
        if(total > target)
            return;
        else if(total == target){
            res.push_back(subRes);
            return;
        }
        for(int i=begin; i> combinationSum2(vector& candidates, int target) {
        vector subRes;
        vector> res;
        vector visited(candidates.size(), false);
        sort(candidates.begin(), candidates.end());
        dfs(0, candidates, subRes, res, 0, target);
        return res;
    }
};

括号生成(Leetcode)

// 需要复习

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

例如,给出 n = 3,生成结果为:
[ “((()))”, “(()())”, “(())()”, “()(())”, “()()()” ]

class Solution {
public:
    void dfs(vector &res, string tmp, int left, int right){
        if(left == 0 && right == 0){
            res.push_back(tmp);
            return;
        }
        if(left > 0) dfs(res, tmp+"(", left-1, right);
        if(left < right) dfs(res, tmp+")", left, right-1);
    }
    vector generateParenthesis(int n) {
        vector res;
        dfs(res, "", n, n);
        return res;
    }
};

电话号码的字母组合(Leetcode)

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
[算法] 深搜整理_第4张图片

示例:

输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”]

python版本

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        character = {'2': ['a', 'b', 'c'],
                    '3': ['d', 'e', 'f'],
                    '4': ['g', 'h', 'i'],
                    '5': ['j', 'k', 'l'],
                    '6': ['m', 'n', 'o'],
                    '7': ['p', 'q', 'r', 's'],
                    '8': ['t', 'u','v'],
                    '9': ['w', 'x', 'y', 'z']}
        if len(digits) == 0:
            return []
        res = character[digits[0]]
        for dig in digits[1:]:
            dig_map = character[dig]
            tmp_res = res
            res = []
            for ch in dig_map:
                tmp = [tt+ch for tt in tmp_res]
                res.extend(tmp)
             
        return res

C++版本

class Solution {
public:
    vector> charactor = {{'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'}
                                     };
    void dfs(int begin, string digits, vector &res, string tmp){
        if(begin == digits.size() && tmp.size()==digits.size()){
            res.push_back(tmp);
            return;
        }
        for(int i=begin; i cur_char = charactor[digits[i]-'0'-2];
            for(int j=0; j letterCombinations(string digits) {
        vector res;
        if(digits.size() == 0)
            return res;
        dfs(0, digits, res, "");
        return res;
    }
};

分割回文串 (Leetcode)

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

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

示例:

输入: “aab”
输出:
[
[“aa”,“b”],
[“a”,“a”,“b”]
]

class Solution {
public:
    bool isPar(string s){
        int begin = 0;
        int end = s.size()-1;
        while(begin < end){
            if(s[begin] != s[end])
                return false;
            begin++;
            end--;
        }
        return true;
    }
    void dfs(const string & s, int begin, vector> &res, vector & cur_res){
        if(begin >= s.size()){
            res.push_back(cur_res);
            return;
        }
        for(int i=1; i<=s.size()-begin; i++){
            string sub = s.substr(begin, i);
            if(sub[0]==sub[sub.size()-1] && isPar(sub)){
                cur_res.push_back(sub);
                dfs(s, begin+i, res, cur_res);
                cur_res.pop_back();
            }
        }
    }
    
    vector> partition(string s){
        vector> res;
        vector cur_res;
        dfs(s, 0, res, cur_res);
        return res;
    }
};

被围绕的区域(Leetcode)

给定一个二维的矩阵,包含 ‘X’ 和 ‘O’(字母 O)。

找到所有被 ‘X’ 围绕的区域,并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。

示例:

X X X X
X O O X
X X O X
X O X X
运行你的函数后,矩阵变为:

X X X X
X X X X
X X X X
X O X X
解释:

被围绕的区间不会存在于边界上,换句话说,任何边界上的 ‘O’ 都不会被填充为 ‘X’。 任何不在边界上,或不与边界上的 ‘O’ 相连的 ‘O’ 最终都会被填充为 ‘X’。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。

class Solution {
public:
    void dfs(vector> & needChange, int x, int y, const vector> & board){
        if(x < 0 || y < 0 || x >= board.size() || y >= board[0].size()){
            return;
        }
        if(board[x][y] == 'O' && needChange[x][y]){
            needChange[x][y] = false;
            dfs(needChange, x-1, y, board);
            dfs(needChange, x+1, y, board);
            dfs(needChange, x, y-1, board);
            dfs(needChange, x, y+1, board);
        }
    }
    void solve(vector>& board) {
        if(board.size()==0)
            return;
        if(board[0].size()==0)
            return;
        vector> needChange(board.size(), vector(board[0].size(), true));
        for(int i=0; i

单词搜索 (Leetcode)

给定一个二维网格和一个单词,找出该单词是否存在于网格中。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例:

board =
[
[‘A’,‘B’,‘C’,‘E’],
[‘S’,‘F’,‘C’,‘S’],
[‘A’,‘D’,‘E’,‘E’]
]

给定 word = “ABCCED”, 返回 true.
给定 word = “SEE”, 返回 true.
给定 word = “ABCB”, 返回 false.

class Solution {
public:
    void dfs(const vector>& board, const string & word, int x, int y, vector> visited, int begin, bool & result){
        // cout<< x<< " "<< y<< endl;
        if(begin == word.size()){
            result = true;
        }
        if(x < 0 || y < 0 || x >= board.size() || y >= board[0].size())
            return;
        if(board[x][y] == word[begin] && visited[x][y] == false && result==false){
            visited[x][y] = true;
            dfs(board, word, x-1, y, visited, begin+1, result);
            dfs(board, word, x+1, y, visited, begin+1, result);
            dfs(board, word, x, y-1, visited, begin+1, result);
            dfs(board, word, x, y+1, visited, begin+1, result);
        }
    }
    bool exist(vector>& board, string word) {
        if(board.size() == 0)
            return false;
        if(board[0].size() == 0)
            return false;
        bool result = false;
        vector> visited(board.size(), vector(board[0].size(), false));
        for(int i=0; i

你可能感兴趣的:(C++,算法)