使用Js实现排列组合算法。

7,846 阅读2分钟

前两天有位技术大牛提了一个他本人非常喜欢问到的面试题,在这里和大家分享一下题目和我的解答,欢迎各位理智讨论。

给出一个数字数组<Number>[], 如[1, 2, 3], 如何打印出所有的排列A33(这里打不出来上下标,见谅)。 即: 打印出[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]。

我们先来看看排列组合在数学上的概念:

排列指的是从n个不同元素中,任取m(m≤n,m与n均为自然数,下同)个不同的元素按照一定的顺序排成一列,叫做从n个不同元素中取出m个元素的一个排列

组合指的是从n个不同元素中,任取m(m≤n)个元素并成一组,叫做从n个不同元素中取出m个元素的一个组合

贴下我的回答:


   function permutationAndCombination(source = [], selectedLimit, isPermutation = true) {
           if (!Array.isArray(source)) return source

           // remove duplicated item
           source = [...new Set(source)]
           selectedLimit = selectedLimit || source.length

           const result = []
           const sourceLen = source.length

           selectedLimit = selectedLimit > sourceLen ? sourceLen : selectedLimit

           const innerLoop = (prefix = [], done = [], index = 0) => {
               const prefixLen = prefix.length

               for (let i = isPermutation ? 0 : index; i < sourceLen; i++) {

                   if (prefixLen > selectedLimit - 1) break

                   // Optimization: Continue to next cycle if current item has be already used for 'prefix'.
                   if (done.includes(i)) continue

                   const item = source[i]
                   const newItem = [...prefix, item]

                   if (prefixLen === selectedLimit - 1) {
                       result.push(newItem)
                   }

                   if (prefixLen < selectedLimit - 1) {
                       innerLoop(newItem, [...done, i], index++)
                   }

               }
           }

           if (source.length) {

               // there is only one case if we want to select all items from source by combination.
               if (!isPermutation && selectedLimit === sourceLen) {
                   return source
               }

               innerLoop()
           }

           return result
       }


A33: 从3个不同元素中取3个元素按一定顺序排列


   permutationAndCombination([1, 2, 3])
   

结果如下


A32: 从3个不同元素中取2个元素按一定顺序排列

 
    permutationAndCombination([1, 2, 3], 2)
    

结果如下


A31: 从3个不同元素中取1个元素按一定顺序排列

 
    permutationAndCombination([1, 2, 3], 1)
    

结果如下


重复元素会被忽略掉,遵循排列组合的定义,我们在方法的开始部分做了去重的操作。

   
   // remove duplicated item
   source = [...new Set(source)]
 

   permutationAndCombination([1, 2, 3, 3])
   

结果如下

如果我们在排列的时候不想忽略重复的,就需要做一个缓存,已经出现的就不需要再次操作了。


C33: 从3个不同元素中取3个元素无序排列,这种情况其实只有一种,代码中做了优化,直接返回源数组。


   // there is only one case if we want to select all items from source by combination.
   if (!isPermutation && selectedLimit === sourceLength) {
       return source
   }
   
 
    permutationAndCombination([1, 2, 3], 3, false)
    

结果如下


C32: 从3个不同元素中取2个元素无序排列

 
    permutationAndCombination([1, 2, 3], 2, false)
    

结果如下


C31: 从3个不同元素中取1个元素无序排列

 
    permutationAndCombination([1, 2, 3], 1, false)
    

结果如下


如果各位有更好的思路,欢迎讨论。