Bootstrap

回溯算法(相关解题):

求子集序列:

解题思路:

  • 已知原集合的数据位数为N,则可以通过二进制比对原来集合,二进制位为1则输出集合上的该位数据,为0则空,二进制的01排序规律与子集的输出一致
  • 由集合的位数可以判断出二进制的范围 0 ~(1<<N)
    • 第一重循环:for (int i = 0; i < (1 << n); i++)
  • 由二进制的0 1 判断当前集合的该位数字是否输出
    • 第二重循环判断集合每一位进行比较:for (int j = 0; j < N; j++)
  • 二层循环判断循环位是否输出:if (i & (1 << j))
  • cout << str[j] << " ";
  1. 有一个集合由A-Z这26个字母组成,前N位的子集输出,打印这个集合的所有子集,每个子集一行,写C代码实现,不能使用递归
void Subset(string str, int n)
//有一个集合由A-Z这26个字母组成,前N位的子集输出
//打印这个集合的所有子集,每个子集一行,写C代码实现,不能使用递归
{
	for (int i = 0; i < (1 << n); i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (i & (1 << j))
				cout << str[j] << " ";
		}
		cout << endl;
	}
}
int main()
{
	string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	Subset(str, 6);
	return 0;
}

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

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

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>>v;
        vector<int>tmp;
        int n=nums.size();
        for(int i=0;i<(1<<n);i++)
        {
            tmp.clear();
            for(int j=0;j<n;j++)
            {
                if(i&(1<<j))
                    tmp.push_back(nums[j]);
            }
            v.push_back(tmp);
        }
        return v;
    }
};

求数据的全排列:

函数:bool next_permutation(起始迭代器,结束迭代器);

在调用 next_permutation 之前,你需要确保 nums 是按字典序排列的升序或降序都可以,但通常选择升序),因为 next_permutation 会生成给定序列的下一个排列。如果 nums 不是有序的,那么第一次调用 next_permutation 将不会得到正确的结果

我们知道有一全排列的函数可以直接使用,该函数返回可否继续排列的bool值

int main()
{
	vector<int>v{ 1,2,3,4 };
	do {
		for (auto it : v)
			cout << it << " ";
		cout << endl;
	} while (next_permutation(v.begin(), v.end()));
	return 0;
}

自己实现算法解析:

//解题精髓:
一个数字:本身一个
两个数字:{1,2}->{1,2},{2,1}
三个数字:{1,2,3}->{1,{2,3}}
					 {2,{}} 
					 {3,{}}

四个数字:{1,2,3,4}->{1,{2,3,4}}
						{2,{}} 
						{3,{}}
						{4,{}}
划分大边距到小边距 的交换
当交换距离为1时,表示当前唯一,无需交换,插入数组
从大边距缩减,并且大边距可以通过交换,得到所有可能性
class Solution {
public:
    void swap(int&a,int&b)
    {
        int tmp=a;
        a=b;
        b=tmp;
    }
    vector<vector<int>> _permute(vector<vector<int>>&v,vector<int>& nums,int b,int e)
    {
        if(e-b==1)
            v.push_back(nums);
        else
        {
            for(int i=b;i<e;i++)
            {
                swap(nums[b],nums[i]);
                _permute(v,nums,b+1,e);
                swap(nums[b],nums[i]);
            }
        }
        return v;
    }
    vector<vector<int>> permute(vector<int>& nums)
    {
        //自己实现:
        vector<vector<int>>v;
        return _permute(v,nums,0,nums.size());

}
    //调用函数实现:
    // vector<vector<int>> permute(vector<int>& nums) {

        // vector<vector<int>> v;
        // sort(nums.begin(),nums.end());
        // do{
        //     v.push_back(nums);
        // }while(next_permutation(nums.begin(),nums.end()));
        // return v;
    // }
};

N皇后:

N*N 的棋盘上,有N位皇后,但是!两两皇后不能在同一行、同一列、同一对角线!

解题思路:

  • N行N列中,存放N位皇后
  • 我们尝试在一行中的每个位置放置皇后,并判断该位置是否与先前的其他皇后冲突
  • 冲突判断,所有皇后不在同一列:v[j] == v[i],一对角线:abs(v[i] - v[j]) == (i - j)
  • 我们只关心不冲突的情况下,冲突时则回溯到上一级,继续for()判断这一行下一列位置
  • 不冲突时,默认这一行该位置已经确定,递归调用处理下一行需要确定的位置_solveNQueens(s,v,n,in+1)
  • 当我们确定了所有N皇后位置时,则插入vector<vector<string>>&s
class Solution {
public:
    bool Checkpos(vector<int>& v, int in) {  
        for (int i = 1; i <= in; i++) {  
            for (int j = i-1; j>=0; j--) { // 修改循环条件以避免越界  
                if (v[j] == v[i] || abs(v[i] - v[j]) == (i - j)) {  
                    return true;  
        } } }  
        return false;  
    }
    void To_string(vector<vector<string>>&s,vector<int>v)
    {
        vector<string>t(v.size(),string(v.size(),'.'));
        for(int i=0;i<v.size();i++)
        {
            t[i][v[i]]='Q';     
        }
        s.push_back(t);
    }
    void _solveNQueens(vector<vector<string>>&s,vector<int>&v,int n,int in)
    {
        for(int i=0;i<n;i++)
        {
            if(in<n)
            v[in]=i;
            if(!Checkpos(v,in))
            {
                if(in==n-1)
                    To_string(s,v);
                else
                _solveNQueens(s,v,n,in+1);
            }
        }
        return ;
    }
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>>s;
        vector<int>v(n);
        _solveNQueens(s,v,n,0);
        return s;
    }
};

矩阵中的路径

class Solution {
    vector<vector<int>>f = { {1,0},{-1,0},{0,1},{0,-1} };
public:
    bool _exist(vector<vector<char>>& board, string word, vector<vector<bool>>& visited,int i, int j, int len) {

        if (len == word.size())
        {
            return true;
        }
        visited[i][j] = true;
        for (int k = 0; k < f.size(); k++)
        {
            int h = f[k][0] + i;
            int l = f[k][1] + j;
            if (h >= 0 && h < board.size() && l >= 0 && l < board[0].size())
            {
                if (board[h][l] == word[len] && visited[h][l] == false)
                    if (_exist(board, word, visited,  h, l, len + 1))return true;
            }
               
        }
        visited[i][j] = false;  // 回溯,标记为未访问       
        return false;
    }
    bool exist(vector<vector<char>>& board, string word) {
        int h = board.size();
        int l = board[0].size();
        vector<vector<bool>>visited(h, vector<bool>(l, false));

        for (int i = 0; i < h; i++)
        {
            for (int j = 0; j < l; j++)
            {
                if (board[i][j] == word[0])
                {
                    if (word.size() == 1)return true;
                    if (_exist(board, word, visited, i, j, 1))
                        return true;
                }
            }
        }
        return false;
    }
};

我们需要关注每次访问后对被访问结点作标记,但是未访问成功后,需要撤除标记

以及在找到完整的word语句后,递归调用的返回值,依次返回给上一级直到原始调用层

;