DS Certi (Pro) Heap (기본 문제) - jjin-choi/study_note GitHub Wiki
-
Max heap (최대 힙)
-
완전 이진 트리 : 이진 트리의 원소가 왼쪽부터 채워짐. 더 채울 수 없는 경우 새로운 레벨의 왼쪽부터 채움
-
부모 노드의 값 >= 자식 노드의 값
-
배열의 1번부터 채우자 !
-
-
포인터
- 그 동안 int 형 포인터 / 구조체 포인터를 사용해 왔는데, (ex. int* A, _node* next)
- 그 변수가 저장되어 있는 메모리가 항상 있고 그 메모리 위치를 가리키는 게 포인터이기 때문에
- 함수 포인터도 사용 가능 ! 해당 함수의 위치가 기록되어 있음.
- 선언을 할 때에는 return type 과 argv type 을 맞추어 선언해주면 된다.
bool (*comp) (int, int); // 함수 포인터 선언
bool maxComp (int a, int b) { return a > b; }
bool minComp (int a, int b) { return a < b; }
comp = maxComp;
comp(1, 2)
#include <stdio.h>
const int MAX = (int)(5e5 + 3);
int heap[MAX];
int N, in, hn;
void swap(int& a, int& b) { int temp = a; a = b; b = temp; }
bool comp(int a, int b) { return (a > b); }
void push_heap(int num) {
heap[++hn] = num;
// c : 자식 노드
for (int c = hn; c > 1; c /= 2) {
// comp (앞, 뒤) : 앞이 우선순위가 높으면 바꿔 주겠다.
if (comp(heap[c], heap[c / 2])) swap(heap[c], heap[c / 2]);
else break;
}
}
int pop_heap() {
int ret = heap[1];
swap(heap[1], heap[hn--]);
for (int c = 2; c <= hn; c *= 2) {
// 일단 오른쪽이 있으면 (c < hn) 왼쪽과 오른쪽 비교
if (c < hn && heap[c + 1] > heap[c]) c++;
// 부모와 우선 순위 높은 자식 비교
if (comp(heap[c], heap[c / 2])) swap(heap[c], heap[c / 2]);
else break;
}
return ret;
}
void print_heap() {
for (int n = 1; n <= N; n++)
printf("%d ", heap[n]);
printf("\n");
}
int main()
{
freopen("input.txt", "r", stdin);
scanf("%d", &N);
for (int n = 0; n < N; n++) {
scanf("%d ", &in);
push_heap(in);
}
print_heap();
for (int n = 1; n <= N; n++) pop_heap();
print_heap();
return 0;
}
#include <stdio.h>
typedef unsigned long long ull;
const int MAX = (5000 + 3);
int N, in;
ull ans;
bool max(int x, int y) { return x > y; }
bool min(int x, int y) { return x < y; }
void swap(int& x, int &y) { int temp = x; x = y; y = temp; }
struct priority_queue {
int hn;
int heap[MAX];
bool (*comp)(int, int);
void init(int type) { hn = 0; comp = type ? max : min; }
void push(int num) {
heap[++hn] = num;
for (int c = hn; c > 1; c /= 2) {
if (comp(heap[c], heap[c / 2])) swap(heap[c], heap[c / 2]);
else break;
}
}
int pop() {
int ret = heap[1];
heap[1] = heap[hn--];
for (int c = 2; c <= hn; c *= 2) {
if (c < hn && !comp(heap[c], heap[c + 1])) c++;
if (comp(heap[c], heap[c / 2])) swap(heap[c], heap[c / 2]);
else break;
}
return ret;
}
int top() { return heap[1]; }
int size() { return hn; }
} maxpq, minpq;
int main()
{
freopen("input.txt", "r", stdin);
scanf("%d", &N);
minpq.init(0);
for (int n = 0; n < N; n++) {
scanf("%d ", &in);
minpq.push(in);
}
for (int n = 0; n < N && minpq.hn > 1; n++) {
ull sum = minpq.pop() + minpq.pop();
ans += sum;
minpq.push(sum);
}
printf("%d\n", ans);
return 0;
}
#include <stdio.h>
typedef unsigned long long ull;
const int MAX = 1500;
ull ugly[MAX];
int ucnt = 1, in;
void swap(ull &x, ull &y) { int temp = x; x = y; y = temp; }
bool max(ull x, ull y) { return x > y; }
bool min(ull x, ull y) { return x < y; }
struct priority_queue {
int hn;
ull heap[MAX + 3];
bool(*comp) (ull, ull);
void init(int type) { hn = 0; comp = type ? max : min; }
void push(ull num) {
heap[++hn] = num;
for (int c = hn; c > 1; c /= 2) {
if (comp(heap[c], heap[c / 2])) swap(heap[c], heap[c / 2]);
else break;
}
}
ull pop() {
if (hn <= 0) return 0;
ull ret = heap[1];
heap[1] = heap[hn--];
for (int c = 2; c <= hn; c *= 2) {
if (c < hn && !comp(heap[c], heap[c + 1])) c++;
if (comp(heap[c], heap[c / 2])) swap(heap[c], heap[c / 2]);
else break;
}
return ret;
}
} minpq, maxpq;
void make_ugly_number() {
int weight[3] = { 2, 3, 5 };
minpq.push(ucnt);
while (ucnt <= MAX) {
ull top = minpq.pop();
if (ugly[ucnt - 1] == top) continue;
ugly[ucnt++] = top;
for (int i = 0; i < 3; i++)
minpq.push(top * weight[i]);
}
}
int main()
{
freopen("input.txt", "r", stdin);
minpq.init(0);
make_ugly_number();
while (scanf("%d", &in) && in)
printf("%llu\n", ugly[in]);
return 0;
}
#include <stdio.h>
const int MAX = (2e5 + 5);
int N, M, in;
void swap(int &x, int&y) { int temp = x; x = y; y = temp; }
bool max(int x, int y) { return x > y; }
bool min(int x, int y) { return x < y; }
struct priority_queue {
int hn;
int heap[MAX];
bool(*comp)(int, int);
void init(int type) { hn = 0; comp = type ? max : min; }
void push(int num) {
heap[++hn] = num;
for (int c = hn; c > 1; c /= 2) {
if (comp(heap[c], heap[c / 2])) swap(heap[c], heap[c / 2]);
else break;
}
}
int pop() {
int ret = heap[1];
heap[1] = heap[hn--];
for (int c = 2; c <= hn; c *= 2) {
if (c < hn && !comp(heap[c], heap[c + 1])) c++;
if (comp(heap[c], heap[c / 2])) swap(heap[c], heap[c / 2]);
else break;
}
return ret;
}
int size() { return hn; }
} minpq, maxpq;
int main()
{
minpq.init(0);
maxpq.init(1);
freopen("input.txt", "r", stdin);
scanf("%d %d", &N, &M);
printf("%d\n", M);
for (int n = 0; n < N / 2; n++) {
for (int i = 0; i < 2; i++) {
scanf("%d ", &in);
if (in < M) maxpq.push(in);
else minpq.push(in);
}
if (minpq.size() > maxpq.size()) {
maxpq.push(M);
M = minpq.pop();
}
else if (minpq.size() < maxpq.size()) {
minpq.push(M);
M = maxpq.pop();
}
printf("%d\n", M);
}
return 0;
}
- 중간 원소 update
-
lazy update (erase / update)
. pop 할 때 유효하지 않은 것을 버리는 방식
. // erase //
. 중간 원소 erase 될 때, 별도의 배열로 유효한 값인지 표시
. top 뽑아낼 때 유효한 값인지 판별 후 유효하지 않으면 pop 하고 유효한 값 나올 때까지 반복
. // update //
. 중간 원소 update 될 때, update 된 정보를 heap 에 push.
. 중복해서 들어가기 때문에 실제 정보가 저장되는 별도의 배열이 있어,
. top 뽑아낼 때 실제 정보와 같은지 판별하여 그렇지 않다면 pop 하고 유효한 값 나올 때까지 반복 -
real time update (erase / update)
. 이번 heap 에는 id 만 들어가고 중복되는 값이 없음.
. 각 원소들의 heap index 를 기록하는 별도의 배열 (pos[]) 필요.
. swap 시 heap 과 pos 둘다 swap 되어야 함.
. 특정 id가 들어왔을 때, pos[id]를 접근하여 heap의 index를 바로 구할 수 있다.
. // erase //
. 중간 원소 erase 될 때, 마지막 원소를 해당 위치(pos[id])로 (지울 원소 위치) 갖고 와서 up(), down() 으로 본인 자리를 찾아감.
. 즉, 기존 pop 와 동일한데 pop 에서 heap[1] = heap[hn--] 에서 1 이 아니라 해당 위치로..
. // update //
. 중간 원소 update 될 때, 해당 위치 (pos[id]) 값을 update 해주고 up(), down()으로 본인 자리를 찾아간다.
-
struct _priority_queue {
int hn;
int heap[MAX];
int pos[MAX]; // id 범위만큼 필요 ( 이 문제 id 는 배열의 index )
// lazy update
#include <stdio.h>
const int MAX = (int)(1e5 + 3);
int N, Q, S[MAX];
// lazy 업데이트 할 때에는 id 가 중복되기 때문에 구조체가 필요
struct _sequence {
int idx;
int val;
} seq[MAX];
template <typename _TYPE>
void swap(_TYPE &x, _TYPE &y) { _TYPE temp = x; x = y; y = temp; }
bool max (_sequence x, _sequence y) {
if (x.val == y.val) return x.idx > y.idx;
return x.val > y.val;
}
bool min(_sequence x, _sequence y) {
if (x.val == y.val) return x.idx < y.idx;
return x.val < y.val;
}
struct _priority_queue {
int hn;
_sequence heap[MAX + MAX]; // query 수도 추가해주어야 함.. !
bool (*comp)(_sequence, _sequence);
void init(int type) { hn = 0; comp = type ? max : min; }
void push(_sequence seq) {
heap[++hn] = seq;
for (int c = hn; c > 1; c /= 2) {
if (comp(heap[c], heap[c / 2])) swap(heap[c], heap[c / 2]);
else break;
}
}
void pop() {
heap[1] = heap[hn--];
for (int c = 2; c <= hn; c *= 2) {
if (c < hn && !comp(heap[c], heap[c + 1])) c++;
if (comp(heap[c], heap[c / 2])) swap(heap[c], heap[c / 2]);
else break;
}
}
_sequence top() {
_sequence ret;
while (1) {
ret = heap[1];
if (S[ret.idx] != ret.val) pop();
else break;
}
return ret;
}
} minpq, maxpq;
int main()
{
minpq.init(0);
maxpq.init(1);
freopen("input.txt", "r", stdin);
scanf("%d %d", &N, &Q);
for (int n = 1; n <= N; n++) {
scanf("%d", S + n);
minpq.push({ n, S[n] });
maxpq.push({ n, S[n] });
}
int cmd, idx, val;
for (int q = 0; q < Q; q++) {
scanf("%d ", &cmd);
if (cmd == 1) {
scanf("%d %d", &idx, &val);
S[idx] = val;
minpq.push({ idx, val });
maxpq.push({ idx, val });
}
if (cmd == 2) {
_sequence min = minpq.top();
printf("%d\n", min.idx);
}
if (cmd == 3) {
_sequence max = maxpq.top();
printf("%d\n", max.idx);
}
}
return 0;
}
- real time 일 때 함수 추가할 내용
- up() : 현재 위치부터 /=2 하면서 조건에 맞으면 swap
- down() : 현재 위치가 parent 가 되기 때문에 parent * 2 부터 *=2 하면서 조건에 맞으면 swap
- erase() : (pop 형태와 비슷) pos[val] 로 hidx 가져와서 hidx 와 hn-- 에 대해 swap 후 up / down
- update() : (push 형태와 비슷) pos[val] 로 hidx 가져와서 up / down
- swap() : heap 과 pos 둘 다 값 변경해주도록
struct _priority_queue {
int hn;
int heap[MAX]; // val 저장. 실제 정보는 S 배열에서 관리.
int pos[MAX]; // val 의 heap index 저장. val 범위만큼 필요
bool(*comp)(int, int);
void init(int type) {
hn = 0; comp = type ? max : min;
}
void swap(int x, int y) {
_swap(heap[x], heap[y]);
_swap(pos[heap[x]], pos[heap[y]]); // pos 에서의 index 는 heap[] 의 값인 것에 주의
}
void up(int hidx) {
for (int c = hidx; c > 1; c /= 2) {
if (comp(heap[c], heap[c / 2])) swap(c, c / 2);
else break;
}
}
void down (int hidx) {
// hidx 가 부모이고 그 자식들 입장에서 움직여야 하므로, c 는 hidx * 2 , 즉 왼쪽 자식부터 시작
for (int c = hidx * 2; c <= hn; c *= 2) {
if (c < hn && !comp(heap[c], heap[c + 1])) c++;
if (comp(heap[c], heap[c / 2])) swap(c, c / 2);
else break;
}
}
void update(int val) {
int hidx = pos[val];
up(hidx), down(hidx);
}
void erase(int val) {
int hidx = pos[val];
swap(hidx, hn--);
up(hidx), down(hidx);
}
void push(int val) {
heap[++hn] = val;
pos[val] = hn;
up(hn); // 할 때에는 현재 hidx 로 하기 때문에 hn
}
void pop() {
swap(1, hn--);
down(1);
}
int top() { return heap[1]; }
} minpq, maxpq;
#include <stdio.h>
typedef unsigned long long ull;
const int MAX = (int)(1e5 + 3);
int N, M;
struct _member { int id; int freq; } mem[MAX];
template <typename _Type>
void _swap(_Type &x, _Type &y) { _Type temp = x; x = y; y = temp; }
bool max(_member x, _member y) {
if (x.freq == y.freq) return x.id > y.id;
return x.freq > y.freq;
}
bool min(_member x, _member y) {
if (x.freq == y.freq) return x.id < y.id;
return x.freq < y.freq;
}
struct _priority_queue {
int hn;
int pos[MAX]; // id 들의 heap index 기록
_member heap[MAX];
bool(*comp)(_member, _member);
void init(int type) { hn = 0; comp = type ? max : min; }
void swap(int x, int y) {
_swap(heap[x], heap[y]);
_swap(pos[heap[x].id], pos[heap[y].id]);
}
void up(int hidx) {
for (int c = hidx; c > 1; c /= 2) {
if (comp(heap[c], heap[c / 2])) swap(c, c / 2);
else break;
}
}
void down(int hidx) {
for (int c = hidx * 2; c <= hn; c *= 2) {
if (c < hn && !comp(heap[c], heap[c + 1])) c++;
if (comp(heap[c], heap[c / 2])) swap(c, c / 2);
else break;
}
}
void push(_member newbie) {
heap[++hn] = newbie;
pos[newbie.id] = hn;
up(hn);
}
void pop() {
swap(1, hn--);
down(1);
}
void erase(_member member) {
int hidx = pos[member.id];
swap(hidx, hn--);
up(hidx), down(hidx);
}
_member top() { return heap[1]; }
} minpq, maxpq;
ull total_frequency() {
ull sum = 0;
if (minpq.hn <= 2) return 0;
for (int m = 1; m <= minpq.hn; m++)
sum += minpq.heap[m].freq;
int max_freq = maxpq.top().freq;
int min_freq = minpq.top().freq;
return sum - max_freq - min_freq;
}
int main()
{
freopen("input.txt", "r", stdin);
scanf("%d %d", &N, &M);
minpq.init(0);
maxpq.init(1);
int cmd, id, freq;
for (int m = 0; m < M; m++) {
scanf("%d", &cmd);
if (cmd == 0) {
scanf("%d %d", &id, &freq);
minpq.push({ id, freq });
maxpq.push({ id, freq });
}
if (cmd == 1) {
if (minpq.hn) {
_member min_mem = minpq.top();
minpq.pop();
maxpq.erase(min_mem);
printf("%d\n", min_mem.id);
}
}
if (cmd == 2) {
if (maxpq.hn) {
_member max_mem = maxpq.top();
maxpq.pop();
minpq.erase(max_mem);
printf("%d\n", max_mem.id);
}
}
if (cmd == 3) {
ull ans = total_frequency();
printf("%d\n", ans);
}
}
return 0;
}