用于记录为期60天的算法提升过程,今天是第30天,今天的题难哭了,抄代码。
🕸️代码随想录训练营-回溯🕸️
- [332. 🌸重新安排行程🌸](#332. 🌸重新安排行程🌸)
- [51. 🌸N皇后🌸](#51. 🌸N皇后🌸)
- [37. 🌸解数独🌸](#37. 🌸解数独🌸)
332. 🌸重新安排行程🌸
给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。
所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。
例如,行程 ["JFK", "LGA"] 与 ["JFK", "LGB"] 相比就更小,排序更靠前。
假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。
思路:
如果在解题的过程中没有对集合元素处理好,就会死循环。
代码
go
type pair struct {
target string
visited bool
}
type pairs []*pair
func (p pairs) Len() int {
return len(p)
}
func (p pairs) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
func (p pairs) Less(i, j int) bool {
return p[i].target < p[j].target
}
func findItinerary(tickets [][]string) []string {
result := []string{}
// map[出发机场] pair{目的地,是否被访问过}
targets := make(map[string]pairs)
for _, ticket := range tickets {
if targets[ticket[0]] == nil {
targets[ticket[0]] = make(pairs, 0)
}
targets[ticket[0]] = append(targets[ticket[0]], &pair{target: ticket[1], visited: false})
}
for k, _ := range targets {
sort.Sort(targets[k])
}
result = append(result, "JFK")
var backtracking func() bool
backtracking = func() bool {
if len(tickets)+1 == len(result) {
return true
}
// 取出起飞航班对应的目的地
for _, pair := range targets[result[len(result)-1]] {
if pair.visited == false {
result = append(result, pair.target)
pair.visited = true
if backtracking() {
return true
}
result = result[:len(result)-1]
pair.visited = false
}
}
return false
}
backtracking()
return result
}
51. 🌸N皇后🌸
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
思路:
n皇后问题是回溯算法解决的经典问题
代码
go
func solveNQueens(n int) [][]string {
var res [][]string
chessboard := make([][]string, n)
for i := 0; i < n; i++ {
chessboard[i] = make([]string, n)
}
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
chessboard[i][j] = "."
}
}
var backtrack func(int)
backtrack = func(row int) {
if row == n {
temp := make([]string, n)
for i, rowStr := range chessboard {
temp[i] = strings.Join(rowStr, "")
}
res = append(res, temp)
return
}
for i := 0; i < n; i++ {
if isValid(n, row, i, chessboard) {
chessboard[row][i] = "Q"
backtrack(row + 1)
chessboard[row][i] = "."
}
}
}
backtrack(0)
return res
}
func isValid(n, row, col int, chessboard [][]string) bool {
for i := 0; i < row; i++ {
if chessboard[i][col] == "Q" {
return false
}
}
for i, j := row-1, col-1; i >= 0 && j >= 0; i, j = i-1, j-1 {
if chessboard[i][j] == "Q" {
return false
}
}
for i, j := row-1, col+1; i >= 0 && j < n; i, j = i-1, j+1 {
if chessboard[i][j] == "Q" {
return false
}
}
return true
}
37. 🌸解数独🌸
编写一个程序,通过填充空格来解决数独问题。
一个数独的解法需遵循如下规则: 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 空白格用 '.' 表示。
思路:
N皇后问题 (opens new window)是因为每一行每一列只放一个皇后,只需要一层for循环遍历一行,递归来遍历列,然后一行一列确定皇后的唯一位置。 本题就不一样了,本题中棋盘的每一个位置都要放一个数字(而N皇后是一行只放一个皇后),并检查数字是否合法,解数独的树形结构要比N皇后更宽更深。
代码
go
func solveSudoku(board [][]byte) {
var backtracking func(board [][]byte) bool
backtracking = func(board [][]byte) bool {
for i := 0; i < 9; i++ {
for j := 0; j < 9; j++ {
//判断此位置是否适合填数字
if board[i][j] != '.' {
continue
}
//尝试填1-9
for k := '1'; k <= '9'; k++ {
if isvalid(i, j, byte(k), board) == true { //如果满足要求就填
board[i][j] = byte(k)
if backtracking(board) == true {
return true
}
board[i][j] = '.'
}
}
return false
}
}
return true
}
backtracking(board)
}
//判断填入数字是否满足要求
func isvalid(row, col int, k byte, board [][]byte) bool {
for i := 0; i < 9; i++ { //行
if board[row][i] == k {
return false
}
}
for i := 0; i < 9; i++ { //列
if board[i][col] == k {
return false
}
}
//方格
startrow := (row / 3) * 3
startcol := (col / 3) * 3
for i := startrow; i < startrow+3; i++ {
for j := startcol; j < startcol+3; j++ {
if board[i][j] == k {
return false
}
}
}
return true
}