모든 사진 자료에 관한 출처는 아래 알튜비튜 - 튜터링에 있음을 밝힙니다.
https://github.com/Altu-Bitu/Notice
Last in, First out맨 끝 위치에서만 모든 연산이 이루어짐O(1). top이라고 하며 삽입은 push, 삭제는 pop
🚩이때 주의할 점은, pop하기 전에 empty체크하기, push하기 전에 full체크하기!
안그럼 index오류가 발생할것이다.
#include <iostream>
# include <vector>
using namespace std;
const int SIZE = 10000;
int top_pointer = -1; // 스택의 맨 윗 상단을 가리킬 포인터
vector<int> stack_vec(SIZE);
//empty
bool empty() {
return top_pointer == -1;
}
//full
bool full() {
return top_pointer == SIZE - 1; //top포인터가 max로 가리킬수있는 인덱스는 size-1.
}
//push
void push(int k) {
stack_vec[++top_pointer]=k;
}
//pop
int pop() {
//pop 할때 벡터에 있는 값을 실질적으로 삭제시키지 않아도됨.
//어짜피 다음번 push할때 그 인덱스가 사용자가 입력한 값으로 바꿔치기 되기 때문.
return stack_vec[top_pointer--];
}
//size
int size() {
return top_pointer + 1;
}
//top
int top() {
return stack_vec[top_pointer];
}
int main() {
cin.tie(NULL);
ios_base:: sync_with_stdio(false);
int n, k;
string cmd;
cin >> n;
while (n--) {
cin >> cmd;
if (cmd == "push") {
cin >> k;
// if (!full())
// push(k);
push(k);
continue;
}
if (cmd == "pop") {
if (empty()) //스택이 비었다면
cout << -1 << '\n';
else {
cout << pop() << '\n';
}
continue;
}
if (cmd == "size") {
cout<< size() << '\n';
continue;
}
if (cmd == "empty") {
cout << empty() << '\n';
continue;
}
if (cmd == "top") {
if (empty())
cout << -1 << '\n';
else
cout << top() << '\n';
continue;
}
}
//스택 순회
while (!empty()) {
cout << top() << ' ';
pop();
}
}
: c++에서는 **stack 라이브러리**를 제공하므로 그것을 이용한다면 이 모든 것을 일일히 구현할 필요가 없다!
empty() : 스택이 비어 있으면 true를, 비어 있지 않으면 false를 반환
size() : 스택에 원소가 몇개 있는지
top() : 스택의 가장 상단에 위치한 값 가져오기.
push(k) : k를 스택 최상단에 삽입
pop() : 스택에서 가장 위에 있는 값을 빼고 가져오기.
First In, First Out)O(1)front, 삽입이 이루어지는 위치를 rear라고 부름enqueue, 삭제는 dequeue
선형큐는 데이터들을 앞으로 당겨주는 과정이 필요하다.
그리고 이러한 선형 큐의 문제점을 보완하기 위한 자료구조로서 원형큐를 이용한다!

Front = Rear인채로 시작하여 큐에 값을 넣을때 rear의 포인터를 1증가 시키고 그 위치에 데이터 삽입이 이루어진다. 큐가 비어있게 된다면 rear와 front는 같은 위치를 가리킨다.

값을 삭제할때에는 front의 포인터를 1증가 시키고 그 위치의 데이터를 배열에서 삭제시켜 가지고 온다. 이때 배열의 포화상태 여부를 판단하기 위하여 배열의 1칸은 항상 비워둔다.
❓ 원형큐에서 Full의 의미?
그리고 맨 마지막 그림이 원형큐의 포화상태를 나타낸 그림인데 ' 한칸이 비었는데 왜 Full상태이지?' 하고 의아해할 수 있다. 잘 생각해보면 앞에서 Front == Rear 인 상태를 공백상태로 보기로 했으므로, 한칸을 비우지 않고 큐를 꽉채운다면 큐가 공백상태인지, 포화상태인지 구별할 수 없다. 따라서 Front뒤에 바로 REAR가 올때, 즉 (Rear +1)%SIZE == Front 라는 수식이 성립할때를 원형큐가 Full상태인것으로 해야 두 상태를 구분지을수 있다.
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int SIZE = 10001;
int front_pointer = 0, rear_pointer = 0;
vector<int> queue_vec(SIZE);
//empty
bool empty() {
return front_pointer == rear_pointer; // front rear가 같은곳을 가리킬때 큐가 비어있다.
}
//full
bool full() {
return (rear_pointer + 1) % SIZE == front_pointer;
}
//push
void push(int k) {
rear_pointer = (rear_pointer + 1) % SIZE; //rear포인터를 먼저 하나 증가시키고
queue_vec[rear_pointer] = k; //그위치에 k를 삽입
}
//pop
int pop() {
front_pointer = (front_pointer + 1) % SIZE; //front포인터를 먼저 하나 증가시키고
return queue_vec[front_pointer]; // 그위치에 있는 값 반환.
//이때 어짜피 비어있는 상태로 치기때문에 값을 굳이 삭제해줄필요 X
}
//size
int size() { //rear = 1, front = 2
int tmp = (rear_pointer - front_pointer);
//front가 더 큰값이 나올수가 있나?
//-> front가 rear포인터를 추월할 순 없지만, 한바퀴를 다돌면 index가 0으로 초기화되므로 단순히 front>rear 가 되는 경우의 수는 가능함.
if (tmp < 0)
tmp += SIZE;
return tmp;
}
//front
int front() { //가장 나중에 들어온 값.
int tmp = (front_pointer + 1) % SIZE; //단순히 맨 앞 값을 가져오기만 하는것이므로 front를 증가시킬 필요 X
return queue_vec[tmp];
}
//back
int back() { //가장 최근에 들어온 값.
return queue_vec[rear_pointer]; //rear포인터가 가리키는 값이 가장 최근에 들어온값.
}
int main() {
cin.tie(NULL);
ios_base::sync_with_stdio(false);
int n, k;
string cmd;
cin >> n;
while (n--) {
cin >> cmd;
if (cmd == "push") {
cin >> k;
if (!full()) //라이브러리 사용시 필요 없음
push(k);
continue;
}
if (cmd == "pop") {
if (empty())
cout << -1 << '\n';
else {
cout << pop() << '\n';
}
continue;
}
if (cmd == "size") {
cout << size() << '\n';
continue;
}
if (cmd == "empty") {
cout << empty() << '\n';
continue;
}
if (cmd == "front") {
if (empty())
cout << -1 << '\n';
else
{
cout << front() << '\n';
}
continue;
}
if (cmd == "back") {
if (empty())
cout << -1 << '\n';
else
cout << back() << '\n';
continue;
}
}
/*
* 큐 순회
*
while (!empty()) {
cout << front() << ' ';
pop();
}
*/
}
: c++에서는 Queue 라이브러리 또한 제공하므로 우리는 이미 만들어진것을 활용하기만 하면 된다!
empty() : 큐가 비어 있으면 true를, 비어 있지 않으면 false를 반환
size() : 큐요소의 총 개수를 반환
front() : 맨 앞 (가장 처음으로 들어온) 원소 리턴
back() : 맨 뒤 (가장 최근에 들어온) 원소 리턴
push(k) : k를 큐에 맨뒤에 삽입
pop() : 큐에서 가장 앞에 있는 요소를 빼고 출력
emplace() : 값을 만드는 생성자 인수를 전달해주면 그 인수들로 새로 원소가 들어갈 장소에 바로 원소를 생성
-> 즉, 들어갈 값(또는 struct)을 생성자로 생성한 다음에 그것을 복사해 새로 컨테이너에 넣는 메모리 낭비를 막음.
stack + queue양끝에서 연산이 이루어짐O(1): queue와 동일한 양상을 띠지만 기능이 두개(pop_back,push_front) 추가됨!
#include <iostream>
#include <vector>
using namespace std;
const int SIZE = 10001;
int front_pointer = 0, rear_pointer = 0;
vector<int> queue_vec(SIZE);
//empty
bool empty() {
return front_pointer == rear_pointer; // front rear가 같은곳을 가리킬때 큐가 비어있다.
}
//full
bool full() {
return (rear_pointer + 1) % SIZE == front_pointer;
}
//push
void push_back(int k) {
rear_pointer = (rear_pointer + 1) % SIZE; //rear포인터를 먼저 하나 증가시키고
queue_vec[rear_pointer] = k; //그위치에 k를 삽입
}
//pop
int pop_front() { // 큐에서의 default pop
front_pointer = (front_pointer + 1) % SIZE; //front포인터를 먼저 하나 증가시키고
return queue_vec[front_pointer]; // 그위치에 있는 값 반환.
//이때 어짜피 비어있는 상태로 치기때문에 값을 굳이 삭제해줄필요 X
}
//size
int size() {
int tmp = (rear_pointer - front_pointer);
if (tmp < 0)
tmp += SIZE;
return tmp;
}
//front
int front() { //가장 나중에 들어온 값.
int tmp = (front_pointer + 1) % SIZE; //단순히 맨 앞 값을 가져오기만 하는것이므로 front를 증가시킬 필요 X
return queue_vec[tmp];
}
//back
int back() { //가장 최근에 들어온 값.
return queue_vec[rear_pointer]; //rear포인터가 가리키는 값이 가장 최근에 들어온값.
}
//덱에 추가되는 기능 두가지
//1. 가장 뒤쪽에서 빼기 -> rear 출력 및 rear --
int pop_back() {
int temp = rear_pointer--;
// index 에러 방지.
if (rear_pointer < 0)
{
rear_pointer += SIZE;
}
return queue_vec[temp];
}
//2. 가장 앞쪽에 수 삽입하기 -> front출력 및 front --
void push_front(int k) {
int temp = front_pointer--;
// index 에러 방지.
if (front_pointer < 0)
{
front_pointer += SIZE;
}
queue_vec[temp] = k;
}
int main() {
cin.tie(NULL);
ios_base::sync_with_stdio(false);
int n, k;
string cmd;
cin >> n;
while (n--) {
cin >> cmd;
if (cmd == "push_front") {
cin >> k;
if (!full())
push_front(k);
continue;
}
if (cmd == "push_back") {
cin >> k;
if (!full()) //라이브러리 사용시 필요 없음
push_back(k);
continue;
}
if (cmd == "pop_front") {
if (empty())
cout << -1 << '\n';
else {
cout << pop_front() << '\n';
}
continue;
}
if (cmd == "pop_back") {
if (empty())
cout << -1 << '\n';
else
cout << pop_back() << '\n';
continue;
}
if (cmd == "size") {
cout << size() << '\n';
continue;
}
if (cmd == "empty") {
cout << empty() << '\n';
continue;
}
if (cmd == "front") {
if (empty())
cout << -1 << '\n';
else
{
cout << front() << '\n';
}
continue;
}
if (cmd == "back") {
if (empty())
cout << -1 << '\n';
else
{
cout << back() << '\n';
}
continue;
}
}
/*
* 덱 순회
*
while (!empty()) {
cout << front() << ' ';
pop();
}
*/
}