数组全排列问题

Posted 前端精髓

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数组全排列问题相关的知识,希望对你有一定的参考价值。

题目:

给定一个数组,打印出数组的所有排列。比如数组为[1, 2, 3],那最终输出为:

[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

就是把数组的所有顺序的可能都输出出来。

解题思路

1、递归解法

思路

递归的主旨思想就是把问题转化成一个或几个更小规模的子问题,比如说[1, 2, 3]的全排列不好处理,那我们固定第一位是1,然后求[2, 3]的全排列,然后再把第一位固定为2,求[1, 3]的全排列,最后把第一位固定成3,求[1, 2]的全排列。这样,经过层层推演,可以把题目转换成求长度为1的数组的全排列,很显然就得到答案了。

进一步解析

上面的思想是基本没什么问题的,但是怎么实现固定第一位呢,这里有个比较巧妙的方法,就是让第一位分别和后面几位进行交换,这样每个数字都会被固定在第一位一次,然后递归进行后面的操作,注意为了复用当前的数组,而不是每次都进行值拷贝,每次递归执行完了,要把之前的顺序换回来,这样就确保了一个父问题处理多个子问题的时候不会因为某个子问题改变了数组顺序,而无法处理下一个子问题了,具体来看下代码:

// 递归函数
function _allSort(arr, start, len) 
  // arr: 要全排列的数组,start: 从数组的start位置之后开始全排列,len: 数组总长度
  for (let i = start; i < len; i++) 
    if (start == len - 1) 
      // 如果当前全排列已经是最后一位了,则输出数组
      console.log(arr)
     else 
      // 将start位置与各个位置交换
      swap(i, start, arr)
      // 交换完start自增1,处理子问题
      _allSort(arr, start + 1, len)
      // 交换完再换回来,确保每个子问题执行完,不影响父问题的顺序
      swap(start, i, arr)
    
  

// 位置交换
function swap (a, b, nums) 
  let temp = nums[a]
  nums[a] = nums[b]
  nums[b] = temp

// 从0位置开始全排列
function allSort(arr) 
  let start = 0
  let len = arr.length
  _allSort(arr, start, len)


allSort([1,2,3])

存在重复的问题
上面的代码,在数组没有重复元素的时候运行是正常的,但是一旦有重复就会出现一些小问题,我们看下arr为[1, 2, 2]的执行结果:

[1, 2, 2]
[1, 2, 2]
[2, 1, 2]
[2, 2, 1]
[2, 2, 1]
[2, 1, 2]

发现没,有一些组合是重复的,针对这些情况,需要做一些过滤,拿[1, 2, 2]进行举例,当1和第一个2交换时正常交换,但是当1和第二个2交换的时候就要判定之前之前有没有和2交换过,基于此我们升级一下代码:

// 递归函数
function _allSort(arr, start, len) 
  // arr: 要全排列的数组,start: 从数组的start位置之后开始全排列,len: 数组总长度
  for (let i = start; i < len; i++) 
    if (start == len - 1) 
      // 如果当前全排列已经是最后一位了,则输出数组
      console.log(arr)
     else 
      // 判定当前元素是否重复出现
      let is_mutil = false
      for (let j = start; j < i; j++) 
        if (arr[j] === arr[i]) 
          console.log(arr[j], arr[i])
          is_mutil = true
          break
        
      
      if (is_mutil) 
        // 如果已经出现过,就不在生成新的子问题了
        continue
      
      // 将start位置与各个位置交换
      swap(i, start, arr)
      // 交换完start自增1,处理子问题
      _allSort(arr, start + 1, len)
      // 交换完再换回来,确保每个子问题执行完,不影响父问题的顺序
      swap(start, i, arr)
    
  

// 位置交换
function swap (a, b, nums) 
  let temp = nums[a]
  nums[a] = nums[b]
  nums[b] = temp

// 从0位置开始全排列
function allSort(arr) 
  let start = 0
  let len = arr.length
  _allSort(arr, start, len)


allSort([1,2,2])

这样就成功解决了有重复数据的问题,不过同时也提升了时间复杂度。

以上是关于数组全排列问题的主要内容,如果未能解决你的问题,请参考以下文章

数组全排列问题

数组的全排列问题

全排列

输出数组全排列

字符串数组全排列——逐个追加组合算法

C语言全排列问题为啥用指针比数组慢很多?