1.题目链接:332. 重新安排行程
跳过
2.题目链接:51. N 皇后
题目描述:
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
解法:
1.首先N皇后问题,想到回溯。那最原始的思考过程应该是这样的,如果n = 3,那么要三层循环,n=4,就4层循环。那么无法控制循环层数,考虑到用回溯。那么树型图中,节点位置其实就是for循环(处理同一行的每一列),而递归就是一层递归处理一行,其实相当于处理的是二维集合。
2.首先我们创建一个全局变量用来收集所有的符合条件的棋盘,再建立棋盘二维数组,大小为n*n,并将其全部初始化为'.'。
3.三步曲:
①参数 --- chessBoard , n,row。---即传入初始化的棋盘,长度n和行初始位置
②返回值 --- void 因为最后的结果存到了全局变量里。
③终止条件 --- 即叶子节点位置,当row到了棋盘的最后一行的下一行的时候,说明整个棋盘已经添加完毕了,此时我们要收集结果(即最后的结果集中存储的是多个不同的棋盘即多个二维数组---链表)。
那这样不就把非法的结果也收集了吗?我们会在单层逻辑的时候,如果非法我们就跳过,所以最后到叶子节点收集的时候都是合法的结果。
④单层逻辑 --- for(一行中的每一列){
1)处理元素 --- if(row,j,chessBoard){即row,j这个位置是合法的话,我们就将元素赋值
chessBoard【row】【j】= ‘Q’;
2)递归 --- backtracking(chessBoard,n,row+1);
3)回溯 --- chessBoard【row】【j】= ‘.’;
}else{
continue;
}
⑤写一个函数判断位置是否合法 --- 判断行是否重复,判断列是否重复,判断对角是否重复。
1)但这里判断的时候不用判断行是否重复,因为我们在一行中添加元素的时候,一次只添加一个元素,所以不用判断行是否重复。
2)判断列的时候,因为我们要向当前行的该位置添加元素,所以呢只需判断当前行之前的列的位置上是否有重复的元素。这个同理在判断对角。
3)判断对角的时候,一个是45°一个是135°,即一个是行列都减1,一个是行减1列加1。
⑥而在此题中leetCode给的返回值是链表型的,所以我们要将chessBoard这个二维数组变成链表,其中存的元素类型是字符串(即将二维数组变成一个链表,链表中存储的元素类型为字符串型即可),我们就要再写一个函数用来将二维数组转成链表。
⑦最后在主函数中调用递归函数,然后返回结果集。
下面为代码(java):
3.题目链接:37. 解数独
题目描述:
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.' 表示。
解法:
1.本题呢相当于一个二维递归的过程,我们要横向纵向遍历棋盘中的每一个元素,然后在棋盘中没有元素的位置,遍历1~9个数字,看其是否是我们要求解的数独。故是个二维递归---即三维的处理过程。
2.三步曲:
①参数 --- board
②返回值 --- boolean 因为我们求的是一个解,而且题目中说了只有一个合法解,所以呢当我们求到了一个合法的解要立即返回(即当一个树枝是合法解,我们就不去遍历其他树枝了就直接返回了)
③终止条件 --- 此题不用写终止条件,为什么呢?因为当有合法解的时候,整个棋盘会遍历完,直接就跳出循环过程了,下面会有个return。而当出现不合法解的时候,我们会在单层逻辑中直接return,所以不用写终止条件。
④单层逻辑 --- for(行){
for(列){
if(board【i】【j】!= ‘.’){continue;} ---即如果遇到非空的直接跳过
1)处理元素(处理的是为空的位置) --- for(char k = '1'~9){
if(isValid(i,j,k,board)) --- 即如果当前位置插入k是合法的话 {
board【i】【j】= k;
2)递归 --- backtracking(board);
boolean result = backtracking(board)
if(result == true){return true;}---即返回值是boolean型的,要承接,如果下一层是true说明找到了对应的数独,直接返回不用进行回溯,就将合法的元素放到了该位置。如果下一层返回是false,那么说明下一层找不到合法的元素了,那么就回到本层向后遍历,即取其它的树枝继续寻找,就用到了回溯。
3)回溯 --- board【i】【j】= ‘.’
}
4)return false(即如果某个位置1~9都不符合的话,那就返回false,说明没有合适的数添加)
}
}
⑤return true;--- 最后整个棋盘都遍历完没有返回false,那就说明棋盘中所有空的位置都添加完成且到了棋盘末尾,到了叶子节点的位置,return true;
这里返回true后会一层层返回到上面那个return true的地方---说明为空的位置都添加完毕了,再一层层向上返回,最后该函数的返回值为true,说明已经得到了一个合法的数独。
⑥isValid 函数要判断行、列重复和九宫格重复,对于九宫格重复,因为其下标为0~8,所以每3个元素的开始位置要统一---可以用(row/3) * 3,和(comlumn/3) *3来表示起始位置,长度都是到+3。这样就划分好了九宫格的范围。然后进行比较是否重复即可。
⑦最后在主函数中调用递归函数即可,因为leetCode中会给输入的board,那么调用递归函数后相当于就将board给填上值了,到时候可能leetCode中会重新输出新的board。
下面为代码(java):
4.总结:
①安排行程问题:二刷再来。
②N皇后问题:
1)是处理二维数组的过程,我们在节点位置的for处理的是同一行的不同列,而递归则是进行不同行的处理。
2)要注意终止条件是当row == n的时候才收集棋盘,因为如果 row == n-1的时候就收集的话,那么相当于最后一行还没处理。
3)而在写判断某个位置放Q是否合法的函数时候,我们要注意的就是,对角线处理(45°和135°都要处理)。
4)在本题中最后的返回结果是链表型的,那么我们收集的二维数组(棋盘)就要转成链表型的,故还要写的转换函数。
③解数独问题:
1)是处理三维数组的过程,也是二维递归的过程。即我们要分别横向和纵向遍历board,同时在棋盘中位置为'.'的位置我们要判断加入1~9中的哪一个,所以是二维递归的过程。
2)那么判断是否合法的函数,就是行列九宫格是否重复。注意的是遇到true就返回,因为本题中我们只求一种合法的数独就可以,同时本题也声明了只有一种合法的结果,所以当递归函数为true的时候,我们就要返回true。
3)下面为什么要有回溯过程呢?因为如果递归函数返回了false,那么说明我们要回到当前层从新向后去寻找合法的数字,所以有了回溯过程。
4)在for(1~9)外部,如果都找不到,就return false。
5)在最开始的两层for循环的外部,return true,因为此时就相当于遍历到了整个树的叶子节点,说明结束了没有false返回,那就返回true,然后再一层一层的跳到上面的return true的地方,再一层层的继续向上返回true,最后整个函数返回true,说明找到合法的数独。