回溯
- 77. 组合
- 216. 组合总和 III
- 17. 电话号码的字母组合
- 39. 组合总和
- 40. 组合总和 Ⅱ
- 131. 分割回文串
- 93. 复原 IP 地址
- 491. 递增的子序列
- 46. 全排列
- 47. 全排列 Ⅱ
- 332. 重新安排行程
- 51. N 皇后
- 37. 解数独
77. 组合
/**
* @param {number} n
* @param {number} k
* @return {number[][]}
*/
var combine = function(n, k) {
let ans = []
let each = []
const backtracking = (n, k, start) => {
if(each.length === k){
ans.push([...each])
return
}
for(let i = start; i <= n - (k - each.length) + 1; i++ ){
each.push(i)
backtracking(n, k, i + 1)
each.pop()
}
}
backtracking(n, k, 1)
return ans
};
216. 组合总和 III
/**
* @param {number} k
* @param {number} n
* @return {number[][]}
*/
var combinationSum3 = function(k, n) {
let each = []
let ans = []
const backtracking = (sum, start, k, n) => {
if(sum > n) return
if(each.length === k){
if(sum === n){
ans.push([...each])
}
return
}
for(let i = start; i <= 9 - (k - each.length) + 1; i++){
each.push(i)
backtracking(sum + i, i + 1, k, n)
each.pop()
}
}
backtracking(0, 1, k, n)
return ans
};
17. 电话号码的字母组合
/**
* @param {string} digits
* @return {string[]}
*/
var letterCombinations = function(digits) {
if(digits.length === 0) return []
let ans = []
let each = []
let obj = {
2: 'abc',
3: 'def',
4: 'ghi',
5: 'jkl',
6: 'mno',
7: 'pqrs',
8: 'tuv',
9: 'wxyz'
}
const backtracking = (start, digits) => {
if(each.length === digits.length){
ans.push(each.join(''))
return
}
for(let i = 0; i < obj[digits[start]].length; i++){
each.push(obj[digits[start]][i])
backtracking(start + 1, digits)
each.pop()
}
}
backtracking(0,digits)
return ans
};
39. 组合总和
- 因为每个元素的数量没有限制,也就是下一层中可选择的位置是可以包括这次的位置。
- 下一层的 start 可以从i开始,如果要求最多只能出现一次那么只能从 i+1 开始
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum = function(candidates, target) {
let ans = []
let each = []
const backtracking = (nowSum, start) => {
if(nowSum > target) return
if(nowSum === target){
ans.push([...each])
return
}
for(let i = start; i < candidates.length; i++){
each.push(candidates[i])
backtracking(nowSum + candidates[i], i)
each.pop()
}
}
backtracking(0,0)
return ans
};
40. 组合总和 Ⅱ
与上面这一题的不同点:
-
candidates 中的每个数字在每个组合中只能使用一次
下一层的取数就要从 i +1 开始
-
数组candidates的元素是有重复的
横向的 for 循环需要去重,在这之前要保证candidates 中的数字是有序的,所以需要先排序,当
i > start && candidates[i] === candidates[i - 1]
直接考虑下一个,跳过 -
剪枝: 当前元素加上后大于 target 跳过
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum2 = function(candidates, target) {
let ans = []
let each = []
const backtracking = (start, nowSum) => {
if(nowSum === target){
ans.push([...each])
return
}
for(let i = start; i < candidates.length; i++){
if(nowSum + candidates[i] > target) continue
if(i > 0 && i > start && candidates[i] === candidates[i - 1]) continue
each.push(candidates[i])
backtracking(i + 1, nowSum + candidates[i])
each.pop()
}
}
candidates.sort((a, b ) => a - b)
backtracking(0, 0)
return ans
};
131. 分割回文串
- i 为切割的地方,s[start~i]为切割下来的子串,先判断子串是否符合回文,不符合就跳过,符合就放在each数组中,然后继续寻找下一个切割位置
- 递归结束的条件为start已经到了字符串s的末尾
- 判断回文字符串:使用双指针,一个从前到后,一个从后到前,遇到第一个不相等的字符就直接返回 false
/**
* @param {string} s
* @return {string[][]}
*/
const isHuiWen = (s, start, end) => {
let i = start
let j = end
while(i < j){
if(s[i] !== s[j]) return false
i ++
j --
}
return true
}
var partition = function(s) {
let ans = []
let each = []
const backtracking = (start) => {
if(start >= s.length){
ans.push([...each])
return
}
for(let i = start; i < s.length ; i++){
if( isHuiWen(s, start, i) === false) continue
each.push(s.slice(start, i + 1))
backtracking(i + 1)
each.pop()
}
}
backtracking(0)
return ans
};
93. 复原 IP 地址
/**
* @param {string} s
* @return {string[]}
*/
var restoreIpAddresses = function(s) {
if(s.length <= 3) return []
let ans = []
let each = []
const judge = (str) => {
if(str.length === 0 || +str > 255 || (str.length >= 2 && str[0] == 0) )
return false
return true
}
const backtracking = (start, pointNum) => {
if(pointNum === 3){
if(judge(s.slice(start,s.length))){
ans.push(each.join('.') + '.' + s.slice(start,s.length))
}
return
}
for(let i = start; i < s.length; i++){
if(s.slice(start, i + 1).length > 3) break
if( !judge(s.slice(start, i + 1)))
continue
each.push(s.slice(start, i + 1))
backtracking(i + 1, pointNum + 1)
each.pop()
}
}
backtracking(0, 0)
return ans
};
491. 递增的子序列
/**
* @param {number[]} nums
* @return {number[][]}
*/
var findSubsequences = function(nums) {
let ans = []
let each = []
const backtracking = (start) => {
if(each.length > 1){
ans.push([...each])
}
let map = new Array(201)
map.fill(0)
for(let i = start; i < nums.length; i++ ){
if(nums[i] < each[each.length - 1] || map[nums[i] + 100] === 1) continue
map[nums[i] + 100] = 1
each.push(nums[i])
backtracking(i + 1)
each.pop()
}
}
backtracking(0)
return ans
};
46. 全排列
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function(nums) {
let ans = []
let each = []
let used = new Array(21)
used.fill(0)
const backtracking = () => {
if(each.length === nums.length){
ans.push([...each])
return
}
for(let i = 0; i < nums.length; i++){
if(used[nums[i] + 10]) continue
used[nums[i] + 10] = 1
each.push(nums[i])
backtracking()
used[nums[i] + 10] = 0
each.pop()
}
}
backtracking()
return ans
};
47. 全排列 Ⅱ
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permuteUnique = function(nums) {
let ans = []
let each = []
let used = new Array(10)
used.fill(0)
const backtracking = () => {
if(each.length === nums.length){
ans.push([...each])
return
}
for(let i = 0; i < nums.length; i++){
if(used[i] || (i > 0 && nums[i] === nums[i-1] && used[i-1]))
continue
each.push(nums[i])
used[i] = 1
backtracking()
used[i] = 0
each.pop()
}
}
nums.sort((a,b) => a - b)
backtracking()
return ans
};
332. 重新安排行程
/**
* @param {string[][]} tickets
* @return {string[]}
*/
var findItinerary = function(tickets) {
let ans = ['JFK']
let obj = {}
// 将出发到达的关系储存在 obj 里
for (ticket of tickets){
const [from, to] = ticket
if( !obj[from] ){
obj[from] = []
}
obj[from].push(to)
}
// 将到达地点排序
for (city in obj){
obj[city].sort()
}
const backtracking = () => {
if(ans.length === tickets.length + 1){
return true
}
if( !obj[ans[ans.length - 1]] || !obj[ans[ans.length - 1]].length)
return false
for(let i = 0; i < obj[ans[ans.length - 1]].length; i++){
let city = obj[ans[ans.length - 1]][i]
obj[ans[ans.length - 1]].splice(i,1)
ans.push(city)
if( backtracking() ) return true
ans.pop()
obj[ans[ans.length - 1]].splice(i,0,city)
}
}
backtracking()
return ans
};
51. N 皇后
/**
* @param {number} n
* @return {string[][]}
*/
var solveNQueens = function(n) {
let ans = []
let each = []
const isValid = (row, col) => {
// 列
for(let i = 0; i < row; i++)
if(each[i][col] === 'Q') return false
// 斜对角线
for(let i = row - 1,j = col - 1; i >= 0 && j >= 0; i--, j--)
if(each[i][j] === 'Q') return false
// 对角线
for(let i = row - 1, j = col + 1; i >= 0 && j <n ; i --, j ++)
if(each[i][j] === 'Q') return false
return true
}
const backtracking = (row) => {
if(row === n){
ans.push([...each])
return
}
let col = new Array(n)
col.fill('.')
for(let i = 0; i < n; i++){
if( !isValid(row, i) ) continue
col.splice(i, 1, 'Q')
each.push(col.join(''))
backtracking(row + 1)
each.pop()
col.splice(i, 1, '.')
}
}
backtracking(0)
return ans
};
37. 解数独
/**
* @param {character[][]} board
* @return {void} Do not return anything, modify board in-place instead.
*/
var solveSudoku = function(board) {
const isValid = (row, col, k) => {
for(let i = 0; i < 9; i++){
if(board[row][i] === k) return false
if(board[i][col] === k) return false
}
let startI = Math.floor(row/3)*3
let startJ = Math.floor(col/3)*3
for(let i = startI; i < startI + 3; i++){
for(let j = startJ; j < startJ + 3; j++){
if(board[i][j] == k) return false
}
}
return true
}
const backtracking = () => {
for(let i = 0; i < board.length; i++){
for(let j = 0; j < board[0].length; j++){
if(board[i][j] !== '.') continue
for(let k = 1; k <= 9; k++){
let str = k.toString()
if(isValid(i,j,str)){
board[i][j] = str
if( backtracking() ) return true
board[i][j] = '.'
}
}
return false
}
}
return true
}
backtracking()
return board
};