๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
โŒจ๏ธ Language/swift

[Swift] ํž™(Heap) ๊ตฌํ˜„ํ•˜๊ธฐ

by hyebin (Helia) 2024. 9. 27.
๋ฐ˜์‘ํ˜•

Heap์€ ํŠธ๋ฆฌ ๊ธฐ๋ฐ˜์˜ ์ž๋ฃŒ๊ตฌ์กฐ๋กœ, ํŠน์ • ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ ์š”์†Œ๋“ค์„ ์ •๋ ฌํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Heap์€ ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€ ์œ ํ˜•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ตœ์†Œ ํž™์€ ์ž‘์€ ๊ฐ’์ด ๋จผ์ € ๋‚˜์˜ค๋„๋ก ์ •๋ ฌํ•˜๊ณ , ์ตœ๋Œ€ ํž™์€ ํฐ ๊ฐ’์ด ๋จผ์ € ๋‚˜์˜ค๋„๋ก ์ •๋ ฌํ•ฉ๋‹ˆ๋‹ค.

 

์ œ๋„ค๋ฆญ์„ ์ด์šฉํ•ด Heap ๊ตฌ์กฐ์ฒด๋ฅผ ์ •์˜ํ•˜๊ณ  ์ฃผ์š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

struct Heap<T> {
    private var elements: [T] = []
    private let priorityFunction: (T, T) -> Bool
    
    var isEmpty: Bool {
        return elements.isEmpty
    }
    
    var count: Int {
        return elements.count
    }
    
    var peek: T? {
        return elements.first
    }
    
    init(priorityFunction: @escaping (T, T) -> Bool) {
        self.priorityFunction = priorityFunction
    }
    
    mutating func push(_ element: T) {
        elements.append(element)
        siftUp(from: elements.count - 1)
    }
    
    mutating func pop() -> T? {
        guard !isEmpty else { return nil }
        elements.swapAt(0, count - 1)
        let element = elements.removeLast()
        if !isEmpty {
            siftDown(from: 0)
        }
        return element
    }
    
    private mutating func siftUp(from index: Int) {
        var childIndex = index
        let child = elements[childIndex]
        var parentIndex = self.parentIndex(of: childIndex)
        
        while childIndex > 0 && priorityFunction(child, elements[parentIndex]) {
            elements[childIndex] = elements[parentIndex]
            childIndex = parentIndex
            parentIndex = self.parentIndex(of: childIndex)
        }
        
        elements[childIndex] = child
    }
    
    private mutating func siftDown(from index: Int) {
        let parent = elements[index]
        var parentIndex = index
        
        while true {
            let leftChildIndex = self.leftChildIndex(of: parentIndex)
            let rightChildIndex = leftChildIndex + 1
            
            var candidateIndex = parentIndex
            
            if leftChildIndex < count && priorityFunction(elements[leftChildIndex], elements[candidateIndex]) {
                candidateIndex = leftChildIndex
            }
            
            if rightChildIndex < count && priorityFunction(elements[rightChildIndex], elements[candidateIndex]) {
                candidateIndex = rightChildIndex
            }
            
            if candidateIndex == parentIndex {
                return
            }
            
            elements.swapAt(parentIndex, candidateIndex)
            parentIndex = candidateIndex
        }
        elements[parentIndex] = parent
    }
    
    private func parentIndex(of index: Int) -> Int {
        return (index - 1) / 2
    }
    
    private func leftChildIndex(of index: Int) -> Int {
        return 2 * index + 1
    }
    
    private func rightChildIndex(of index: Int) -> Int {
        return 2 * index + 2
    }
}

 

์œ„ ์ฝ”๋“œ์—์„œ๋Š” Heap์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

  • push(_:): ์ƒˆ๋กœ์šด ์š”์†Œ๋ฅผ ํž™์— ์ถ”๊ฐ€ํ•˜๊ณ  ํž™์˜ ์†์„ฑ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” O(log n)์ž…๋‹ˆ๋‹ค.
  • pop(): ํž™์˜ ์ตœ์ƒ์œ„ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ์ œ๊ฑฐ ํ›„ ํž™์˜ ์†์„ฑ์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” O(log n)์ž…๋‹ˆ๋‹ค.
  • peek: ์ตœ์ƒ์œ„ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•˜์ง€ ์•Š๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ๋ณต์žก๋„๋Š” O(1)์ž…๋‹ˆ๋‹ค.
  • siftUp(from:): ์ƒˆ๋กœ์šด ์š”์†Œ๋ฅผ ์‚ฝ์ž… ํ›„ ์˜ฌ๋ฐ”๋ฅธ ์œ„์น˜๋กœ ์ด๋™์‹œํ‚ต๋‹ˆ๋‹ค.
  • siftDown(from:): ์ตœ์ƒ์œ„ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•œ ํ›„ ํž™ ์†์„ฑ์„ ์œ ์ง€ํ•˜๋„๋ก ์žฌ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.

๊ตฌํ˜„ํ•œ Heap์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ์ตœ์†Œ ํž™ ํ…Œ์ŠคํŠธ
var minHeap = Heap<Int> { $0 < $1 }
minHeap.push(3)
minHeap.push(1)
minHeap.push(4)
minHeap.push(1)
minHeap.push(5)

print("Min Heap Test:")
while let element = minHeap.pop() {
    print(element)
}

 

 

์ถœ๋ ฅ

Min Heap Test:
1
1
3
4
5

 

// ์ตœ๋Œ€ ํž™ ํ…Œ์ŠคํŠธ
var maxHeap = Heap<Int> { $0 > $1 }
maxHeap.push(3)
maxHeap.push(1)
maxHeap.push(4)
maxHeap.push(1)
maxHeap.push(5)

print("\nMax Heap Test:")
while let element = maxHeap.pop() {
    print(element)
}

 

์ถœ๋ ฅ

Max Heap Test:
5
4
3
1
1

 

 

์ตœ์†Œ ํž™์€ ์˜ค๋ฆ„์ฐจ์ˆœ์œผ๋กœ ์š”์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ , ์ตœ๋Œ€ ํž™์€ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์š”์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ตฌ์กฐ๋Š” ์ •๋ ฌ ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‚˜ ์šฐ์„ ์ˆœ์œ„ ํ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์—์„œ ๋งค์šฐ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 
๋ฐ˜์‘ํ˜•