2023. 3. 3. 14:08ㆍ자료구조 및 알고리즘/바킹독 정리
바킹독 덱 정리
덱은 Restricted Structure의 끝판왕과 같은 느낌의 자료구조인데, 양쪽 끝에서 삽입과 삭제가 전부 가능합니다. 참고로 자료구조의 덱은 deque고 Double Ended Queue라는 뜻을 가지고 있습니다. 우리가 하스스톤이나 유희왕에서 얘기하는 덱은 Deck라서 발음은 둘 다 덱으로 똑같긴 해도 다른 단어입니다.
아무튼 덱은 양쪽 끝에서 삽입과 삭제가 전부 가능한 자료구조이니 스택과 큐를 덱의 특수한 예시라고 생각해도 괜찮겠습니다.
-바킹독-
덱에서도 삽입, 삭제, 제일 앞/뒤 원소의 확인이 다 O(1)입니다. 그리고 제일 앞/뒤가 아닌 나머지 원소들의 확인/변경이 원칙적으로 불가능한데 독특하게도 STL deque에서는 인덱스로 원소에 접근할 수 있습니다. STL stack, queue에서 불가능했던걸 생각하면 꽤 독특한 일입니다.
-바킹독-
...(중략) 하지만 덱에서는 양쪽에서 모두 삽입이 가능합니다. 그렇기 때문에 마치 여의봉처럼 양쪽으로 확장해야 합니다. 그러면 별 생각 없이 시작 지점이 0번지로 뒀을 경우 왼쪽으로 확장을 할 수가 없게 됩니다. 대신에 시작 지점을 배열의 중간으로 둬야 합니다. 중간으로 두면 중앙에서 양쪽으로 확장이 가능합니다. 그래서 배열의 크기는 2*MX+1이고 head와 tail의 초기값은 MX인 것입니다.
-바킹독-
직접 덱 구현해보기!
#include <iostream>
using namespace std;
const int MX = 1000005;
int dat[2 * MX + 1];
int head = MX, tail = MX;
void push_front(int x) {
dat[--head] = x;
}
void push_back(int x) {
dat[tail++] = x;
}
void pop_front() {
head++;
}
void pop_back() {
tail--;
}
int front() {
return dat[head];
}
int back() {
return dat[tail-1];
}
void test() {
}
int main(void) {
test();
}
[Deque STL 사용하기]
#include <iostream>
#include <deque>
using namespace std;
int main(void) {
deque<int> DQ;
DQ.push_front(10);
DQ.push_back(50); // 10 50
DQ.push_front(24); // 24 10 50
for (auto x : DQ) cout << x;
cout << DQ.size() << '\n'; // 3
if (DQ.empty()) cout << "DQ is empty\n";
else cout << "DQ is not empty\n";
DQ.pop_front(); // 10 50
DQ.pop_back(); // 10
cout << DQ.back() << '\n'; // 10
DQ.push_back(72); // 10 72
cout << DQ.front() << '\n'; // 10
DQ.push_back(12); // 10 72 12
DQ[2] = 17; // 10 72 17
DQ.insert(DQ.begin() + 1, 33); // 10 33 72 17
DQ.insert(DQ.begin() + 4, 60); // 10 33 72 17 60
for (auto x : DQ) cout << x << ' ';
cout << '\n';
DQ.erase(DQ.begin() + 3); // 10 33 72 60
cout << DQ[3] << '\n'; // 60
DQ.clear(); // DQ의 모든 원소 제거
return 0;
}
물론 STL에 덱이 있어서 그냥 가져다쓰면 되는데, STL deque은 매우 독특하게도 double ended queue라는 느낌보다는 vector랑 비슷한데 front에서도 O(1)에 추가와 제거가 가능한 느낌이 강합니다. pop_front, push_front, pop_back, push_front 함수야 당연히 덱이니 있어야 정상인데, 이외에도 19-25번째 줄을 보면 insert도 있고 erase도 있고 인덱스로 원소에 접근도 할 수 있습니다.
이와 같이 STL vector에서 제공되는 기능을 STL deque에서도 다 제공해주고 심지어 deque은 front에서도 O(1)에 추가와 제거가 가능하니 deque이 vector보다 상위호환이 아닌가 하는 생각이 들 수 있겠지만, vector와 달리 deque은 모든 원소들이 메모리 상에 연속하게 배치되어 있지 않습니다. 구조에 대해 자세히 설명을 드리고 싶지만 강의의 난이도를 많이 벗어나는 내용인 것 같아서 궁금하시다면 스스로 c++ deque vs vector와 같은 검색으로 찾아보시면 되겠습니다.
다만 어떤 것 하나만 알고 가시면 되냐면, 앞쪽과 뒷쪽에서의 추가와 제거가 모두 필요하면 당연히 STL deque을 사용하는게 효율적이지만 굳이 앞쪽에서의 추가와 제거가 필요하지 않고 배열과 같은 느낌으로 쓰고싶을 땐 STL deque말고 STL vector를 쓰면 됩니다. 참고로 deque 레퍼런스 문서에도 4번째 문단에 "Both vectors and deques provide a very similar interface" 어쩌구저쩌구 하면서 관련 얘기가 나와있어서 한 번 읽어보시는 것도 좋습니다.(레퍼런스 링크, 예제 링크) - 링크는 바킹독님 블로그 가서....!
-바킹독-
BOJ 문제 풀기 10866 덱
#include <iostream>
#include <deque>
using namespace std;
int main(void) {
ios::sync_with_stdio(0);
cin.tie(0);
deque<int> DQ;
int n;
cin >> n;
while (n--) {
string q;
cin >> q;
if (q == "push_back") {
int val;
cin >> val;
DQ.push_back(val);
}
else if (q == "push_front") {
int val;
cin >> val;
DQ.push_front(val);
}
else if (q == "pop_front") {
if (DQ.empty())
cout << -1 << '\n';
else {
cout << DQ.front() << '\n';
DQ.pop_front();
}
}
else if (q == "pop_back") {
if (DQ.empty())
cout << -1 << '\n';
else {
cout << DQ.back() << '\n';
DQ.pop_back();
}
}
else if (q == "size")
cout << DQ.size() << '\n';
else if (q == "empty")
cout << DQ.empty() << '\n';
else if (q == "front") {
if (DQ.empty())
cout << -1 << '\n';
else
cout << DQ.front() << '\n';
}
else{ // back
if (DQ.empty())
cout << -1 << '\n';
else
cout << DQ.back() << '\n';
}
}
}
출처) 바킹독 블로그 https://blog.encrypted.gg/935
[실전 알고리즘] 0x07강 - 덱
안녕하세요, 오늘도 반갑습니다. 스택과 큐에 이어 이번에는 덱을 다루겠습니다. 목차가 0x02만 바뀌고 계속 똑같습니다. 한 번 눈으로 슥 훑고 넘어가겠습니다. 덱은 Restricted Structure의 끝판왕과
blog.encrypted.gg
'자료구조 및 알고리즘 > 바킹독 정리' 카테고리의 다른 글
[바킹독 시뮬레이션] 정리 중 아직은 뉴비 (0) | 2023.04.08 |
---|---|
[바킹독 BFS] BFS 기본 문제 유형 분석 (0) | 2023.04.03 |
[바킹독] BFS 정리(진행 중) (0) | 2023.03.27 |
큐 강의 정리 (0) | 2023.02.24 |
[바킹독 강의]스택 정리 (1) | 2023.02.15 |