认识回溯算法
回溯算法说白了就是穷举法。 不过回溯算法使用剪枝函数,剪去一些不可能到达 最终状态(即答案状态)的 节点 ,从而减少状态空间树节点的生成。 回溯法 是一个既带有系统性又带有跳跃性的的 搜索算法 。回溯算法通常拿来解组合、排列和子集等问题。
模板:
def backtracking():
if ( ):
...
return
for i in range():
...
backtracking()
path.pop()
组合
题目:
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
示例 1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
示例 2:
输入:n = 1, k = 1
输出:[[1]]
有人疑惑为什么到选2的时候只有[3,4]没有1,这是因为如果有1就和前面的[1,2]重复了,后面也是同理
,只要选这个数后面的数字就可以了。
令path=[],观察发现遍历深度为k次,遍历宽度为n-k+len(path[])+1次,当len(path)=k时返回结果
回溯的关键点就是找到遍历深度、宽度和结束条件。到了叶子节点返回的时候要pop(),例如path=[1,2],要path.pop()才可以让path=[1],然后再添加得到[1,3]。
代码:
class Solution(object):
def combine(self, n, k):
"""
:type n: int
:type k: int
:rtype: List[List[int]]
"""
path=[]
res=[]
def backtracking(startIndex,n,k):
if len(path)==k:#当len(path)==k时添加
res.append(path[:])
return
for i in range(startIndex,n-k+2+len(path)):#这里为是n-k+2+len(path),因为要遍历n-k+len(path[])+1 次,左闭右开
path.append(i)
backtracking(i+1,n,k)#回溯
path.pop()#删除
backtracking(1,n,k)
return res
子集
题目:
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
观察发现我们想要的结果就是要画红线的地方,当到num的下标为len(nums)时深度结束,下标为len(nums)时结束,和上一题不同的地方就是不是在叶子节点处收集结果。
代码:
class Solution(object):
def subsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
result=[]
path=[]
def backtracking(nums,startIndex):
result.append(path[:])
for i in range(startIndex,len(nums)):
path.append(nums[i])
backtracking(nums,i+1)
path.pop()
backtracking(nums,0)
return result
图着色
题目:
在一个有n个节点的图上,有m种颜色,要求相邻节点颜色不可以相同,打印出有多少种涂法。
例子:
4个节点,3种颜色
答案:
1213
1312
2123
1321
3132
3231
按照以上两题的思路我们可以知道深度为n,宽度为m;也就是每节点有四种颜色要遍历,因为相邻节点的颜色不可以相同,所以要有一个剪枝函数来判断是否满足条件,当走到最后一个节点时返回。这题目是我的作业所以就用c++来写了。
#include<iostream>
using namespace std;
class GraphColoring{
public:
GraphColoring(int x,int y)
{ vertex=x;
numberColors=y;
Graph=new int*[x];//初始化图
for(int i=0;i<x;i++)
{
Graph[i]=new int[x];
}
for(int i=0;i<x;i++)
{
for(int j=0;j<x;j++)
{
Graph[i][j]=0;
}
}
colour=new int[x];//初始化颜色搭配数组
for(int i=0;i<x;i++)
{
colour[i]=0;
}
}
void InsertEdge();//插入图的边
bool isvalidity(int Index,int currentColor);//判断在该点,这种颜色符不符合条件
void backtracking(int starIndex);
private:
int vertex,numberColors,*colour,**Graph;//vertex为图的节点个数,numberColors为颜色总数,colour为颜色搭配的数组,Graph是图
};
void GraphColoring::InsertEdge()
{
int i,j;
cout<<"输入连接的两个顶点:";
cin>>i>>j;
Graph[i][j]=1;//无向图,直接一次赋两次值
Graph[j][i]=1;
}
bool GraphColoring::isvalidity(int Index,int currentColor)
{
for(int i=0;i<vertex;i++)
{
if(Graph[Index][i]==1 && colour[i]==currentColor)//遍历与该点连接的节点,看颜色是否相同
{
return false;
}
}
return true;
}
void GraphColoring::backtracking(int Index)
{
if (Index==vertex)//当走到最后一个节点时,说明这种涂法符合条件
{
for(int i=0;i<vertex;i++)
{
cout<<colour[i];//输出颜色搭配
}
cout<<endl;
return;
}
for(int currentColor=1;currentColor<numberColors+1;currentColor++)
{
if (not(isvalidity(Index,currentColor)))
{
continue;
}
colour[Index]=currentColor;
backtracking(Index+1);
colour[Index]=0;//删除颜色
}
}
int main(){
int v,c,e;
cout<<"分别输入顶点的个数,颜色的总数,边的条数:";
cin>>v>>c>>e;
GraphColoring G(v,c);
for(int i=0;i<e;i++)
{
G.InsertEdge();
}
G.backtracking(0);
}
n皇后
题目:
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例 1:
带尺寸的图片:
输入:n = 4
输出:[[".Q…","…Q",“Q…”,"…Q."],["…Q.",“Q…”,"…Q",".Q…"]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:[[“Q”]]
我们可以把数组给拆成一行一行,这样我们就可以知道深度为n,每个皇后的有四种摆法,当row等于n时说明这种摆法符合条件,然后通过剪枝函数就可以排除掉那些不符合条件的摆法。
代码:
class Solution(object):
def solveNQueens(self, n):
"""
:type n: int
:rtype: List[List[str]]
"""
res=[]
board=[['.']*n for i in range(n)]
def isvalidity(row,column):
for i in range(len(board)): #判断同列是否满足条件
if board[i][column] == 'Q':
return False
i = row -1
j = column -1
while i>=0 and j>=0:#判断135度是否满足条件
if board[i][j] == 'Q':
return False
i -= 1
j -= 1
i = row - 1
j = column + 1
while i>=0 and j < len(board):#判断45度是否满足条件
if board[i][j] == 'Q':
return False
i -= 1
j += 1
return True
def backtracking(row):
if row==n:
temp=[]
for i in board:
str=''.join(i)
temp.append(str)
res.append(temp)
return
for column in range(n):
if not isvalidity(row,column):
continue
board[row][column]='Q'
backtracking(row+1)
board[row][column]='.'#恢复原来的模样
backtracking(0)
return res