简体   繁体   中英

Creating a method that generates all permutations of string in kotlin using recursion

trying to write some code that generates all permutations of an input string as a recursion exercise but can't figure out why I get a stack overflow error.

fun main() {
    println(subSet(listOf("abcd")))
}

fun subSet(s: List<String>): List<String>{
    return listOf<String>() + createSubSets(s)
}

fun createSubSets(s: List<String>): List<String>{
    if(s.isEmpty()){
        return listOf()
    }
    return s.mapIndexed{i, elem ->
        elem + createSubSets(s.drop(i))
    }
}

This statement leads to infinite recursion:

return s.mapIndexed { i, elem ->
    elem + createSubSets(s.drop(i))
}

In it, the first iteration has the i value of 0 (with the elem being the character at index 0 ), and the recursive call createSubSets(s.drop(i)) is equivalent to createSubSets(s) , because dropping zero characters from a string returns the original string.

Your recursive function is not safe. It iterates forever, keeps adding to the stack and that's why you're getting a stack overflow.

Apart from fixing your algorithm, you can do two things to improve the situation:

  1. Add an exit condition to make the function exit eventually.
fun main() {
    println(subSet(listOf("a", "b", "c", "d")))
}

fun subSet(s: List<String>): List<String>{
    return createSubSets(s, 0)
}

fun createSubSets(s: List<String>, index: Int): List<String> {
    if (index > s.lastIndex) {
        return emptyList()
    }
    val otherElements = s.subList(0, index) + s.subList(index + 1, s.size)
    val element = s[index]
    return otherElements.map { element + it } + createSubSets(s, index + 1)
}

// [ab, ac, ad, ba, bc, bd, ca, cb, cd, da, db, dc]
  1. Make the function tail-recursive so that even with long strings it won't stack overflow.
fun main() {
    println(subSet(listOf("a", "b", "c", "d")))
}

fun subSet(s: List<String>): List<String>{
    return createSubSets(s, 0, emptyList())
}

tailrec fun createSubSets(s: List<String>, index: Int, carryOn: List<String>): List<String> {
    if (index > s.lastIndex) {
        return carryOn
    }
    val otherElements = s.subList(0, index) + s.subList(index + 1, s.size)
    val element = s[index]
    return createSubSets(s, index + 1, carryOn + otherElements.map { element + it })
}

// [ab, ac, ad, ba, bc, bd, ca, cb, cd, da, db, dc]

Notice that the tail-recursive version always calls itself as the last thing. Specifically, it doesn't perform any operations on the result of calling itself. That's why it has to carry on the result to the next call, but that is also why it's stack safe.

Finally, notice that you don't need recursion at all to solve this problem:

fun main() {
    println(subSet(listOf("a", "b", "c", "d")))
}

fun subSet(s: List<String>): List<String>{
    return createSubSets(s)
}

fun createSubSets(s: List<String>): List<String> {
    return s.mapIndexed { i, element ->
        val otherElements = s.subList(0, i) + s.subList(i + 1, s.size)

        otherElements.map { element + it }
    }.flatten()
}

// [ab, ac, ad, ba, bc, bd, ca, cb, cd, da, db, dc]
fun main() {
permutations("abcd","")   }
                                         
fun permutations(str:String, storedString: String){
if(str.length == 1)
    println(storedString+str)
else
for(i in str.indices)
    permutations(str.removeRange(i,i+1),storedString+str[i])}

Another example:

fun String.permute(result: String = ""): List<String> =
    if (isEmpty()) listOf(result) else flatMapIndexed { i, c -> removeRange(i, i + 1).permute(result + c) }

fun main() {
    println("abc".permute()) // [abc, acb, bac, bca, cab, cba]
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM