-
332重新安排行程
- 代码随想录 (programmercarl.com)
-
第一印象
- 一刷回溯困难题。想法是根据to查找下一个from,查到就继续向下查找,查不到就进行回溯。path长度满足条件就压入结果集,最后再根据字典序返回最先的即可。
-
讲解观后感
- 第一印象中没有思考全面的,这道题目有几个难点:**
- 一个行程中,如果航班处理不好容易变成一个圈,成为死循环
- 有多种解法,字母序靠前排在前面,让很多同学望而退步,如何该记录映射关系呢 ?
- 使用回溯法(也可以说深搜) 的话,那么终止条件是什么呢?
- 搜索的过程中,如何遍历一个机场所对应的所有机场。
- 本题解题的关键在于容器的选择和利用,对于代码的操控要求很高。
-
解题代码
-
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{}
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
}
-
51N皇后
- 代码随想录 (programmercarl.com)
-
第一印象
- 一刷经典回溯难题,简单逻辑是安排第一个皇后的位置后标记下一个皇后不可以摆的位置,然后向下搜索,把所有皇后摆放完成便压入结果集。如何标记不可摆的位置不容易想出来。
-
讲解观后感
- 卡尔明确给出了棋盘的宽度就是for循环的长度,递归的深度就是棋盘的高度,这样就可以套进回溯法的模板里了。
- 用皇后们的约束条件,来回溯搜索这棵树,只要搜索到了树的叶子节点,说明就找到了皇后们的合理位置了
-
解题代码
-
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解数独
- 代码随想录 (programmercarl.com)
-
第一印象
- 一刷回溯困难题。知道应该使用暴力搜索的回溯法,但是又不用什么条件的和逻辑向下搜索
-
讲解观后感
- 本题类似于N皇后,不过是要在行和列上同时遍历。回溯函数返回值选择true,因为是要寻找唯一达到叶子节点的路径。
-
解题代码
-
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
}
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
}