PS
[알고리즘 - Swift] 순열과 조합 구현하기
itisjustK
2023. 4. 1. 18:02
순열과 조합
코테 문제를 풀다보면 순열과 조합이 필요한 경우가 있다. Swift에서는 순열과 조합을 직접 구현해야 한다.
순열과 조합은 각각 Stack, 재귀를 통해 구현할 수 있다. 어떻게 구현하는지 순열부터 알아보자.
순열
순열은 서로 다른 m개에서 n개를 골라 줄세우는 것이다. (mPn)
줄을 세우기 때문에 요소들의 순서 또한 경우의 수에 영향을 준다.
Stack
func permutation<T: Comparable>(_ array: [T], _ num: Int) -> [[T]] {
var result = [[T]]()
/// 주어진 원소 개수보다 많은 가짓수를 뽑을 수 없음
guard num <= array.count else { return result }
/// 튜플 형태로 stack에 삽입. (방문 히스토리, 방문 유무)
var stack: [([T], [Bool])] = array.enumerated().map {
var visited = Array(repeating: false, count: array.count)
visited[$0.offset] = true
return ([$0.element], visited)
}
while stack.count > 0 {
let current = stack.removeLast()
let elements = current.0
var visited = current.1
/// 주어진 가짓수만큼 다 뽑았으면 result에 추가하고 다음 반복으로 넘어가기
if elements.count == num {
result.append(elements)
continue
}
/// 방문 유무를 보면서 아직 방문 안 한 곳 stack에 추가하기
for i in 0..<array.count {
if visited[i] == true { continue }
visited[i] = true
stack.append((elements + [array[i]], visited))
visited[i] = false
}
}
return result
}
재귀
func permutation<T: Comparable>(_ array: [T], _ num: Int) -> [[T]] {
var result = [[T]]()
/// 주어진 원소 개수보다 많은 가짓수를 뽑을 수 없음
guard num <= array.count else { return result }
/// 방문 표시 할 배열
var visited = Array(repeating: false, count: array.count)
func cycle(_ current: [T]) {
/// 주어진 가짓수만큼 다 뽑았으면 result에 추가하고 다음 반복으로 넘어가기
if current.count == num {
result.append(current)
return
}
for i in 0..<array.count {
/// 방문했다면 다음 반복으로 넘어가기
if visited[i] { continue }
else {
/// 방문하지 않았다면, 방문표시 한 것을 넘겨주고, 방문 히스토리에도 추가하고 더 파고들기
visited[i] = true
cycle(current + [array[i]])
visited[i] = false
}
}
}
cycle([])
return result
}
조합
조합은 서로 다른 m개에서 n개의 조합을 찾는 것이다. (mCn)
순열과 달리 줄을 세울 필요가 없어서 순서와 상관 없이 구성만 따지면 된다.
Stack
func combination<T: Comparable>(_ array: [T], _ num: Int) -> [[T]] {
var result = [[T]]()
/// 주어진 원소 개수보다 많은 가짓수를 뽑을 수 없음
guard num <= array.count else { return result }
/// 튜플 형태로 stack에 삽입. (방문 히스토리, 현재 위치)
var stack = array.enumerated().map { ([$0.element], $0.offset) }
while stack.count > 0 {
let current = stack.removeLast()
let elements = current.0
var index = current.1
/// 주어진 가짓수만큼 다 뽑았으면 result에 추가하고 다음 반복으로 넘어가기
if elements.count == num {
result.append(elements)
continue
}
/// 현재 위치가 끝이라면 다음 반복으로 지나가기
/// 다음에 방문할 곳은 +1로 표시. 이렇게 함으로써 일종의 방향을 가짐. 방향을 가짐으로써 중복 방문을 면함.
guard (index+1) < array.count else { continue }
/// 다음에 방문할 곳 stack에 추가
for i in (index+1)..<array.count {
stack.append((elements + [array[i]], i))
}
}
return result
}
재귀
func combination<T: Comparable>(_ array: [T], _ num: Int) -> [[T]] {
var result = [[T]]()
/// 주어진 원소 개수보다 많은 가짓수를 뽑을 수 없음
guard num <= array.count else { return result }
func cycle(_ index: Int, _ current: [T]) {
/// 주어진 가짓수만큼 다 뽑았으면 result에 추가하고 다음 반복으로 넘어가기
if current.count == num {
result.append(current)
return
}
/// index부터 범위를 시작하면서 일종의 방향성을 갖게 함
for i in index..<array.count {
/// 다음 index를 지정해주고, 방문 히스토리에도 추가해줘서 한 번 더 파고듦
cycle(i+1, current + [array[i]])
}
}
cycle(0, [])
return result
}
활용 문제
프로그래머스 - 메뉴 리뉴얼 (lv2)
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
sol) 조합 (DFS)
/1
주어진 course에 대해
각 orders에서
course의 수에 맞는 조합 구하고
/2
주어진 course에 대해
course의 수를 충족하는 조합 중에서 가장 많이 반복된 것 찾기
import Foundation
fileprivate func solution(_ orders:[String], _ course:[Int]) -> [String] {
var ans = [String]()
let arr = orders.map{ Array($0).map { String($0) } }
var dict = [Int:[[String]]]()
for n in course {
var values = [[String]]()
for str in arr {
values += combination(str, n)
}
dict[n] = values
}
for n in course {
let values = dict[n]!.map { $0.joined() }
var check = [String:Int]()
for i in Set(values) {
check[i] = values.filter{ $0 == i }.count
}
let max = check.values.max()
for i in check {
if i.value > 1 && i.value == max {
ans.append(i.key)
}
}
}
return ans.sorted()
}
func combination<T: Comparable>(_ array: [T], _ num: Int) -> [[T]] {
var result = [[T]]()
guard num <= array.count else { return result }
func cycle(_ index: Int, _ current: [T]) {
if current.count == num {
result.append(current.sorted())
return
}
for i in index..<array.count {
cycle(i+1, current+[array[i]])
}
}
cycle(0, [])
return result
}