Search
Duplicate

Swift 알고리즘 (1)

Created
2023/10/17 02:44
Tags
CodingTest
태그

Swift 알고리즘

문자열

접두사, 접미사

prefix, suffix, dropFirst, dropLast
let s = "abcdef" let s1 = s.prefix(3) // abc let s2 = s.suffix(3) // def let s3 = s.dropFirst(3) // def let s4 = s.dropLast(3) // def let s5 = s.prefix(100) // abcdef
Swift
복사
이 메서드들은 Array에도 똑같이 적용된다.

인덱스 접근

let s = "abcdef" let x = s[s.index(s.startIndex, offsetBy: 3)] // "d" // x의 타입은 String이 아닌 Character // 배열과 문자열의 인덱스 접근법 비교 let arr = [0, 1, 2, 3, 4] let str = "abcde" // 배열은 편하게 인덱스 접근 가능 let arr2 = arr[0..<3] print(arr2) // 문자열은 편하게 인덱스 접근이 불가능. String.Index 형으로 접근해야 한다 let i = str.startIndex let j = str.index(i, offsetBy: 3) let str2 = str[i..<j] print(arr2) // [0, 1, 2] print(str2) // abc
Swift
복사

슬라이싱

문자열 str = abcdef가 있을 때, abc만 남기고 자르고 싶을 경우
파이썬에서는 str[0:3] 문자열 슬라이싱을 하면 되었지만 Swift에서는 지원하지 않는다.
let s5 = s[s.startIndex ..< s.index(s.startIndex, offsetBy: 3)] // "abc" // Swift의 Index로 자른 결과는 String이 아닌 SubString 타입 var stringList = [String]() stringList.append(String(s5)) // 타입 변환 필요
Swift
복사

뒤집기

import Foundation let s = "abcde" print(String(s.reversed())) // "edcba"
Swift
복사

곱셈

import Foundation let str = "abc" print(String(repeating: str, count: 3)) // "abcabcabc"
Swift
복사

교체

import Foundation let str = "abcde" let str2 = str.replacingOccurrences(of: "abc", with: "하이") // "하이de"
Swift
복사

배열

first, last

first, last 라는 키워드를 제공. 비어있을 시 nil을 반환
let str = "abc" let arr = [1, 2, 3] let arr2 = [Int]() str.last // "c" arr.last // 3 str.first // "a" arr.first // 1 arr2.first // nil
Swift
복사

정수 → 배열

map의 결과는 Array 형태이기 때문에 다음과 같은 방법으로 정수 → 배열 변환을 할 수 있다.
import Foundation var x: Int = 12345 var arr = String(x).map{ Int(String($0))! } print(arr) // [1, 2, 3, 4, 5]
Swift
복사

배열 초기화

여러가지 형태의 배열 초기화 방법
let N = 5 let a = [Int](repeating: 0, count: N) let b = [Int](0..<N) let c = [Int](0..<N).map{2 * ($0)} let d = [Int](0..<N).filter($0 % 2 == 0} print(a) // [0, 0, 0, 0, 0] print(b) // [0, 1, 2, 3, 4] print(c) // [0, 2, 4, 6, 8] print(d) // [0, 2, 4]
Swift
복사

딕셔너리

원소 확인

원소가 있는지 없는지는 그냥 딕셔너리에 키 값으로 접근했을 때, nil 인지 아닌지로 판단한다.
var myDict = [String : Int]() if let val = myDict['a'] { print(val) } else { print("없음") }
Swift
복사

원소 개수 확인

count로 딕셔너리 원소 개수를 확인할 수 있다. 비어있는 경우는 count == 0 이 아닌 isEmpty를 활용하는 것이 좋겠다.
let myDict = ["a": 1, "b": 2, "c": 3] print(myDict.count) if !(myDict.isEmpty) { print("비어있지 않음") }
Swift
복사

원소 삭제

key-pair를 삭제하고 싶다면 nil을 대입해주면 된다.
let myDict = ["a": 1, "b": 2, "c": 3] myDict[a] = nil
Swift
복사

defaultDict

딕셔너리에 없는 값을 조회할 경우, 디폴트 값을 설정할 수 있다.
let myDict = ["a": 1, "b": 2, "c": 3] print(myDict["d", default: -1])
Swift
복사

딕셔너리 비교

딕셔너리끼리도 == 연산을 할 수 있다.
var a = [String : Int]() var b = [String : Int]() a["a"] = 1 a["b"] = 2 b["b"] = 2 b["a"] = 1 print(a == b) // true b["c"] = 3 print(a == b) // false
Swift
복사

주의점

딕셔너리 결과는 항상 옵셔널이기 때문에 옵셔널 처리에 주의해야한다.
let myDict = ["a": 1, "b": 2, "c": 3] if let a = myDicct["a"], a > 0 { print("양수") }
Swift
복사

큐 (Queue)

Swift에서는 Queue를 지원해주지 않는다.
Array에서 popLast() 라는 메서드와 removeFirst() 라는 메서드가 있기는 하다.
하지만 공식 문서를 읽어보면, removeFirst()의 시간복잡도는 O(N)이기 때문에 비효율적
→ Stack 2개로 Queue를 구현

큐 구현법 1 - 정석

1.
inputStack, outputStack 을 정의한다.
2.
enqueue - inputStack에 삽입한다.
3.
dequeue는 다음 3가지 상황을 고려한다.
a.
inputStack, outputStack이 모두 비어있을 경우
nil 리턴
b.
outputStack이 비어있지 않은 경우
outputStack에서 popLast()
c.
outputStack이 비어있지만 inputStack이 비어있지 않은 경우
inputStack이 빌 때까지 모두 outputStack으로 pop 수행 후, outputStack에서 popLast()
하지만 Swift에서는 비어있는 array에서 popLast()를 수행 시 자동으로 nil을 반환하므로 위 3가지 상황을 고려하지 않고 편하게 코드를 작성해도 된다.
import Foundation struct Queue<T> { var inputStack = [T]() var outputStack = [T]() mutating func append(_ x: T) { inputStack.append(x) } mutating func pop() -> T? { var top: T? while !(inputStack.isEmpty) { let x = inputStack.popLast()! outputStack.append(x) } top = outputStack.popLast() return top } mutating func head() -> T? { while !(inputStack.isEmpty) { let x = inputStack.popLast()! outputStack.append(x) } return outputStack.isEmpty ? nil : outputStack[outputStack.endIndex - 1] } func isEmpty() -> Bool { return inputStack.isEmpty && outputStack.isEmpty ? true : false } } var q = Queue<Int>()
Swift
복사
시간복잡도
append: O(1)
pop: 상황에 따라 O(1) or O(N)
이 코드는 기본적인 큐 연산을 지원하며, 제네릭을 사용하여 다양한 타입으로 큐를 구현할 수 있다.
하지만 ‘pop()’ 과 ‘head()’ 메서드에서 모든 요소를 매번 이동시키는 방식은 효율성 면에서 개선될 여지가 있다. 매 연산 때마다 요소를 이동시키는 대신, 필요할 때만 요소를 재배열하는 방법을 고려할 수 있다.
또한, 현재 ‘pop()’이나 ‘head()’ 메서드를 호출할 때마다 ‘inputStack’의 모든 내용을 ‘outputStack’으로 옮기고, 이 과정을 반복하지말고 ‘outputStack’이 비어있을 때만 ‘inputStack’에서 ‘outputStack’으로 요소를 옮기는 방식으로 최적화할 수 있다.
최적화한 코드
import Foundation struct Queue<T> { private var inputStack = [T]() private var outputStack = [T]() mutating func append(_ x: T) { inputStack.append(x) } mutating func pop() -> T? { if outputStack.isEmpty { transferElements() } return outputStack.popLast() } mutating func head() -> T? { if outputStack.isEmpty { transferElements() } return outputStack.last } func isEmpty() -> Bool { return inputStack.isEmpty && outputStack.isEmpty } private mutating func transferElements() { while !inputStack.isEmpty { outputStack.append(inputStack.popLast()!) } } } var queue = Queue<Int>()
Swift
복사

큐 구현법 2 - reversed 활용

reverse()는 O(N) 소요, reversed는 O(1) 소요된다.
따라서 swift의 reversed를 활용하면 append와 pop 모두 O(1) 소요되는 큐를 구현할 수 있다.
import Foundation struct Queue<T> { var inputStack = [T]() var outputStack = [T]() mutating func append(_ x: T) { inputStack.append(x) } mutating func pop() -> T? { var top: T? if outputStack.isEmpty { outputStack = inputStack.reversed() inputStack.removeAll() top = outputStack.popLast() } else { top = outputStack.popLast() } return top } mutating func head() -> T? { if outputStack.isEmpty { outputStack = inputStack.reversed() inputStack.removeAll() } return outputStack.isEmpty ? nil : outputStack[outputStack.endIndex - 1] } func isEmpty() -> Bool { return inputStack.isEmpty && outputStack.isEmpty ? true : false } } var q = Queue<Int>()
Swift
복사

힙 Heap

힙은 각 노드가 하위 노드보다 작은(뜨는 큰) 이진 트리이다.
이러한 종류의 이진 트리는 주로 우선순위 큐를 구현하는데 사용된다. 힙에는 두 가지 주요 유형이 있다.
최소 힙 (Min-Heap)
부모 노드가 자식 노드보다 작거나 같은 값을 가진다.
최대 힙 (Max-Heap)
부모 노드가 자식 노드보다 크거나 같은 값을 가진다.

소스코드

import Foundation public struct Heap<T> { // 전체 노드, 힙의 요소를 저장하느 배열 var nodes: [T] = [] // 비교 연산자, 두 요소를 비교하는데 사용되는 비교함수 // 이 함수는 힙이 최소힙인지 최대힙인지 결정한다. let comparer: (T, T) -> Bool var isEmpty: Bool { return nodes.isEmpty } // 예를들어, Heap<Int>(comparer: >=)는 Min Heap init(comparer: @escaping (T, T) -> Bool { self.comparer = comparer } // top 반환 func top() -> T? { return nodes.first } // 삽입 mutating func push(_ element: T) { var index = nodes.count nodes.append(element) // 현재 노드가 루트 노드가 아니며, 현재 노드와 부모 노드 사이의 힙 속성이 위반 되었다면 while index > 0, !comparer(nodes[index], nodes[(index - 1) / 2]) { nodes.swapAt(index, (index - 1) / 2) index = (index - 1) / 2 } } // 삭제 mutating func pop() -> T? { // 힙에 노드가 없는 경우, 즉시 함수에서 반환, 빈 힙에서 요소를 제거하려고 시도하는 것을 방지 guard !nodes.isEmpty else { return nil } if nodes.count == 1 { return nodes.removeFirst() } // 제거할 노드(루트 노드)의 값을 저장, 이 값은 마지막에 반환된다. let result = nodes.first // 마지막 노드와 루트 노드의 위치를 바꾼다 nodes.swapAt(0, nodes.count - 1) // 위치를 바꾼 원래의 루트 노드(마지막 위치에 있음)를 제거 _ = nodes.popLast() // 이제 "down-heap bubbling" 과정이 시작된다. // 이 과정은 제거된 루트 노드의 자리를 채우기 위해 힙을 재정렬 var index = 0 while index < nodes.count { let left = index * 2 + 1 let right = left + 1 // 오른쪽 자식이 있는지 확인하는 조건 // 있으면 오른쪽 자식과 왼쪽 자식을 비교하고, // 더 작은(또는 더 큰) 자식과 현재 노드를 바꾼다. if right < nodes.count { if comparer(nodes[left], nodes[right]), !comparer(nodes[right], nodes[index]) { nodes.swapAt(right, index) index = right } else if !comparer(nodes[left], nodes[index]) { nodes.swapAt(left, index) index = left } else { break } else if left < nodes.count { if !comparer(nodes[left], nodes[index]) } nodes.swapAt(left, index) index = left } else { break } } else { break } } return result } } extension Heap where T: Comparable { // 초기 설정을 해주지 않으면 Min Heap init() { self.init(comparer: >=) } }
Swift
복사

힙 활용

파이썬에서는 heapq를 지원하지만, swfit에서는 힙 자료구조를 기본 제공하지 않는다.
Heap 활용 코드
// Min Heap 세팅 var minheap = Heap<Int>(comparer: >=) // Max Heap 세팅 var maxHeap = Heap<Int>(comparer: <=) // 100 삽입 minHeap.push(100) // 삭제 let _ = minHeap.pop() // top 반환 let top = minHeap.top() // heap 원소 개수 let heapCount = minHeap.nodes.count
Swift
복사

 Reference