(백준/C++) 11286_절댓값 힙
11286번: 절댓값 힙 (acmicpc.net) 문제는 힙을 절대값을 고려해서 만드는 문제입니다.
힙(heap)이란, 최대값이나 최소값을 빠르게 찾기 위한 완전 이진 트리를 기본으로 한 자료구조로, 매 삽입/삭제시 조건을 만족하는 정렬을 수행합니다.
이때 부모(P)와 자식(C) 사이의 정렬을 수행하지만, 자식간(LC, RC)의 정렬은 보장하지 않아도 되기 때문에 $O(logN)$의 수행 시간을 갖습니다.
(최소/최대 값을 찾는 시간 복잡도는 $O(1)$이지만, pop을 수행한다면 $O(logN)$이 됩니다.)
접근 방법
이 문제는 절대 값을 포함한 최소 힙(Min Heap) 문제로 볼 수 있습니다.
배열에서 절댓값이 가장 작은 값을 출력하고, 그 값을 배열에서 제거한다. 절댓값이 가장 작은 값이 여러개일 때는, 가장 작은 수를 출력하고, 그 값을 배열에서 제거한다.
따라서 이 문제는
- 최소 힙(Min Heap) 자료구조를 사용하고
- 절댓값을 기준으로 최소 힙을 구성하되, 절댓값이 같을 경우 실제 값이 작은 것을 우선으로 정렬하면 됩니다.
부모 요소 인덱스 찾기
- index 0 부터 시작하는 경우, 부모의 인덱스는 $(Child - 1) / 2$로 구할 수 있습니다.
자식 요소 인덱스 찾기
- index 0 부터 시작하는 경우, 왼쪽 자식은 $Parent * 2 + 1$로 구할 수 있습니다.
- 오른쪽 자식은 쉽게 $LChild+1$로 구할 수 있습니다.
풀이
이 문제를 풀 때, 고려해야 할 점은 절대 값이 같은 숫자의 경우입니다.
위에서부터 아래쪽으로 검사할 때(pop), 자식의 선택에 있어서도 이 조건이 고려되어야 합니다.
1
abs(heap[lchild]) == abs(heap[rchild]) && heap[lchild] < heap[rchild]
LChild와 RChild의 절대 값이 같은 경우, 가장 작은 수를 선택합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <iostream>
using namespace std;
class absoluteHeap
{
public:
absoluteHeap(int n)
{
heap = static_cast<int*>(malloc(sizeof(int) * n));
reserveSize = n;
}
~absoluteHeap()
{
free(heap);
}
void input(int num)
{
// 0이 입력되면 출력 후 pop한다.
if(num == 0)
cout << pop() << '\n';
else
push(num);
}
private:
// 1.절대값이 작은 숫자를 위로 보내고
// 2.같은 수인 경우 작은 숫자를 위로 보낸다
void push(int n)
{
if (currentSize >= reserveSize) return;
heap[currentSize++] = n;
int child = currentSize - 1;
int parent = (child - 1) / 2;
while (child > 0 && abs(heap[parent]) >= abs(heap[child]))
{
// 2-1.부모가 자식보다 작으면, 정렬이 완료된 것으로 보고 종료한다
if (abs(heap[parent]) == abs(heap[child]) && heap[parent] < heap[child]) break;
swap(heap[parent], heap[child]);
child = parent;
parent = (child - 1) / 2;
}
}
int pop()
{
if (currentSize <= 0) return 0;
int result = heap[0];
heap[0] = heap[--currentSize];
int parent = 0;
while (parent < currentSize)
{
int lchild = parent * 2 + 1;
int rchild = lchild + 1;
int child = -1;
if(lchild < currentSize && rchild < currentSize)
{
if (abs(heap[lchild]) < abs(heap[rchild]) || // 절대값이 작거나
(abs(heap[lchild]) == abs(heap[rchild]) && heap[lchild] < heap[rchild])) // 같은 경우 음수 쪽을 선택한다.
child = lchild;
else
child = rchild;
}
else if(lchild < currentSize)
{
child = lchild;
}
if(child != -1 && abs(heap[parent]) > abs(heap[child]) || // 절대값이 작거나
(abs(heap[parent]) == abs(heap[child]) && heap[parent] > heap[child])) // 같은 경우 음수 라면 바꾼다.
{
swap(heap[parent], heap[child]);
parent = child;
}
else
{
break; // child가 없는 경우 종료한다
}
}
return result;
}
private:
int* heap;
int reserveSize = 0;
int currentSize = 0;
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
int N;
cin >> N;
absoluteHeap heap = absoluteHeap(N);
while(N--)
{
int tmp;
cin >> tmp;
heap.input(tmp);
}
}
이 기사는 저작권자의 CC BY-NC-ND 4.0 라이센스를 따릅니다.