从今天开始打算把每天的刷题进度同步到CSDN上,一是方便管理刷题进度,二是方便二刷时回来看当时的解题思路。
目前一刷按照代码随想录和力扣HOT100来刷的,今日主题是回溯。
1. LC22括号生成
题目链接
思路:
回溯或也叫DFS过程中,left和right的值在函数传参中改变的,所以不需要单独回溯。
class Solution {
StringBuilder sb = new StringBuilder();
List<String> list = new ArrayList<>();
public List<String> generateParenthesis(int n) {
backTracking(n, n);
return list;
}
public void backTracking(int left, int right){
if (left == 0 && right == 0){
String str = sb.toString();
list.add(str);
return;
}
if (left > 0){
sb.append('(');
backTracking(left-1, right);
sb.deleteCharAt(sb.length()-1);
}
if (right > left){
sb.append(')');
backTracking(left, right-1);
sb.deleteCharAt(sb.length()-1);
}
return;
}
}
2. LC79单词搜索
题目链接
还是用的回溯法。
核心思路:
for循环遍历二维数组,当遍历到的当前值等于字符串的第一个字符时,进入回溯。向下、向上、向右、向左去回溯,找四个方向是否有符合要求的字符串。如有,直接返回true。如没有,则回到最开始的for循环遍历继续遍历二维数组里的下一个字符。
用used数组记录哪个方向已经遍历过了。
两个坑:
1.刚开始只写了backTracking里的代码,没有写exist方法里的for循环,会造成一个问题,如果board[rowIndex][columnIndex]不符合条件时,没有办法使指针移到board里的下一格,只能return false。所以要在exist方法里写for循环,强行使指针不断移动。
2.如果board只有一个字符,且该字符与字符串的第一个字符相等,必须加上最后一个if条件来判断,否则当board只有一个字符时,无论字符串什么内容,都只会返回false。
class Solution {
StringBuilder sb = new StringBuilder();
boolean[][] used;
public boolean exist(char[][] board, String word) {
used = new boolean[board.length][board[0].length];
//必须要写这个for循环
for (int i = 0; i < board.length; i++){
for (int j = 0; j < board[0].length; j++){
if (board[i][j] == word.charAt(0)){
boolean result = backTracking(board, word, i, j);
if (result){
return true;
}
}
}
}
return false;
}
public boolean backTracking(char[][] board, String word, int rowIndex, int columnIndex){
if (sb.length() == word.length()){
return true;
}
if (!used[rowIndex][columnIndex] && board[rowIndex][columnIndex] == word.charAt(sb.length())){
sb.append(board[rowIndex][columnIndex]);
used[rowIndex][columnIndex] = true;
boolean result1 = false;
boolean result2 = false;
boolean result3 = false;
boolean result4 = false;
if (rowIndex <= board.length-2){
result1 = backTracking(board, word, rowIndex+1, columnIndex); //下
if (result1){
return true;
}
}
if (rowIndex >= 1){
result2 = backTracking(board, word, rowIndex-1, columnIndex); //上
if (result2){
return true;
}
}
if (columnIndex <= board[0].length-2){
result3 = backTracking(board, word, rowIndex, columnIndex+1); //右
if (result3){
return true;
}
}
if (columnIndex >= 1){
result4 = backTracking(board, word, rowIndex, columnIndex-1); //左
if (result4){
return true;
}
}
//很重要:['a'], a --> true; ['a'], abc --> false
if (board.length == 1 && board[0].length == 1){
return (sb.length() == word.length());
}
used[rowIndex][columnIndex] = false;
sb.deleteCharAt(sb.length()-1);
}
return false;
}
}
3. 代码随想录 回溯-20. N皇后
题目链接
思路:
class Solution {
char[][] result;
List<List<String>> list = new ArrayList<>();
int num = 0;
public List<List<String>> solveNQueens(int n) {
result = new char[n][n];
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++){
result[i][j] = '.';
}
}
backTracking(0, n);
return list;
}
public void backTracking(int rowIndex, int n){
//终止条件
if (rowIndex == n){
if (num == n){
List<String> path = new ArrayList<>();
for (int i = 0; i < n; i++){
StringBuilder sb = new StringBuilder();
for (int j = 0; j < n; j++){
sb.append(result[i][j]);
}
path.add(sb.toString());
}
list.add(path);
}
return;
}
for (int colIndex = 0; colIndex < n; colIndex++){
if (check(rowIndex, colIndex, n)){
result[rowIndex][colIndex] = 'Q';
num++;
backTracking(rowIndex+1, n);
result[rowIndex][colIndex] = '.';
num--;
}
}
return;
}
//相当于剪枝
public boolean check(int rowIndex, int colIndex, int n){
//判断是否同一列
for (int i = 0; i < rowIndex; i++){
if(result[i][colIndex] == 'Q'){
return false;
}
}
//判断是否左对角线
for (int i = rowIndex-1, j = colIndex-1; i >= 0 && j >= 0; i--, j--){
if (result[i][j] == 'Q'){
return false;
}
}
//判断是否右对角线
for (int i = rowIndex-1, j = colIndex + 1; i >= 0 && j < n; i--, j++){
if (result[i][j] == 'Q'){
return false;
}
}
return true;
}
}
4. 代码随想录 回溯.19重新安排行程
题目链接
如果backTracking所有的情况,返回list.get(0)会导致超时,所以backTracking返回boolean,一旦遇到正确路径就返回,停止搜索。尽管如此,LC上最后一个用例还是超时了,未解决。。。
注意以下这行代码,目的就是对JFK后面的index为1的元素排序,选则最小的那条路径。
Collections.sort(tickets, (a, b) -> a.get(1).compareTo(b.get(1)));
让我们逐步解释:
Collections.sort: 这是 Java 的集合工具类 Collections 中的一个静态方法,用于对集合进行排序。
tickets: 这是要排序的列表对象。
(a, b) -> a.get(1).compareTo(b.get(1)): 这是一个 Lambda 表达式,用于提供比较两个元素的逻辑。在这里,它指定了按照列表中每个元素的索引 1(第二个元素)进行比较。
Lambda 表达式的结构是 (parameters) -> expression 或 (parameters) -> { statements }。在这里,(a, b) 是 Lambda 表达式的参数,a.get(1).compareTo(b.get(1)) 是表达式,表示按索引 1 比较两个元素。
总的来说,这行代码的作用是对 tickets 列表按照每个元素的索引 1 进行升序排序。这假定 tickets 是一个包含列表的列表,而每个内部列表都有至少两个元素(在这里是索引 0 和索引 1)。
注意:在实际使用中,请确保 tickets 不为 null,并且每个内部列表的长度足够大,以防止 IndexOutOfBoundsException 异常。
class Solution {
//List<List<String>> list = new ArrayList<>();
List<String> path = new ArrayList<>();
boolean[] used;
public List<String> findItinerary(List<List<String>> tickets) {
used = new boolean[tickets.size()];
Collections.sort(tickets, (a, b) -> a.get(1).compareTo(b.get(1)));
path.add("JFK");
backTracking(tickets);
//return list.get(0);
return path;
}
public boolean backTracking(List<List<String>> tickets){
if (path.size() == tickets.size()+1){
//list.add(new ArrayList<>(path));
return true;
}
for (int i=0; i<tickets.size(); i++){
if (!used[i] && tickets.get(i).get(0).equals(path.get(path.size()-1))){
used[i] = true;
path.add(tickets.get(i).get(1));
if (backTracking(tickets)){
return true;
}
path.remove(path.size()-1);
used[i] = false;
}
}
return false;
}
}