【代码随想录——回溯算法——四周目】

1.重新安排行程

在这里插入图片描述

1.1 我的代码,超时通不过

var (
	used   []bool
	path   []string
	res    []string
	isFind bool
)

func findItinerary(tickets [][]string) []string {
	sortTickets(tickets)
	res = make([]string, len(tickets)+1)
	path = make([]string, 0)
	used = make([]bool, len(tickets))
	isFind = false
	start := "JFK"
	path = append(path, start)
	dfs(start, 0, tickets)
	return res
}

func dfs(start string, num int, tickets [][]string) {
	if isFind {
		return
	}
	if num == len(tickets) {
		copy(res, path)
		isFind = true
		return
	}

	for i := 0; i < len(tickets); i++ {
		// 当前机票未使用,且起点为start
		if !used[i] && tickets[i][0] == start {
			used[i] = true
			path = append(path, tickets[i][1])
			dfs(tickets[i][1], num+1, tickets)
			if isFind {
				return
			}
			path = path[:len(path)-1]
			used[i] = false
		}
	}
}

func sortTickets(tickets [][]string) {
	sort.Slice(tickets, func(i, j int) bool {
		if tickets[i][0] == tickets[j][0] {
			return tickets[i][1] < tickets[j][1]
		}
		return tickets[i][0] < tickets[j][0]
	})
}

1.2 正确的解法

首先先把图的邻接表存进字典,并且按字典序排序,然后从‘JFK’开始深搜,每前进一层就减去一条路径,直到某个起点不存在路径的时候就会跳出while循环进行回溯,相对先找不到路径的一定是放在相对后面,所以当前搜索的起点from会插在当前输出路径的第一个位置。

// No.332 重新安排行程
type pair struct {
	// pair储存机票目的地和当前航线是否已经使用
	target  string
	visited bool
}

// 储存pair数组,实现sort接口
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) (res []string) {
	// record储存每个机场可到达的目的地,以及当前航线是否已选择
	targets := make(map[string]pairs, 0)
	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])
	}
	var backtrack func() bool
	backtrack = func() bool {
		if len(tickets)+1 == len(res) {
			// 结果机场数量比票数大1说明已经构建出所有路径,找到了结果
			return true
		}
		// 当前所在机场
		here := res[len(res)-1]
		for i, t := range targets[here] {
			if i > 0 && targets[here][i-1].target == t.target && !targets[here][i-1].visited {
				// 剪枝(非常重要),如果上一个目的地和当前的相同,且上一个没用过,说明是从上一个回溯回来的
				// 上一个不可能那么当前的也不可能,直接跳过
				continue
			}
			// 枚举所有可能的目的地,已经使用过的航线除外
			if !t.visited {
				res = append(res, t.target)
				t.visited = true
				if backtrack() {
					return true
				}
				res = res[:len(res)-1]
				t.visited = false
			}
		}
		return false
	}
	// 所有机票从JFK出发
	res = append(res, "JFK")
	backtrack()
	return
}

2.N皇后

在这里插入图片描述

var (
	res  [][]string
	path [][]int
)

func solveNQueens(n int) [][]string {
	res = make([][]string, 0)
	path = make([][]int, n)
	for i := 0; i < n; i++ {
		path[i] = make([]int, n)
	}
	dfs(n, 0)
	return res
}

func dfs(n int, curRow int) {
	if curRow == n {
		temp := make([]string, 0)
		for i := 0; i < n; i++ {
			str := ""
			for j := 0; j < n; j++ {
				if path[i][j] == 0 {
					str = str + "."
				} else {
					str = str + "Q"
				}
			}
			temp = append(temp, str)
		}
		res = append(res, temp)
	}
	for i := 0; i < n; i++ {
		if canPlaceQueen(curRow, i, n) {
			path[curRow][i] = 1
			dfs(n, curRow+1)
			path[curRow][i] = 0
		}
	}
}

func canPlaceQueen(row, col, n int) bool {
	if row >= n || col >= n || row < 0 || col < 0 {
		return false
	}
	// 检查同行同列的情况
	for i := 0; i < n; i++ {
		if path[i][col] == 1 {
			return false
		}
		if path[row][i] == 1 {
			return false
		}
	}
	// 检查主对角线
	row1, col1 := row, col
	for row1 >= 0 && col1 >= 0 {
		if path[row1][col1] == 1 {
			return false
		}
		row1--
		col1--
	}
	row1, col1 = row, col
	for row1 < n && col1 < n {
		if path[row1][col1] == 1 {
			return false
		}
		row1++
		col1++
	}
	// 检查副对角线
	row1, col1 = row, col
	for row1 >= 0 && col1 < n {
		if path[row1][col1] == 1 {
			return false
		}
		row1--
		col1++
	}
	row1, col1 = row, col
	for row1 < n && col1 >= 0 {
		if path[row1][col1] == 1 {
			return false
		}
		row1++
		col1--
	}
	return true
}

3.解数独

在这里插入图片描述

var (
	chunks     [9][]bool
	rows       [9][]bool
	cols       [9][]bool
	matrixSize int  = 9
	emptyCount int  = 0
	hasFind    bool = false
)

func solveSudoku(board [][]byte) {
	emptyCount = 0
	hasFind = false
	for i := 0; i < matrixSize; i++ {
		chunks[i] = make([]bool, 10) //用来表示每个区域中的数字1-9是否已经被使用
		rows[i] = make([]bool, 10)   //用来表示每行中的数字1-9是否已经被使用
		cols[i] = make([]bool, 10)   //用来表示每列中的数字1-9是否已经被使用
	}
	for i := 0; i < matrixSize; i++ {
		for j := 0; j < matrixSize; j++ {
			if board[i][j] != '.' {
				num := board[i][j] - '0'
				chunkId := findChunkIndex(i, j)
				chunks[chunkId][num] = true
				rows[i][num] = true
				cols[j][num] = true
			} else {
				emptyCount++
			}
		}
	}
	dfs(emptyCount, board, &hasFind)
}

func dfs(emptyCount int, board [][]byte, hasFind *bool) {
	if *hasFind { //在其他分支已经找到答案了
		return
	}
	if emptyCount == 0 { //空格全部填完,找到答案
		*hasFind = true
		return
	}
	for i := 0; i < matrixSize; i++ {
		for j := 0; j < matrixSize; j++ {
			if board[i][j] == '.' {
				for num := byte(1); num <= 9; num++ {
					if canPlaceNumber(i, j, num) {
						chunkId := findChunkIndex(i, j)
						board[i][j] = '0' + num
						chunks[chunkId][num] = true
						rows[i][num] = true
						cols[j][num] = true
						emptyCount--
						dfs(emptyCount, board, hasFind)
						if *hasFind { //在其他分支已经找到答案了
							return
						}
						chunks[chunkId][num] = false
						rows[i][num] = false
						cols[j][num] = false
						board[i][j] = '.'
						emptyCount++
					}
				}
				return
			}
		}
	}
}
//查看在该位置是否能放下该数
func canPlaceNumber(row, col int, num byte) bool {
	//行列中是否已经有这个数
	if rows[row][num] || cols[col][num] {
		return false
	}
	//3*3的块中是否有这个数
	if chunks[findChunkIndex(row, col)][num] {
		return false
	}
	return true
}
//找到所属的3*3的块
func findChunkIndex(row, col int) int {
	return (row/3)*3 + col/3
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值