背景:这是在刷leetcode时,经常遇到的算法。
-
给一个数组
[1,2,3],求元素的所有排列方式 -
eg:
[1,2,3],它的排列方式有六种,123,132,213,231,312,321, -
所有排列方式的总数为
3*2*1=3!=arr.length!,数组长度的阶乘 -
所有排列方式的总数超过
千万时,计算量就会非常恐怖
实现思路
-
遍历数组,将数组转为对象,再遍历对象+回溯,得出所有排列组合
-
第一步,通过arr获取所有排列方式的对象
obj
// 此方法将数组所有可能的排列方式,转换为对象的形式
// 递归到最后一个位置时,使用 # 作为特殊标识,下一步需要使用
// 这个方法的运行时长与arr的长度有关,长度小于等于10都正常,大于10就会非常恐怖
const getObj = (arr) => {
if (arr.length == 1) return { '#': arr[0] }
const obj = {}
for (const key of arr) {
if (!obj[key]) {
obj[key] = getObj(arr.filter(v => v != key))
}
}
return obj
}
getObj(arr)
- 第二步,将对象
obj转回数组allKeys
const allKeys = []
const getKeyArr = (obj) => {
for (const key in obj) {
if (key == '#') {
allKeys.push(obj[key], '-')
return obj[key]
} else {
allKeys.push(key)
getKeyArr(obj[key])
}
}
}
getKeyArr(allobj)
- 第三步,将
allKeys组装成最后需要的样子
// 这里 curItem.slice(0, -v.length) 很重要,需要理解上一步的 allKeys.push(obj[key], '-')
// 把 allKeys 打印出来看着结合理解
const len = arr.join('').length
let curItem = ''
const allChild = allKeys
.join('')
.split('-')
.slice(0, -1)
.map(v => {
curItem = v.length == len ? v : curItem.slice(0, -v.length) + v
return curItem
})
-
核心逻辑到这里就完了,但是给的数组中可能会有相同的元素
-
这种情况可以在
getObj前处理数组arr,对其中重复的元素添加特殊字符,将其变成唯一元素,在第三步之后再通过正则去掉特殊字符还原元素
// 对其中重复的元素添加特殊字符,将其变成唯一元素
let id = 1, ids = []
const uniWords = (arr) => {
const obj = {}
for (let i = 0; i < arr.length; i++) {
if (!obj[arr[i]]) {
obj[arr[i]] = arr[i]
} else {
const uid = '&' + id + '&'
id++
arr[i] = arr[i] + uid
ids.push(arr[i])
}
}
}
...
...
...
// 通过正则去掉特殊字符还原元素
const allUniChild = allChild.map(v => {
return v.replaceAll(/&\d+&/g, '')
})
---------------- 2025-02-18
第二种方式,还是原来的思路,代码上有点区别
const test = () => {
const obj = {}
const fn = (obj, nums) => {
for (const item of nums) {
const arr = nums.filter((v) => v != item)//[1,1,5]这种重复的数据需要优化
if (arr.length == 0) {
obj[item] = item
} else {
obj[item] = {}
fn(obj[item], arr)
}
}
}
fn(obj, nums)
console.log(obj)
const _res = []
const fn2 = (obj, arr) => {
for (const key in obj) {
if (typeof obj[key] != 'object') {
arr.push(key)
_res.push(arr)
} else {
const arr2 = [...arr, key]
fn2(obj[key], arr2)
}
}
}
fn2(obj, [])
console.log(_res)
}
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
//最多九个数字,十个数字就需要五秒左右
console.time('test')
test(nums)
console.timeEnd('test')