Kotlin生成没有重复元素的列表的排列(按顺序)

tvmytwxo  于 2023-03-19  发布在  Kotlin
关注(0)|答案(4)|浏览(225)

有没有一种简单的(甚至可能是Kotlin方法)生成给定列表(包含重复元素)的所有排列,它:

  • 保持元素的顺序
  • 删除所有重复元素
  • 包括所有元素

例如:
给定列表:[A, B, C, A, B, D, A],我希望得到以下结果:
[A, B, C, D][A, C, B, D][B, C, A, D][C, A, B, D][C, B, A, A][B, C, D, A]...(如果存在更多组合)

以下结果无效:

[A, B, C, A, D](一式两份A)
[A, B, C, A](重复A和缺失D)
[A, C, D, B](顺序错误)
谢谢你的帮助。

u91tlkcl

u91tlkcl1#

尝试下面的解决方案。高度递归但容易理解(最接近我们的想法)。一个缺点是mem的使用。它取决于你写一个非递归的。

fun <T> allPermutations(set: Set<T>): Set<List<T>> {
    if (set.isEmpty()) return emptySet()

    fun <T> _allPermutations(list: List<T>): Set<List<T>> {
        if (list.isEmpty()) return setOf(emptyList())

        val result: MutableSet<List<T>> = mutableSetOf()
        for (i in list.indices) {
            _allPermutations(list - list[i]).forEach {
                item -> result.add(item + list[i])
            }
        }
        return result
    }

    return _allPermutations(set.toList())
}

输入是一个集合。对于您的问题,您所需要做的就是在调用方法之前将列表转换为集合:allPermutations (yourList.toSet())

vngu2lb8

vngu2lb82#

代码见play.kotlinlang.org

fun main() {
    val list = listOf('A', 'B', 'C', 'A', 'B', 'D', 'A')
    generatePermutations(list, ::println)
}

/**
 * Generates all permutations described in your question
 * For the sake of performance it calls [onNextPermutation] for each permutation,
 * but it uses the same list to write permutations in,
 * so if you need to use these permutations elsewhere copy its parameter by youself
 */
fun <T> generatePermutations(elementsList: List<T>, onNextPermutation: (List<T>) -> Unit) {
    if (elementsList.isEmpty()) {
        onNextPermutation(emptyList())
        return
    }
    val elementCounts = LinkedHashMap<T, Int>() // We need to remember order in which the elements were added to map
    elementsList.forEach {
        elementCounts[it] = 1 + (elementCounts[it] ?: 0) // Count our elements
    }
    val differentElements = elementCounts.keys
    
    val totalPermutationsCount = elementCounts.values.fold(1) { a, b -> a * b }
    
    // Next 3 collections are reused through generator loop for the sake of performance
    
    val takenEntryNumbers = LinkedHashMap<T, Int>() // Number of entry of each element we will take to next permutation
    differentElements.forEach { takenEntryNumbers[it] = 0 }
    
    val entriesOfElementViewed = HashMap<T, Int>() // Count of entries of each element we already viewed while iterating elementsList
    differentElements.forEach { entriesOfElementViewed[it] = 0 }
    
    val currentPermutation = ArrayList<T>() // Mutable list which we will use to write permutations in
    repeat(differentElements.size) { currentPermutation.add(elementsList[0]) } // Just fill it to needed size
    
    repeat(totalPermutationsCount) { // Generate next permutation
        var entriesTaken = 0 // Total count of entries taken in this permutation
        for (element in elementsList) { // Generate current permutation
            if (entriesOfElementViewed[element] == takenEntryNumbers[element]) {
                currentPermutation[entriesTaken++] = element
            }
            entriesOfElementViewed[element] = 1 + (entriesOfElementViewed[element] ?: 0)
        }
        onNextPermutation(currentPermutation)
        // Update collections to start next permutation
        differentElements.forEach { entriesOfElementViewed[it] = 0 }
        // Generate next permutation of entry numbers, where each entry number is less than element's total count
        for (element in differentElements) { 
            if (1 + (takenEntryNumbers[element] ?: 0) == elementCounts[element]) {
                takenEntryNumbers[element] = 0
            }
            else {
                takenEntryNumbers[element] = 1 + (takenEntryNumbers[element] ?: 0)
                break
            }
        }
        
    }
    
}

输出:
[阿、B、丙、丁]
[B、C、A、D]
[B、C、D、A]
[阿、中、B、丁]
[C、A、B、D]
[丙、B、丁、阿]
为O(listSize * permutationsCount)中的每个列表泛型类型解决问题

u4dcyp6a

u4dcyp6a3#

这里有一种方法可以用某种功能性的风格来完成它。
它首先收集一组不同值的“指令”,这些值与应该保留的出现次数索引配对。为此,它将唯一值“Map”到它们的出现次数。然后,它将它们“折叠”到所有可能的配对组合列表中。折叠操作从一个空排列集开始。然后每个唯一值将其所有可能保留的索引与现有的置换集合相乘。
然后,我们遍历所有指令集以应用指令:从所述原始列表的副本中移除每个唯一值中的除一个之外的所有唯一值。

fun <T> getPermutationsWithDistinctValues(original: List<T>): Set<List<T>> {
    if (original.isEmpty())
        return emptySet()
    val permutationInstructions = original.toSet()
        .map { it to original.count { x -> x == it } }
        .fold(listOf(setOf<Pair<T, Int>>())) { acc, (value, valueCount) ->
            mutableListOf<Set<Pair<T, Int>>>().apply {
                for (set in acc) for (retainIndex in 0 until valueCount) add(set + (value to retainIndex))
            }
        }
    return mutableSetOf<List<T>>().also { outSet ->
        for (instructionSet in permutationInstructions) {
            outSet += original.toMutableList().apply {
                for ((value, retainIndex) in instructionSet) {
                    repeat(retainIndex) { removeAt(indexOfFirst { it == value }) }
                    repeat(count { it == value } - 1) { removeAt(indexOfLast { it == value }) }
                }
            }
        }
    }
}

我认为复杂度是O(n*mn),其中n是不同值的个数,m是不同值的最高重复次数,和另一个答案一样,因为最坏情况下的排列数是mn。

dsf9zpds

dsf9zpds4#

返回所有排列的扩展函数

fun <T> List<T>.permutations(): List<List<T>> = if(isEmpty()) listOf(emptyList()) else  mutableListOf<List<T>>().also{result ->
        for(i in this.indices){
            (this - this[i]).permutations().forEach{
                result.add(it + this[i])
            }
        }
    }

相关问题