힙 문제 목록 링크 / 문제 링크 / Github 링크 |
---|
import Foundation
func solution(_ operations:[String]) -> [Int] {
return []
}
※ 이전 글에서 이어짐
- 이중 우선순위 큐 구현 옵션
- 두 개의 힙 사용 (Min-Heap + Max-Heap) ❌
- Min-Max Heap 사용 ✅
- Balanced Binary Search Tree (균형 이진 탐색 트리)
import Foundation
// Min-Max Heap 구조체 정의
struct MinMaxHeap<T: Comparable> {
// 힙 요소를 저장하는 배열
var elements: Array<T> = []
// 힙의 최소값 (Min-Max Heap에서 항상 루트 노드에 위치)
var minimum: T? {
guard !elements.isEmpty else { return nil }
return elements[0]
}
// 힙의 최대값 (Min-Max Heap에서 루트의 두 자식 중 큰 값)
var maximum: T? {
guard !elements.isEmpty else { return nil }
if elements.count == 1 {
return elements[0] // 힙에 하나만 있으면 최소값과 최대값이 동일
} else if elements.count == 2 {
return elements[1] // 루트의 하나의 자식만 있는 경우
} else {
return max(elements[1], elements[2]) // 두 자식 중 더 큰 값 반환
}
}
// 최대값 제거
mutating func removeMax() -> T? {
guard !elements.isEmpty else { return nil }
if elements.count == 1 {
return elements.removeLast() // 하나만 남은 경우 바로 제거
} else if elements.count == 2 {
return elements.removeLast() // 두 개만 있는 경우 루트의 자식 제거
} else {
let maxIndex = elements[1] > elements[2] ? 1 : 2 // 최대값의 인덱스
let maxValue = elements[maxIndex]
elements[maxIndex] = elements.removeLast() // 최대값을 마지막 원소로 대체
bubbleDown(from: maxIndex) // Max-level의 규칙 복구
return maxValue
}
}
// 최소값 제거
mutating func removeMin() -> T? {
guard !elements.isEmpty else { return nil }
let minValue = elements[0] // 루트에 있는 최소값 저장
if elements.count == 1 {
elements.removeLast() // 하나만 남은 경우 바로 제거
} else {
elements[0] = elements.removeLast() // 마지막 원소로 루트를 대체
bubbleDown(from: 0) // Min-level의 규칙 복구
}
return minValue
}
// 새 값 삽입
mutating func insert(_ value: T) {
elements.append(value) // 힙의 맨 끝에 삽입
bubbleUp(from: elements.count - 1) // 적절한 위치로 이동하며 Min-Max Heap 규칙 유지
}
// 삽입 시 값의 위치를 올바르게 조정
private mutating func bubbleUp(from index: Int) {
let currentIndex = index
let parentIndex = self.parentIndex(index)
if isMinLevel(currentIndex) { // 현재 노드가 Min-level인 경우
if elements[parentIndex] < elements[currentIndex] { // 부모가 더 작으면 Max-level 규칙 위반
elements.swapAt(parentIndex, currentIndex)
bubbleUpMax(from: parentIndex) // Max-level로 버블 업
} else {
bubbleUpMin(from: currentIndex) // Min-level로 버블 업
}
} else { // 현재 노드가 Max-level인 경우
if elements[parentIndex] > elements[currentIndex] { // 부모가 더 크면 Min-level 규칙 위반
elements.swapAt(parentIndex, currentIndex)
bubbleUpMin(from: parentIndex) // Min-level로 버블 업
} else {
bubbleUpMax(from: currentIndex) // Max-level로 버블 업
}
}
}
// Min-level에서 버블 업 (조부모와 비교하며 이동)
private mutating func bubbleUpMin(from index: Int) {
var currentIndex = index
while let grandparentIndex = self.grandparentIndex(currentIndex), elements[currentIndex] < elements[grandparentIndex] {
elements.swapAt(grandparentIndex, currentIndex)
currentIndex = grandparentIndex
}
}
// Max-level에서 버블 업 (조부모와 비교하며 이동)
private mutating func bubbleUpMax(from index: Int) {
var currentIndex = index
while let grandparentIndex = self.grandparentIndex(currentIndex), elements[currentIndex] > elements[grandparentIndex] {
elements.swapAt(grandparentIndex, currentIndex)
currentIndex = grandparentIndex
}
}
// 버블 다운 (값을 적절히 이동시켜 Min-Max Heap 규칙 유지)
private mutating func bubbleDown(from index: Int) {
if isMinLevel(index) {
bubbleDownMin(from: index)
} else {
bubbleDownMax(from: index)
}
}
// Min-level에서 버블 다운
private mutating func bubbleDownMin(from index: Int) {
guard let smallestIndex = findSmallestCandidateIndex(from: index) else { return }
if elements[index] > elements[smallestIndex] {
if isGrandChildIndex(smallestIndex, of: index) { // 손자와 교환
elements.swapAt(index, smallestIndex)
let parentIndex = self.parentIndex(smallestIndex)
if elements[smallestIndex] < elements[parentIndex] { // 부모와의 규칙도 확인
elements.swapAt(smallestIndex, parentIndex)
}
bubbleDownMin(from: smallestIndex)
} else { // 자식과 교환
elements.swapAt(index, smallestIndex)
}
}
}
// Max-level에서 버블 다운
private mutating func bubbleDownMax(from index: Int) {
guard let largestIndex = findLargestCandidateIndex(from: index) else { return }
if elements[index] < elements[largestIndex] {
if isGrandChildIndex(largestIndex, of: index) { // 손자와 교환
elements.swapAt(index, largestIndex)
let parentIndex = self.parentIndex(largestIndex)
if elements[largestIndex] < elements[parentIndex] { // 부모와의 규칙도 확인
elements.swapAt(largestIndex, parentIndex)
}
bubbleDownMax(from: largestIndex)
} else { // 자식과 교환
elements.swapAt(index, largestIndex)
}
}
}
// 현재 노드가 Min-level인지 확인
private func isMinLevel(_ index: Int) -> Bool {
Int(log2(Double(index + 1))) % 2 == 0
}
// 부모 노드의 인덱스 계산
private func parentIndex(_ index: Int) -> Int {
( index - 1 ) / 2
}
// 조부모 노드의 인덱스 계산
private func grandparentIndex(_ index: Int) -> Int? {
guard index > 2 else { return nil }
return parentIndex(parentIndex(index))
}
// 왼쪽 자식 노드의 인덱스 계산
private func leftChildIndex(_ index: Int) -> Int {
index * 2 + 1
}
// 오른쪽 자식 노드의 인덱스 계산
private func rightChildIndex(_ index: Int) -> Int {
index * 2 + 2
}
// Min-level에서 가장 작은 후보 인덱스 찾기
private func findSmallestCandidateIndex(from index: Int) -> Int? {
let indices = [leftChildIndex(index), rightChildIndex(index),
leftChildIndex(leftChildIndex(index)), rightChildIndex(leftChildIndex(index)),
leftChildIndex(rightChildIndex(index)), rightChildIndex(rightChildIndex(index))]
return indices.filter { $0 < elements.count }.min(by: { elements[$0] < elements[$1] })
}
// Max-level에서 가장 큰 후보 인덱스 찾기
private func findLargestCandidateIndex(from index: Int) -> Int? {
let indices = [leftChildIndex(index), rightChildIndex(index),
leftChildIndex(leftChildIndex(index)), rightChildIndex(leftChildIndex(index)),
leftChildIndex(rightChildIndex(index)), rightChildIndex(rightChildIndex(index))]
return indices.filter { $0 < elements.count }.max(by: { elements[$0] < elements[$1] })
}
// 특정 노드가 손자인지 확인
private func isGrandChildIndex(_ childIndex: Int, of parentIndex: Int) -> Bool {
let leftChildIndex = self.leftChildIndex(parentIndex)
let rightChildIndex = self.rightChildIndex(parentIndex)
return childIndex == self.leftChildIndex(leftChildIndex) || childIndex == self.rightChildIndex(leftChildIndex) || childIndex == self.leftChildIndex(rightChildIndex) || childIndex == self.rightChildIndex(rightChildIndex)
}
}
- solution 함수 구현
I 숫자
: 큐에 주어진 숫자를 삽입합니다.D 1
: 큐에서 최댓값을 삭제합니다.D -1
: 큐에서 최솟값을 삭제합니다.- 큐가 비어있으면
[0,0]
, 비어있지 않으면[최댓값, 최솟값]
을 반환- 큐가 비어있으면 삭제 연산 무시
- 최댓값/최솟값 연산의 대상이 둘 이상인 경우 하나만 삭제
func solution(_ operations:[String]) -> [Int] {
var heap = MinMaxHeap<Int>()
for operation in operations {
let commands = operation.components(separatedBy: " ")
let behaviour = commands[0]
guard let value = Int(commands[1]) else { continue }
if behaviour == "I" {
heap.insert(value)
} else {
if value == 1 {
_ = heap.removeMax()
} else {
_ = heap.removeMin()
}
}
}
let maxValue = heap.maximum ?? 0
let minValue = heap.minimum ?? 0
return [maxValue, minValue]
}
테스트 통과 실패 |
---|
func solution(_ operations:[String]) -> [Int] {
var heap = MinMaxHeap<Int>()
print("") // 디버깅 print
for operation in operations {
let commands = operation.components(separatedBy: " ")
guard commands.count == 2, let value = Int(commands[1]) else { continue }
let behaviour = commands[0]
if behaviour == "I" {
heap.insert(value)
} else if behaviour == "D" {
if value == 1 {
_ = heap.removeMax()
} else if value == -1 {
_ = heap.removeMin()
}
}
print("[\(operation)] \(heap.elements)") // 디버깅 print
}
let maxValue = heap.maximum ?? 0
let minValue = heap.minimum ?? 0
// 비어 있는 경우 [0, 0] 반환
return heap.elements.isEmpty ? [0, 0] : [maxValue, minValue]
}
print 동작 안함 |
---|
그냥
sort
쓰기
import Foundation
struct MinMaxHeap {
private var elements: [Int] = []
var isEmpty: Bool {
return elements.isEmpty
}
var max: Int? {
return elements.max()
}
var min: Int? {
return elements.min()
}
mutating func insert(_ value: Int) {
elements.append(value)
elements.sort()
}
mutating func deleteMax() {
if let maxIndex = elements.indices.max(by: { elements[$0] < elements[$1] }) {
elements.remove(at: maxIndex)
}
}
mutating func deleteMin() {
if let minIndex = elements.indices.min(by: { elements[$0] < elements[$1] }) {
elements.remove(at: minIndex)
}
}
}
// Solution Function
func solution(_ operations: [String]) -> [Int] {
var heap = MinMaxHeap()
for operation in operations {
let components = operation.split(separator: " ")
let command = components[0]
let value = Int(components[1])!
if command == "I" {
heap.insert(value)
} else if command == "D" {
if value == 1 {
heap.deleteMax()
} else if value == -1 {
heap.deleteMin()
}
}
}
if heap.isEmpty {
return [0, 0]
} else {
return [heap.max ?? 0, heap.min ?? 0]
}
}
테스트 통과 |
---|
???: 바보 같은 놈, 네 녀석이 아무리 기어도 sort() 하나에 정리되는 것을.. 네가 이진트리를 붙잡으며 낭비한 시간을 땅을 치며 후회하도록 해라. |
주어진 원소 A에 대하여,
- 새로운 원소 A를 힙의 맨 뒤에 추가한다.
- A가 위치한 레벨이 Min-level인지 Max-level인지 판단한다.
- 추가된 원소가 트리의 깊이(레벨)를 기준으로 Min-level인지 Max-level인지 결정된다.
- (깊이가 짝수면 Min-level, 홀수면 Max-level.)
- A를 부모와 비교한다:
- A가 Max-level이고 A가 부모보다 작다면, A와 부모를 교환한다.
- A가 Min-level이고 A가 부모보다 크다면, A와 부모를 교환한다.
- A를 조부모와 비교한다:
- A가 Min-level이고 A가 조부모보다 작다면, A와 조부모를 교환한다.
- A가 Max-level이고 A가 조부모보다 크다면, A와 조부모를 교환한다.
- 부모 또는 조부모와 교환이 이루어졌다면, A의 새로운 위치에서 3번 과정을 반복한다.
- 교환이 더 이상 필요하지 않을 경우, 알고리즘을 종료한다.
주어진 원소 A에 대하여,
- 만약 A가 Min-level이라면:
- A의 자식들과 손자들 중 가장 작은 값을 가진 원소를 B라고 하고 B의 인덱스를 i라고 한다.
- B가 A보다 더 작은 값을 가지고 있다면:
- 만약 B가 A의 손자라면:
- B와 A의 위치를 서로 바꾼다.
- A가 새로운 위치에서 부모보다 더 큰 값을 가지게 되었다면, A와 부모의 위치를 바꾼다.
- i번째 위치에서 Bubble Down 알고리즘을 재귀적으로 다시 수행한다.
- 만약 B가 A의 자식이라면:
- B와 A의 위치를 바꾸고 알고리즘을 종료한다(자식 레벨만 점검해도 규칙이 복구됨).
- 만약 A가 Max-level이라면:
- A의 자식들과 손자들 중 가장 큰 값을 가진 원소를 B라고 하고 B의 인덱스를 i라고 한다.
- B가 A보다 더 큰 값을 가지고 있다면:
- 만약 B가 A의 손자라면:
- B와 A의 위치를 서로 바꾼다.
- A가 새로운 위치에서 부모보다 더 작은 값을 가지게 되었다면, A와 부모의 위치를 바꾼다.
- i번째 위치에서 Bubble Down 알고리즘을 재귀적으로 다시 수행한다.
- 만약 B가 A의 자식이라면:
- B와 A의 위치를 바꾸고 알고리즘을 종료한다(자식 레벨만 점검해도 규칙이 복구됨).
본문의 내용과 관계는 없지만, 오늘 ChatGPT와 대화했던 내용:
https://chatgpt.com/share/6753032e-b580-8008-aea7-e456ecc67107