77. 组合

func combine(n int, k int) (ans [][]int) {
    path := []int{}
    var dfs func(int)
    dfs = func(cur int) {
        if len(path) == k {
            ans = append(ans, append([]int{}, path...))
            return
        }
		// 剪枝
        d := k - len(path)
        if n-cur+1 < d {
            return
        }
        for i := cur; i <= n; i++ {
            path = append(path, i)
            dfs(i+1)
            path = path[:len(path)-1]
        }
    }
    dfs(1)
    return
}

216. 组合总和 III

func combinationSum3(k int, n int) (ans [][]int) {
	var com []int
	var dfs func(int, int)
	dfs = func(cur, sum int) {
		if sum > n {
			return
		}
		if len(com) == k {
			if sum == n {
				ans = append(ans, append([]int{}, com...))
			}
			return
		}

		// 还有 k-len(com) 个元素要取, 如果
		for i := cur; i <= 9-(k-len(com))+1; i++ {
			sum += i
			com = append(com, i)
			dfs(i+1, sum)
			sum -= i
			com = com[:len(com)-1]
		}
	}

	dfs(1, 0)
	return
}

17. 电话号码的字母组合

func letterCombinations(digits string) (ans []string) {
	if len(digits) == 0 {
		return
	}
	var path string
	var dfs func(int)
	dfs = func(cur int) {
		if cur == len(digits) {
			ans = append(ans, path)
			return
		}

		var ss []string
		switch digits[cur] {
		case '2':
			ss = []string{"a", "b", "c"}
		case '3':
			ss = []string{"d", "e", "f"}
		case '4':
			ss = []string{"g", "h", "i"}
		case '5':
			ss = []string{"j", "k", "l"}
		case '6':
			ss = []string{"m", "n", "o"}
		case '7':
			ss = []string{"p", "q", "r", "s"}
		case '8':
			ss = []string{"t", "u", "v"}
		case '9':
			ss = []string{"w", "x", "y", "z"}
		}
		for _, s := range ss {
			path += s
			dfs(cur + 1)
			path = path[:len(path)-1]
		}
	}
	dfs(0)
	return
}

39. 组合总和

func combinationSum(candidates []int, target int) (ans [][]int) {
	var path []int
	var dfs func(int, int)
	dfs = func(start, sum int) {
		if sum >= target {
			if sum == target {
				ans = append(ans, append([]int{}, path...))
			}
			return
		}
		// 从 start 开始, 因为可以重复取元素, 但是为了避免集合重复
		// 所以不能再取 i < start 的了
		for i := start; i < len(candidates); i++ {
			path = append(path, candidates[i])
			sum += candidates[i]
			dfs(i, sum)
			path = path[:len(path)-1]
			sum -= candidates[i]
		}
	}
	dfs(0, 0)
	return
}

40. 组合总和 II

如何利用 used 数组来去重

func combinationSum2(candidates []int, target int) (ans [][]int) {
	// 排序, 使得相同值的元素排列在一起
	sort.Ints(candidates)
	// 这个变量帮助去判断在同一层级下, 是否有选取到和前面相同的元素
	used := make([]bool, len(candidates))
	var path []int
	var dfs func(int, int)
	dfs = func(start, sum int) {
		if sum >= target {
			if sum == target {
				ans = append(ans, append([]int{}, path...))
			}
			return
		}
		for i := start; i < len(candidates); i++ {
			// 如果当前选取的元素和数组中的上一个元素的值相同,
			// 那么, 假设当前元素与数组中的上一个元素都为 a
			// 并且!!!⚠️⚠️⚠️
			// 在 used 数组中, 数组的上一个元素并未被选取, 说明当前该元素并非在一个分支下
			// 并非在 dfs 递归的过程中被选取, 而是在 for 循环中被选取
			if i > 0 && candidates[i-1] == candidates[i] && !used[i-1] {
				continue
			}
			path = append(path, candidates[i])
			used[i] = true
			sum += candidates[i]
			dfs(i+1, sum)
			path = path[:len(path)-1]
			used[i] = false
			sum -= candidates[i]
		}
	}
	dfs(0, 0)
	return
}

由于在同一个层级上(例如下图第二层中), 第一次已经选了 1, 而在该树枝的下一层中, 可选元素 【 1 2 3 】 中的 1 与第二层第二个树枝所选择的 1 不仅数值相同, 而且元素是相同的

在树的同一层级中, 我用了第一个 1, 然后回溯之后, 再用了第二个 1, 这种情况是会发生重复的, 要避免这种情况(剪枝)

            【 1  1  1  3 】                 
               /      |                          
         1√   /    1× |                          
             /        |                          
            v         v                          
       【 1 1 3 】  【 1 3 】                        
         /  \                                     
    1√  /    \  1×                                  
       /      \                                   
      v        v                                  
  【 1 3 】   【 3 】

51. N 皇后

func solveNQueens(n int) (ans [][]string) {
	var path []int
	queen := make([]bool, n)
	queenLegal := func() bool {
		for i, p := range path {
			for j := i + 1; j < len(path); j++ {
				if j-i == path[j]-p {
					return false
				}
				if j-i == p-path[j] {
					return false
				}
			}
		}
		return true
	}
	var dfs func()
	dfs = func() {
		if len(path) == n {
			if queenLegal() {
				board := make([]string, n)
				for i, c := range path {
					board[i] = strings.Repeat(".", c) + "Q" + strings.Repeat(".", n-c-1)
				}
				ans = append(ans, board)
			}
			return
		}

		for i := 0; i < n; i++ {
			if queen[i] {
				continue
			}
			queen[i] = true
			path = append(path, i)
			dfs()
			queen[i] = false
			path = path[:len(path)-1]
		}
	}
	dfs()
	return
}

131. 分割回文串

func partition(s string) (ans [][]string) {
	var path []string

	isPalid := func(i, j int) bool {
		for i < j {
			if s[i] != s[j] {
				return false
			}
			i++
			j--
		}
		return true
	}

	var dfs func(int)
	dfs = func(start int) {
		if start >= len(s) {
			ans = append(ans, append([]string{}, path...))
			return
		}

		for j := start; j < len(s); j++ {
			if !isPalid(start, j) {
				continue
			}
			path = append(path, s[start:j+1])
			dfs(j + 1)
			path = path[:len(path)-1]
		}
	}
	dfs(0)
	return
}

93. 复原 IP 地址