中级之回溯算法

总结:回溯算法实际上是一个类似枚举的搜索尝试过程,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,其特点就是走不通就退回再选另一条路走。 在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。在使用回溯法的时候要注意一点,一定要设置一个向前搜索的终结条件,否则函数就会无限向前搜索从而无法“退回”重新选择。

题目1:电话号码的字母组合

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

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

示例:

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

思路:这个问题实质上就是不同数组间元素的排列组合。用一个map记录不同数字对应的字母集,然后根据所给出的字符串中的数字找到相对应的字母集,最后将各个字母集中的字母排列组合即可。

//用来存储键盘个数字所对应的字母 
map<char,vector<char>> board;
//res是结果集,len是字符串长度,count是子集的长度 
void helper(vector<string> &res,string digits,int len,string s,int count)
{
	if(count==len)
	{
		res.push_back(s);
		return;
	}
	else
	{
		vector<char> temp = board[digits[count]];
		int temp_len = temp.size();
		for(int i=0;i<temp_len;i++)
			helper(res,digits,len,s+temp[i],count+1);	
	}
}
vector<string> letterCombinations(string digits) 
{
	vector<string> res;
	int len = digits.size();
	if(len==0)
		return res;
	board['2'] = {'a','b','c'};
	board['3'] = {'d','e','f'};
	board['4'] = {'g','h','i'};
	board['5'] = {'j','k','l'};
	board['6'] = {'m','n','o'};
	board['7'] = {'p','q','r','s'};
	board['8'] = {'t','u','v'};
	board['9'] = {'w','x','y','z'};
	helper(res,digits,len,"",0);
	return res;      
}

题目2:生成括号

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

例如,给出 n = 3,生成结果为:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

思路:每次在字符串中插入新字符有两种选择:一.如果插入的"("没有到达规定数目,可以继续插入"(";二.如果插入的"("数量大于插入的")"的数量可以插入")"。我们只需要在每次插入的时候进行判断,并且根据所判断的结果向下进行递归就可以得到所有情况。

//rCount代表已经放入字符串的'('的数量,lCount代表已经放入的')'的数量 
void helper(vector<string> &res,string s,int n,int rCount,int lCount)
{
	if(rCount==n&&lCount==n)
	{
		res.push_back(s);
		return;
	}	
	else
	{
		if(rCount<n)
			helper(res,s+"(",n,rCount+1,lCount);
		//放入")"数量必须小于放入"("数量 
		if(lCount<rCount)
			helper(res,s+")",n,rCount,lCount+1);
	}
}
vector<string> generateParenthesis(int n) 
{
     vector<string> result;
     if(n==0)
     	return result;
	 helper(result,"",n,0,0);
	 return result;   
}

题目3:全排列

给定一个没有重复数字的序列,返回其所有可能的全排列。

示例:

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

思路:全排列实质上就是nums[i]与nums[j](j>=i)依次进行互换所得到的新的数组。

void swap(int &num1,int &num2)
{
	int temp = num1;
	num1 = num2;
	num2 = temp;
}
void solve(vector<vector<int> > &result,vector<int> &nums,int len,int count)
{
	if(count==len)
	{
		result.push_back(nums);
		return;
	}
	else
	{
		for(int i=count;i<len;i++)
		{
			swap(nums[count],nums[i]);
			solve(result,nums,len,count+1);
			//这里如果不换回来后面会产生很多重复的数组 
			swap(nums[count],nums[i]);
		}
	}
}
vector<vector<int> > permute(vector<int> &nums) 
{
	vector<vector<int> > result;
	int len = nums.size();
	solve(result,nums,len,0);
	return result;
}

题目4:子集

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

说明:解集不能包含重复的子集。

示例:

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

思路:得到一个数组的所有子集实质上就是从数组中抽取长度为i(0=<i<=len)的所有子集。

//solve函数是用于从nums中抽取长度为aim的子集,index是为了保证抽取的数字在上一次抽取数字的后面,保证不重复 
void solve(vector<vector<int> > &result,vector<int> &subset,vector<int> nums,int len,int aim,int index,int count)
{
	if(count==aim)
	{
		result.push_back(subset);
		return;
	}
	else
	{
		for(int i=index;i<len;i++)
		{
			subset.push_back(nums[i]);
			solve(result,subset,nums,len,aim,i+1,count+1);
			subset.pop_back();
		}
		return;
	}
}
vector<vector<int> > subsets(vector<int>& nums) 
{
	vector<vector<int> > result;
	int len = nums.size();
	if(len==0)
		return result;
	//抽取长度不同的子集 
	for(int i=0;i<=len;i++)
	{
		vector<int> subset;
		solve(result,subset,nums,len,i,0,0);
	} 
	return result; 
}

题目5:单词搜索

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

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

示例:

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

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

思路:遍历二维数组,当遍历到和给定字符串首字母相同的字母时,向其上下左右四个方向搜索,寻找是否有与给定字符串第二个字母相同的字母与其相连,如果有则重复上述过程,直到找到全部字符串,如果没有找到全部字符串则继续向后遍历二维数组。要注意的是遍历过的字符要做标记,否则会产生重复遍历的情况。

//rlen和clen是二维网格的宽和长,len是字符串的长度,x和y是字符的位置 
bool judge(vector<vector<char> > &board,int rlen,int clen,string word,int len,int count,int x,int y)
{
	if(count==len)
		return true;
	else
	{
		char temp = board[x][y];
		//为了避免重复遍历 
		board[x][y] = '#';
		if(x>0&&board[x-1][y]==word[count]&&judge(board,rlen,clen,word,len,count+1,x-1,y))
			return true;
		if(x<rlen-1&&board[x+1][y]==word[count]&&judge(board,rlen,clen,word,len,count+1,x+1,y))
			return true;	
		if(y>0&&board[x][y-1]==word[count]&&judge(board,rlen,clen,word,len,count+1,x,y-1))
			return true;
		if(y<clen-1&&board[x][y+1]==word[count]&&judge(board,rlen,clen,word,len,count+1,x,y+1))
			return true;
		board[x][y] = temp;
		return false;
	}
} 
bool exist(vector<vector<char> > &board, string word) 
{
    int rlen = board.size();
    int clen = board[0].size();
    int len = word.size();
    if(rlen==0||clen==0||len==0)
    	return false;
    for(int i=0;i<rlen;i++)
    {
    	for(int j=0;j<clen;j++)
    	{
    		if(board[i][j]==word[0])
    		{
    			if(judge(board,rlen,clen,word,len,1,i,j))
    				return true;
    		}
    	}
    }
    return false;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值