代码随想录算法训练营第三十天|332.重新安排行程、51. N皇后、37. 解数独

代码随想录算法训练营第三十天|332.重新安排行程、51. N皇后、37. 解数独

332.重新安排行程

题目链接

问题简述:从给定的飞机票中,输出能使用全部飞机票的行程,如果存在多个行程,选择字母排序靠前的行程。

思考:第一次做困难的题,这道题与之前最大的区别在于回溯函数有boolean的返回值,选择排序靠前行程在于先对目的地进行排序,最先找到的行程就是靠前的行程。最后还有一个用例没过,二刷再看吧,可以剪枝。

算法思路

  • 定义res为最终的路径,定义path为当前的路径,used数组标记机票使用情况。
  • 主函数先对机票按照目的地进行排序,并在path中添加初始地JKK,在进行回溯。
  • 每次回溯时传入飞机票集合,返回值为boolean,指的是是否找到了一个路径。如果路径中包含了票数加一个地点,则将路径保存到res返回true;如果机票没用完,则遍历所有机票,如果当前票已被使用则跳过本次循环,若未被使用,找到出发地为path最后一个地方的票并在path中加入改票的目的地。修改这张票的使用情况,然后进行递归,如果为true则返回true,即找到了一条路线。没找到路径则修改used并回溯path。如果循环结束没找到路径,则返回fasle。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class Solution {
    //定义返回值
    List<String> res = new ArrayList<>();
    List<String> path = new ArrayList<>();
    int[] used;
    public List<String> findItinerary(List<List<String>> tickets) {
      	//将每张票按照目的地进行排序
        Collections.sort(tickets, (a, b) -> a.get(1).compareTo(b.get(1)));
        path.add("JFK");
        used = new int[tickets.size()];
        backtracking(tickets);
        return res;
    }
    //回溯函数
    public boolean backtracking(List<List<String>> tickets){
        //path中元素个数和nums相等时保存path
        if (path.size() == tickets.size() + 1){
            res = path;
            return true;
        }
        //每次都遍历所有结点,如果当前结点已经在path中则跳过
        for (int i = 0; i < tickets.size(); i++) {
            //如果当前结点已经被使用,说明当前票已被使用
            if (used[i] == 1) continue;
            if (used[i] == 0 && tickets.get(i).get(0).equals(path.get(path.size() - 1))){
                path.add(tickets.get(i).get(1));
                used[i] = 1;
              	//找到了路径直接返回true
                if (backtracking(tickets)) return true;
              	//没找到路径进行回溯
                used[i] = 0;
                path.remove(path.size() - 1);
            }
        }
      	// 如果遍历所有结点都没有找到有效路径,返回false
        return false;
    }
}

51. N皇后

题目链接

问题简述:在棋盘上放置皇后棋子,要求放置的棋子行列和斜45、135度都不再有其他棋子。

思考:之前处理的都是数组,这道题处理棋盘稍微不一样些。注意在检查有效时只需要判断之前行的元素。

算法思路

  • 定义一个二维数组chessboard初始化全为.;定义函数chessToList将二维数组转换为输出结果;isValid用来判断当前位置是否有效。row为当前处理的行。

  • 每次递归从先判断是否已经处理完了最后一行,如果处理完了则row等于n,此时将二维数组转换为输出结果并返回;如果没处理完最后一行,依次遍历当前行元素,并判断该位置是否有效,如果有效则放置棋子,再进行递归下一行,递归结束进行回溯。最终res存储返回的结果。

import java.util.ArrayList;
import java.util.List;

class Solution {
    //定义返回值
    List<List<String>> res = new ArrayList<>();

    public List<List<String>> solveNQueens(int n) {
        char[][] chessboard = new char[n][n];
        // 初始化棋盘
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                chessboard[i][j] = '.';
            }
        }
        backtracking(n, 0, chessboard);
        return res;
    }

    //回溯函数
    public void backtracking(int n, int row, char[][] chessboard){
        if(row == n){
            res.add(chessToList(chessboard, n));
            return;
        }
        for (int i = 0; i < n; i++) {
            if (isValid(chessboard, row, i, n)){
                chessboard[row][i] = 'Q';
                backtracking(n, row + 1, chessboard);
                chessboard[row][i] = '.';
            }
        }
    }

    //二维数组转为list
    public List<String> chessToList(char[][] chessboard, int n){
        List<String> path = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            StringBuilder s = new StringBuilder();
            for (int j = 0; j < n; j++) {
                s.append(chessboard[i][j]);
            }
            path.add(new String(s));
        }
        return path;
    }

    //判断位置是否有效
    public boolean isValid(char[][] chessboard, int row, int i, int n){
        for (int j = 0; j < n; j++) {
            if (chessboard[row][j] == 'Q') return false;
            if (chessboard[j][i] == 'Q') return false;
        }
        
        // 检查45度对角线
        for (int i1=row-1, j1=i-1; i1>=0 && j1>=0; i1--, j1--) {
            if (chessboard[i1][j1] == 'Q') {
                return false;
            }
        }

        // 检查135度对角线
        for (int i1 = row-1, j1 = i+1; i1 >=0 && j1 <=n-1; i1--, j1++) {
            if (chessboard[i1][j1] == 'Q') {
                return false;
            }
        }
        return true;
    }
}

37. 解数独

题目链接

问题简述:填满给定的数独。

思考:这个和第一道相似地方在于回溯函数返回值都为boolean。然后递归中的循环是二层循环。结束条件就是遍历完所有的结点。

算法思路:遍历所有9*9结点,判断该结点是否有数字。如果没有数字依次将1~9放入,判断该数字是否符合要求,如果符合要求则填入该数字。再进行递归,如果递归返回true则直接返回true,再进行回溯。如果跳出1~9的数字循环说明没找到有效填充方式,则返回fasle。如果某次递归遍历完了所有结点,则返回true,此时找到了一种有效的填充方式。

class Solution {
    public void solveSudoku(char[][] board) {
        backtracking(board);
    }

    public boolean backtracking(char[][] board){
        //遍历所有结点
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                //如果结点不是数字
                if (board[i][j] == '.'){
                    //遍历所有1~9的数字
                    for (int k = 1; k < 10; k++) {
                        //判断k能否放置
                        if (incol(board, i, j, k)){
                            board[i][j] = (char)(k + '0');
                            if (backtracking(board)) return true;
                            board[i][j] = '.';
                        }
                    }
                    //如果没有数字可以放置,返回 false
                    return false;
                }
            }
        }
        //格子都填满了
        return true;
    }

    public boolean incol(char[][] board, int row, int col, int i){
        char ch = (char)(i + '0');
        for (int j = 0; j < 9; j++) {
            if (board[row][j] == ch) return false;
            if (board[j][col] == ch) return false;
        }
        // 检查 3x3 宫
        int boxRow = row / 3 * 3;
        int boxCol = col / 3 * 3;
        for (int j = 0; j < 3; j++) {
            for (int k = 0; k < 3; k++) {
                if (board[boxRow + j][boxCol + k] == ch) return false;
            }
        }
        return true;
    }
}

感想

诶。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理和应用能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值