알고리즘 테스트에 쓰이는 여러 기초적인 알고리즘을 내 방식대로 구현하여 완전히 내 것으로 만들 필요성을 느꼈다.
특히 순열과 조합 같은 경우 자주 쓰는 라이브러리가 기억이 잘 나지 않을 경우를 대비해, 나만의 알고리즘을 만들어보기로 했다.
다음과 같은 상황을 상정해보겠다.
문자열이 ABCD가 주어졌고, 이를 조합하여 총 4! = 24개의 순열을 만들 수 있다.
이 때 이 순열을 모두 나열해야하는 상황이다.
보통은 재귀를 이용해서 풀지만, 개인적으로는 재귀보다는 스택을 이용하여 푸는 게 더 재미있고 직관적인 편이라 스택을 이용하여 풀어보겠다.
먼저 필요한 아이템부터 나열해보겠다.
이 아이템들을 이용한 스택 구현 방법에 대해 설명하겠다.
말로 하면 복잡한데, 결국 코드로 옮기면 아래와 같다.
// @param currentPermutation: 현재 조합 중인 순열
// @param notIncludedChar: 아직 포함되지 않은 문자의 인덱스
data class Permutation(val currentString: String, val notIncludedChar: ArrayList<Int>)
// 1번 아이템
val mString = "ABC"
// 2번 아이템
val permutationList = mutableListOf<String>()
// 3번 아이템
val composedPermutation = mutableSetOf<String>()
// 4번 아이템
val stack = Stack<Permutation>()
val r = mString.length
for (charIdx in mString.indices) {
// 1번 과정
// 5번 아이템
val notIncludedChar: ArrayList<Int> = mString.indices.filterNot { it == charIdx } as ArrayList<Int>
// 2번 과정
stack.push(Permutation(mString[charIdx].toString(), notIncludedChar))
// 3번 과정
while (stack.isNotEmpty()) {
val currentPermutation = stack.peek()
if (currentPermutation.currentString in composedPermutation) {
stack.pop()
continue
}
composedPermutation.add(currentPermutation.currentString)
// 4번 과정
if (currentPermutation.currentString.length == r) {
permutationList.add(currentPermutation.currentString)
stack.pop()
continue
}
// 5번 과정
for (i in currentPermutation.notIncludedChar) {
// 새 문자열
val nextString = currentPermutation.currentString + mString[i].toString()
// 새 5번 아이템
val nextNotIncludedChar = currentPermutation.notIncludedChar.filterNot { it == i } as ArrayList<Int>
if (nextString !in composedPermutation) stack.push(Permutation(nextString, nextNotIncludedChar))
}
}
}