๐Ÿ’ป CS/์ž๋ฃŒ๊ตฌ์กฐ

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

JerryiOS 2023. 4. 11. 18:53

์ด๊ธ€์€ ๊ฐœ์ธ์ ์ธ ๊ณต๋ถ€๋ฅผ ์œ„ํ•ด ์ž‘์„ฑ๋œ ๊ธ€์ž„์„ ๋ฐํž™๋‹ˆ๋‹ค.

์ด๊ธ€์˜ ๋ชจ๋“  ๋‚ด์šฉ์— ๋Œ€ํ•œ ์ถœ์ฒ˜๋Š” https://jeonyeohun.tistory.com/325 ์ด๊ณณ์ž…๋‹ˆ๋‹ค.

 

ํž™

์ด์ง„ํŠธ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐ˜์ •๋ ฌ ์ƒํƒœ๋กœ ์œ ์ง€ํ•˜๋Š” ์ž๋ฃŒ๊ตฌ์กฐ

ํž™์€ ํŠธ๋ฆฌ์˜ ๋ฃจํŠธ ๋…ธ๋“œ์— ๋ฐ์ดํ„ฐ๋“ค์˜ ์ตœ์†Ÿ๊ฐ’ ํ˜น์€ ์ตœ๋Œ€๊ฐ’์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

์ตœ์†Ÿ๊ฐ’์„ ๋ฃจํŠธ์— ๋‘˜ ๋•Œ๋Š” ์ตœ์†Œํž™, ์ตœ๋Œ€๊ฐ’์„ ๋ฃจํŠธ์— ๋‘๋ฉด ์ตœ๋Œ€ํž™์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

 

์™„์ „ ์ด์ง„ํŠธ๋ฆฌ

ํŠธ๋ฆฌ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ชจ๋“  ๋…ธ๋“œ๊ฐ€ ์ตœ๋Œ€ 2๊ฐœ์˜ ์ž์‹๋…ธ๋“œ๋ฅผ ๊ฐ€์ง€๋Š” ์ž๋ฃŒ๊ตฌ์กฐ

์™„์ „ ์ด์ง„ํŠธ๋ฆฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ํŠน์ง•์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

1. ๊ฐ ๋†’์ด์— ์žˆ๋Š” ๋ชจ๋“  ๋…ธ๋“œ๊ฐ€ ์ž์‹๋…ธ๋“œ๋ฅผ ๊ฐ€์ ธ์•ผ ๋‹ค์Œ ๋†’์ด์— ์ƒˆ ๋…ธ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
2. ๊ฐ€์žฅ ์™ผ์ชฝ๋ถ€ํ„ฐ ์ฐจ๋ก€๋Œ€๋กœ ๋…ธ๋“œ๋ฅผ ์ฑ„์›Œ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

์ด ๋ฐฐ์—ด ๊ธฐ๋ฐ˜์˜ ์ด์ง„ํŠธ๋ฆฌ๋ฅผ ์ด์šฉํ•ด์„œ ํž™์„ ๊ตฌํ˜„ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

ํž™์˜ ์ด์ง„ํŠธ๋ฆฌ๋Š” ํŠธ๋ฆฌ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฐ ์„œ๋ธŒํŠธ๋ฆฌ๋“ค๋„ ๋ชจ๋‘ ํž™์„ ๋งŒ์กฑํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

 

๋ผˆ๋Œ€ ๋งŒ๋“ค๊ธฐ

import Foundation
 
struct Heap<T: Comparable> {
    private var elements: [T] = []
    private let sortFunction: (T, T) -> Bool
    
    var isEmpty: Bool {
        return self.elements.count == 1
    }
    var peek: T? {
        if self.isEmpty { return nil }
        return self.elements[1]
    }
    var count: Int {
        return self.elements.count - 1
    }
    
    init(elements: [T] = [], sortFunction: @escaping (T, T) -> Bool) {
        if !elements.isEmpty {
            self.elements = [elements.first!] + elements
        } else {
            self.elements = elements
        }
        self.sortFunction = sortFunction
        if elements.count > 1 {
            self.buildHeap()
        }
    }
    
    func leftChild(of index: Int) -> Int {
        return index * 2 
    }
    func rightChild(of index: Int) -> Int {
        return index * 2 + 1
    }
    func parent(of index: Int) -> Int {
        return (index) / 2
    }
 }

elements

์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋“ค์„ ์ €์žฅํ•  ์ด์ง„ํŠธ๋ฆฌ๋กœ ํ‘œํ˜„๋œ ๋ฐฐ์—ด์ž…๋‹ˆ๋‹ค.

sortFunction

์ตœ์†Œํž™, ์ตœ๋Œ€ํž™์˜ ๊ธฐ์ค€์ ์œผ๋กœ ์‚ผ์„ ์ •๋ ฌ ๋กœ์ง์„ ๊ฐ€์ง„ ํด๋กœ์ €์ž…๋‹ˆ๋‹ค.

๋งŒ์•ฝ ์ €์žฅ๋˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์ •์ˆ˜๊ณ , sortFunction์ด ์˜ค๋ฆ„์ฐจ์ˆœ์ •๋ ฌ์ด๋ฉด ์ตœ์†Œํž™, ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ์ด๋ฉด ์ตœ๋Œ€ํž™์ด ๋ฉ๋‹ˆ๋‹ค.

isEmpty

ํ˜„์žฌ ์ด์ง„ํŠธ๋ฆฌ์˜ ์š”์†Œ๋“ค์ด 1๊ฐœ์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

leftChild, rightChild, parent

๋ถ€๋ชจ์™€ ์ž์‹๋…ธ๋“œ์˜ ์ธ๋ฑ์Šค๋ฅผ ๊ตฌํ•˜๋Š” ๋ฉ”์„œ๋“œ

 

์‚ฝ์ž… - O(logN)

์ด๋ ‡๊ฒŒ ์ตœ๋Œ€ํž™์ด ์žˆ๊ณ , 9๋ฅผ ์‚ฝ์ž…ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๊ณผ์ •๋“ค์„ ๊ฑฐ์ณ ํŠธ๋ฆฌ๊ฐ€ ์žฌ์กฐ์ • ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฐ€์žฅ ๋์— ๋…ธ๋“œ๋ฅผ ๋ถ™์˜€์œผ๋‹ˆ ๋…ธ๋“œ๋ฅผ ์˜ฌ๋ผ๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๋งŒํผ ์ตœ๋Œ€ํ•œ ์˜ฌ๋ ค์ฃผ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์•ผ ํŠธ๋ฆฌ์˜ ๊ฐ ์„œ๋ธŒํŠธ๋ฆฌ๋“ค๋„ ์ตœ๋Œ€ํž™์„ ๋งŒ์กฑํ•˜๊ฒŒ๋ฉ๋‹ˆ๋‹ค.

์ „์ฒดํŠธ๋ฆฌ์™€ ๋ชจ๋“  ์„œ๋ธŒํŠธ๋ฆฌ๊ฐ€ ์ตœ๋Œ€ํž™์„ ๋งŒ์กฑํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

์‚ฝ์ž… ๊ตฌํ˜„ - swimUp

์‚ฝ์ž…์˜ ์—ฐ์‚ฐ์€

1. ํŠธ๋ฆฌ ์ œ์ผ ๋์— ์ƒˆ ๋…ธ๋“œ ๋ถ™์ด๊ธฐ
2. ์ƒˆ ๋…ธ๋“œ๋ฅผ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋Š” ๊ณณ๊นŒ์ง€ ์˜ฌ๋ฆฌ๊ธฐ

์ด ์ˆœ์„œ๋กœ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

 

2๋ฒˆ ๊ณผ์ •์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

mutating func swimUp(from index: Int) {
    var index = index
    while index != 1 && self.sortFunction(self.elements[index], self.elements[self.parent(of: index)]) {
        self.elements.swapAt(index, self.parent(of: index))
        index = self.parent(of: index)
    }
}

 

๋ฃจํŠธ๋…ธ๋“œ์— ๋‹ค๋‹ค๋ฅด๊ฑฐ๋‚˜, ํ˜„์žฌ๋…ธ๋“œ๊ฐ€ ๋ถ€๋ชจ๋…ธ๋“œ๋ณด๋‹ค ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋‚ฎ์•„์ง€๊ธฐ ์ „๊นŒ์ง€ ๋…ธ๋“œ์™€ ๋ถ€๋ชจ๋…ธ๋“œ๋ฅผ ๊ณ„์† ๊ตํ™˜ํ•ด์ค๋‹ˆ๋‹ค.

 

์ด ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์ตœ์•…์˜ ๊ฒฝ์šฐ์—๋Š” ์ง€๊ธˆ ๋ฆฌํ”„๋…ธ๋“œ๋ถ€ํ„ฐ ๋ฃจํŠธ๋…ธ๋“œ๊นŒ์ง€ ๊ฑฐ์Šฌ๋Ÿฌ ์˜ฌ๋ผ๊ฐ€๋ฉฐ ๊ตํ™˜์„ ์ง„ํ–‰ํ•˜๊ธฐ ๋•Œ๋ฌธ์—

์‹œ๊ฐ„๋ณต์žก๋„๋Š” ํŠธ๋ฆฌ์˜ ๋†’์ด์— ๋Œ€ํ•œ ๋…ธ๋“œ๊ฐœ์ˆ˜์ธ O(logN)์ด ๋ฉ๋‹ˆ๋‹ค.

 

1๋ฒˆ ๊ณผ์ •๊ณผ 2๋ฒˆ๊ณผ์ •์€ ํ•ฉํ•œ ์ „์ฒด ์‚ฝ์ž…์˜ ๊ตฌํ˜„์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

mutating func insert(node: T) {
    if self.elements.isEmpty {
        self.elements.append(node)
        return
    }
    self.elements.append(node)
    self.swimUp(from: self.elements.endIndex - 1)
}

 

์‚ญ์ œ - O(logN)

์‚ญ์ œ๋ฅผ ์œ„ํ•ด ๋ฃจํŠธ๋…ธ๋“œ์™€ ๋งˆ์ง€๋ง‰ ๋ฆฌํ”„๋…ธ๋“œ๋ฅผ ๊ตํ™˜ํ•ด์ค๋‹ˆ๋‹ค.

์ตœ๋Œ€ํž™์ด ๊นจ์กŒ๊ธฐ ๋•Œ๋ฌธ์— ํŠธ๋ฆฌ๋ฅผ ์žฌ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

๋ฃจํŠธ๋…ธ๋“œ์™€ ์ž์‹๋…ธ๋“œ๋“ค ์ค‘์— ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋” ๋†’์€ ๋…ธ๋“œ๋ฅผ ๊ณจ๋ผ ํ˜„์žฌ ๋ฃจํŠธ๋…ธ๋“œ์™€ ๋ณ€๊ฒฝํ•ด์ค๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ชจ๋“  ์„œ๋ธŒํŠธ๋ฆฌ์˜ ์ตœ๋Œ€ํž™์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋ฃจํŠธ๋…ธ๋“œ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ๊ฒŒ๋ฉ๋‹ˆ๋‹ค.

 

์‚ญ์ œ ๊ตฌํ˜„ - diveDown

์‚ญ์ œ์˜ ์—ฐ์‚ฐ์€

1. ๋ฃจํŠธ๋…ธ๋“œ์™€ ๋ฆฌํ”„๋…ธ๋“œ๋ฅผ ๊ตํ™˜ํ•ฉ๋‹ˆ๋‹ค.
2. ํŠธ๋ฆฌ๋ฅผ ์žฌ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

์œ„ ๋‘๊ฐ€์ง€ ์ˆœ์„œ๋กœ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

 

2๋ฒˆ๊ณผ์ •์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

mutating func diveDown(from index: Int) {
    var higherPriority = index
    let leftIndex = self.leftChild(of: index)
    let rightIndex = self.rightChild(of: index)

    // ์™ผ์ชฝ ์ž์‹ ๋…ธ๋“œ๊ฐ€ ๋” ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์„ ๋•Œ
    if leftIndex < self.elements.endIndex && self.sortFunction(self.elements[leftIndex], self.elements[higherPriority]) {
        higherPriority = leftIndex
    }
    // ์˜ค๋ฅธ์ชฝ ์ž์‹ ๋…ธ๋“œ๊ฐ€ ๋” ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’์„ ๋•Œ
    if rightIndex < self.elements.endIndex && self.sortFunction(self.elements[rightIndex], self.elements[higherPriority]) {
        higherPriority = rightIndex
    }
    // ๊ตํ™˜์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ๋Š” ๊ตํ™˜ ํ›„ ์„œ๋ธŒํŠธ๋ฆฌ๋กœ ์ด๋™
    if higherPriority != index {
        self.elements.swapAt(higherPriority, index)
        self.diveDown(from: higherPriority)
    }
}

๋” ์ด์ƒ ๊ตํ™˜์ด ํ•„์š”์—†์„ ๋•Œ๊นŒ์ง€ ์™ผ์ชฝ ์ž์‹๋…ธ๋“œ์™€ ์˜ค๋ฅธ์ชฝ ์ž์‹๋…ธ๋“œ๋ฅผ ํ˜„์žฌ ๋ฃจํŠธ๋…ธ๋“œ์™€ ๋น„๊ตํ•˜๋ฉด์„œ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋” ๋†’์€ ๋…ธ๋“œ์™€ ๊ตํ™˜์„ ํ•ด์ฃผ๊ณ , ๊ตํ™˜์ด ๋˜๋ฉด ์ƒˆ๋กœ์šด ๋…ธ๋“œ๋ฅผ ๋ฃจํŠธ๋…ธ๋“œ๋กœ ์‚ผ์•„์„œ diveDown์„ ์žฌ๊ท€์ ์œผ๋กœ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

 

์ตœ์•…์˜ ๊ฒฝ์šฐ ๋ฃจํŠธ๋…ธ๋“œ๋ถ€ํ„ฐ ๋ฆฌํ”„๋…ธ๋“œ๊นŒ์ง€ ๊ตํ™˜์„ ์ง„ํ–‰ํ•˜๋Š” ์‹œ๊ฐ„๋ณต์žก๋„ O(logN)๊ฐ€ ์š”๊ตฌ๋ฉ๋‹ˆ๋‹ค.

 

1๋ฒˆ ๊ณผ์ •๊ณผ 2๋ฒˆ๊ณผ์ •์„ ํ•ฉ์ณ ์‚ญ์ œ๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

mutating func remove() -> T? {
    if self.isEmpty { return nil }
    self.elements.swapAt(1, elements.endIndex - 1)
    let deleted = elements.removeLast()
    self.diveDown(from: 1)

    return deleted
}

 

๋ฐฐ์—ด์„ ํž™์œผ๋กœ - Build Heap

๋งŒ์•ฝ ์ƒ์„ฑ์ž๋กœ ๋ฐ›์€ ๋ฐฐ์—ด์— ๊ฐ’์ด ๋“ค์–ด ์žˆ๋‹ค๋ฉด ๋ฐฐ์—ด ์ „์ฒด๋ฅผ ๋‹ค์‹œ ํž™์œผ๋กœ ์žฌ๊ตฌ์„ฑํ•ด์ฃผ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์„ฑ์ž์—์„œ buildHeap์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

mutating func buildHeap() {
    for index in (1...(self.elements.count / 2)).reversed() {
        self.diveDown(from: index)
    }
}

 

์ „์ฒด ์ฝ”๋“œ

import Foundation

struct Heap<T: Comparable> {
    private var elements: [T] = []
    private let sortFunction: (T, T) -> Bool
    
    var isEmpty: Bool {
        return self.elements.count == 1
    }
    var peek: T? {
        if self.isEmpty { return nil }
        return self.elements.last!
    }
    var count: Int {
        return self.elements.count - 1
    }
    
    init(elements: [T] = [], sortFunction: @escaping (T, T) -> Bool) {
        if !elements.isEmpty {
            self.elements = [elements.first!] + elements
        } else {
            self.elements = elements
        }
        self.sortFunction = sortFunction
        if elements.count > 1 {
            self.buildHeap()
        }
    }
    
    func leftChild(of index: Int) -> Int {
        return index * 2 
    }
    func rightChild(of index: Int) -> Int {
        return index * 2 + 1
    }
    func parent(of index: Int) -> Int {
        return (index) / 2
    }
    mutating func add(element: T) {
        self.elements.append(element)
    }
    mutating func diveDown(from index: Int) {
        var higherPriority = index
        let leftIndex = self.leftChild(of: index)
        let rightIndex = self.rightChild(of: index)
        
        if leftIndex < self.elements.endIndex && self.sortFunction(self.elements[leftIndex], self.elements[higherPriority]) {
            higherPriority = leftIndex
        }
        if rightIndex < self.elements.endIndex && self.sortFunction(self.elements[rightIndex], self.elements[higherPriority]) {
            higherPriority = rightIndex
        }
        if higherPriority != index {
            self.elements.swapAt(higherPriority, index)
            self.diveDown(from: higherPriority)
        }
    }
    mutating func swimUp(from index: Int) {
        var index = index
        while index != 1 && self.sortFunction(self.elements[index], self.elements[self.parent(of: index)]) {
            self.elements.swapAt(index, self.parent(of: index))
            index = self.parent(of: index)
        }
    }
    mutating func buildHeap() {
        for index in (1...(self.elements.count / 2)).reversed() {
            self.diveDown(from: index)
        }
    }
    mutating func insert(node: T) {
        if self.elements.isEmpty {
            self.elements.append(node)
        }
        self.elements.append(node)
        self.swimUp(from: self.elements.endIndex - 1)
    }
    mutating func remove() -> T? {
        if self.isEmpty { return nil }
        self.elements.swapAt(1, elements.endIndex - 1)
        let deleted = elements.removeLast()
        self.diveDown(from: 1)
        
        return deleted
    }
}

 


๊ฐ„์†Œํ™” ์ฝ”๋“œ

struct Heap<T: Comparable> {
    private var elements: [T] = []
    private let comparer: (T, T) -> Bool
    
    init(comparer: @escaping (T,T) -> Bool) {
        self.comparer = comparer
    }
    
    mutating func insert(element: T) {
        if elements.isEmpty {
            elements.append(element)
            elements.append(element)
            return
        }
        elements.append(element)
        swimUp(index: elements.count - 1)
    }
    
    mutating private func swimUp(index: Int) {
        var index = index
        while index > 1 && comparer(elements[index], elements[index / 2]) {
            elements.swapAt(index, index / 2)
            index /= 2
        }
    }

    mutating func pop() -> T? {
        if elements.count < 2 { return nil }
        elements.swapAt(1, elements.count - 1)
        let deletedElement = elements.removeLast()
        diveDown(index: 1)
        return deletedElement
    }
    
    mutating private func diveDown(index: Int) {
        var swapIndex = index
        var isSwap = false
        let leftIndex = index * 2
        let rightIndex = index * 2 + 1

        if leftIndex < elements.endIndex && comparer(elements[leftIndex], elements[swapIndex]) {
            swapIndex = leftIndex
            isSwap = true
        }
        
        if rightIndex < elements.endIndex && comparer(elements[rightIndex], elements[swapIndex]) {
            swapIndex = rightIndex
            isSwap = true
        }

        if isSwap {
            elements.swapAt(swapIndex, index)
            diveDown(index: swapIndex)
        }
    }
}

var maxHeap = Heap<Int>(comparer: >)
var minHeap = Heap<Int>(comparer: <)

์ถœ์ฒ˜ : https://dev-mandos.tistory.com/246 (๋งŒ๋„์Šค๋‹˜ ํ‹ฐ์Šคํ† ๋ฆฌ)

๋ฐ˜์‘ํ˜•