스택은 LIFO(Last-In First-Out), 즉 후입선출의 자료구조이다. 구조는 사진과 같은 형태이며, 아래부터 쌓여서 나중에 삽입된 데이터가 먼저 빠져나가도록 되어있다.
아래 사진을 보면 top 변수는 가장 최근에 들어온 요소를 가리키며 스택이 비어있으면 -1이다.
스택의 특징인 후입선출(LIFO)을 활용하여 여러 분야에서 활용 가능하다.
삽입
삽입 연산(push) 시 top에 요소 하나를 추가한 후, top을 한 칸 위로 올려준다.
삭제
삭제 연산(pop) 시 top에서 요소 하나를 뺀 후, top을 한 칸 아래로 내려준다.
접근
접근 연산은 없다. 다만 top의 요소를 꺼내지는 않고 확인만 하는 용도의 연산(peek)은 있다.
스택은 배열 또는 연결리스트로 구현할 수 있다. 연결리스트로 구현 시 스택의 크기 제한이 없다고 볼 수 있으며, 기타 장단점은 '연결리스트' 파트에서 설명한 바와 같다.
C++에서는
import <stack>;
로 C++ STL 라이브러리를 불러온 후,stack<int> stk;
과 같이stack<자료형>
스택이름으로 변수를 생성한다.
#include <iostream>
#include <string>
using namespace std;
class Stack {
private:
int top;
int arr[5];
public:
Stack() {
top = -1; // 비어있음
for (int i = 0; i < 5; i++) {
arr[i] = 0;
}
}
bool isEmpty() {
if (top == -1) {
return true;
}
else {
return false;
}
}
bool isFull() {
if (top == 4)
return true;
else
return false;
}
void push(int val) {
if (isFull()) {
cout << "stack overflow" << endl;
}
else {
top++;
arr[top] = val;
}
}
int pop() {
if (isEmpty()) {
cout << "stack empty" << endl;
return 0;
}
else {
int popValue = arr[top];
arr[top] = 0;
top--;
return popValue;
}
}
int count() {
return (top + 1);
}
int peek(int pos) {
if (isEmpty()) {
cout << "stack underflow" << endl;
return 0;
}
else {
return arr[pos];
}
}
void change(int pos, int val) {
arr[pos] = val;
cout << "value changed at location" << pos << endl;
}
void display() {
cout << "All values in the Stack are " << endl;
for (int i = 4; i >= 0; i--) {
cout << arr[i] << endl;
}
}
};
int main() {
Stack s1;
int option, postion, value;
do {
cout << "What operation do you want to perform? Select Option number. Enter 0 to exit." << endl;
cout << "1. Push()" << endl;
cout << "2. Pop()" << endl;
cout << "3. isEmpty()" << endl;
cout << "4. isFull()" << endl;
cout << "5. peek()" << endl;
cout << "6. count()" << endl;
cout << "7. change()" << endl;
cout << "8. display()" << endl;
cout << "9. Clear Screen" << endl << endl;
cin >> option;
switch (option) {
case 0:
break;
case 1:
cout << "Enter an item to push in the stack" << endl;
cin >> value;
s1.push(value);
break;
case 2:
cout << "Pop Function Called - Poped Value: " << s1.pop() << endl;
break;
case 3:
if (s1.isEmpty())
cout << "Stack is Empty" << endl;
else
cout << "Stack is not Empty" << endl;
break;
case 4:
if (s1.isFull())
cout << "Stack is Full" << endl;
else
cout << "Stack is not Full" << endl;
break;
case 5:
cout << "Enter position of item you want to peek: " << endl;
cin >> postion;
cout << "Peek Function Called - Value at position " << postion << " is " << s1.peek(postion) << endl;
break;
case 6:
cout << "Count Function Called - Number of Items in the Stack are: " << s1.count() << endl;
break;
case 7:
cout << "Change Function Called - " << endl;
cout << "Enter position of item you want to change : ";
cin >> postion;
cout << endl;
cout << "Enter value of item you want to change : ";
cin >> value;
s1.change(postion, value);
break;
case 8:
cout << "Display Function Called - " << endl;
s1.display();
break;
case 9:
system("cls");
break;
default:
cout << "Enter Proper Option number " << endl;
}
} while (option != 0);
return 0;
}
큐는 FIFO(First-In First-Out), 즉 선입선출의 자료구조이다. 구조는 아래 사진과 같으며, 대기열처럼 밀려서 먼저 삽입된 데이터가 먼저 빠져나가도록 되어있다.
삽입은 back에서 일어나며, 삭제는 front에서 발생한다.
큐는 주로 데이터가 입력된 시간 순서대로 처리해야 할 필요가 있는 상황에 이용한다.
삽입
삽입 연산(put == Enqueue) 시 back에 요소 하나를 추가한 후, back을 하나 뒤로 옮겨준다.
삭제
삭제 연산(get == Dequeue ) 시 front에서 요소 하나를 삭제한 후, front를 하나 뒤로 옮겨준다.
접근
peek 연산을 이용하여 front에서 요소를 확인할 수 있다.
큐 또한 주로 배열로 구현되고, 연결리스트로도 구현할 수 있긴 하다. 연결리스트로 큐를 구현하였을 때는 굳이 환형 큐가 아니더라도 오버플로우가 발생할 걱정은 없다.
C++에서는
import <queue>;
로 C++ STL 라이브러리를 불러온 후,queue<int> q;
과 같이queue<자료형>
큐이름으로 변수를 생성한다.
#include <iostream>
using namespace std;
class Queue {
private:
int front;
int rear;
int arr[5];
public:
Queue() {
front = -1;
rear = -1;
for (int i = 0; i < 5; i++) {
arr[i] = 0;
}
}
bool isEmpty() {
if (front == -1 && rear == -1)
return true;
else
return false;
}
bool isFull() {
if (rear == 4) {
return true;
}
else {
return false;
}
}
void enqueue(int val) {
if (isFull()) {
cout << "Queue is FULL" << endl;
return;
}
else if (isEmpty()) {
rear = 0;
front = 0;
arr[rear] = val;
}
else {
rear++;
arr[rear] = val;
}
}
int dequeue() {
int x;
if (isEmpty()) {
cout << "Queue is EMPTY" << endl;
return 0;
}
else if (front == rear) {
x = arr[front];
arr[front] = 0;
rear = -1;
front = -1;
return x;
}
else
{
x = arr[front];
arr[front] = 0;
front++;
return x;
}
}
int count() {
return (rear - front + 1);
}
void display() {
cout << "All values in the Queue are - " << endl;
for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
}
};
int main() {
Queue q1;
int value, option;
do {
cout << "\n\nWhat operation do you want to perform? Select Option number. Enter 0 to exit." << endl;
cout << "1. Enqueue()" << endl;
cout << "2. Dequeue()" << endl;
cout << "3. isEmpty()" << endl;
cout << "4. isFull()" << endl;
cout << "5. count()" << endl;
cout << "6. display()" << endl;
cout << "7. Clear Screen" << endl << endl;
cin >> option;
switch (option) {
case 0:
break;
case 1:
cout << "Enqueue Operation \nEnter an item to Enqueue in the Queue" << endl;
cin >> value;
q1.enqueue(value);
break;
case 2:
cout << "Dequeue Operation \nDequeued Value : " << q1.dequeue() << endl;
break;
case 3:
if (q1.isEmpty())
cout << "Queue is Empty" << endl;
else
cout << "Queue is not Empty" << endl;
break;
case 4:
if (q1.isFull())
cout << "Queue is Full" << endl;
else
cout << "Queue is not Full" << endl;
break;
case 5:
cout << "Count Operation \nCount of items in Queue : " << q1.count() << endl;
break;
case 6:
cout << "Display Function Called - " << endl;
q1.display();
break;
case 7:
system("cls");
break;
default:
cout << "Enter Proper Option number " << endl;
}
} while (option != 0);
return 0;
}
배열을 이용한 큐는 이미 사용한 영역인 front의 앞부분에 대해서 다시 활용을 못하기 때문에 메모리를 낭비한다는 단점이 있음
또한, 이동 큐를 이용하여 큐가 다 찼을 경우 데이터들을 앞쪽으로 이동시켜 사용하는 방법이 있지만 남아있는 모든 데이터를 다 이동 시켜야 한다는 불편한 작업을 수행해야 하기 때문에 효율적이지 못함
#include <iostream>
using namespace std;
class CircularQueue {
private:
int front;
int rear;
int arr[5];
int itemCount;
public:
CircularQueue() {
itemCount = 0;
front = -1;
rear = -1;
for (int i = 0; i < 5; i++) {
arr[i] = 0;
}
}
bool isEmpty() {
if (front == -1 && rear == -1)
return true;
else
return false;
}
bool isFull() {
if ((rear+1)%5 == front) { // queue와 다른점
return true;
}
else {
return false;
}
}
void enqueue(int val) {
if (isFull()) {
cout << "Queue is FULL" << endl;
return;
}
else if (isEmpty()) {
rear = 0;
front = 0;
arr[rear] = val;
}
else {
rear = (rear+1)%5; // queue와 다른점
arr[rear] = val;
}
itemCount++;
}
int dequeue() {
int x = 0;
if (isEmpty()) {
cout << "Queue is EMPTY" << endl;
return x;
}
else if (front == rear) {
x = arr[front];
arr[front] = 0;
rear = -1;
front = -1;
itemCount--;
return x;
}
else
{
x = arr[front];
arr[front] = 0;
front = (front+1)%5;
itemCount--;
return x;
}
}
int count() {
return itemCount;
}
void display() {
cout << "All values in the Queue are - " << endl;
for (int i = 0; i < 5; i++) {
cout << arr[i] << " ";
}
}
};
int main() {
CircularQueue q1;
int value, option;
do {
cout << "\n\nWhat operation do you want to perform? Select Option number. Enter 0 to exit." << endl;
cout << "1. Enqueue()" << endl;
cout << "2. Dequeue()" << endl;
cout << "3. isEmpty()" << endl;
cout << "4. isFull()" << endl;
cout << "5. count()" << endl;
cout << "6. display()" << endl;
cout << "7. Clear Screen" << endl << endl;
cin >> option;
switch (option) {
case 0:
break;
case 1:
cout << "Enqueue Operation \nEnter an item to Enqueue in the Queue" << endl;
cin >> value;
q1.enqueue(value);
break;
case 2:
cout << "Dequeue Operation \nDequeued Value : " << q1.dequeue() << endl;
break;
case 3:
if (q1.isEmpty())
cout << "Queue is Empty" << endl;
else
cout << "Queue is not Empty" << endl;
break;
case 4:
if (q1.isFull())
cout << "Queue is Full" << endl;
else
cout << "Queue is not Full" << endl;
break;
case 5:
cout << "Count Operation \nCount of items in Queue : " << q1.count() << endl;
break;
case 6:
cout << "Display Function Called - " << endl;
q1.display();
break;
case 7:
system("cls");
break;
default:
cout << "Enter Proper Option number " << endl;
}
} while (option != 0);
return 0;
}
데이터의 우선순위에 따라 우선순위가 높은 데이터 부터 꺼내도록 만들어진 큐
데이터를 삽입할 때, 우선순위에 따라서 정렬하여 삽입
한 후 한쪽 방향에서만 데이터를 꺼내어 쓰도록 작성
스택과 큐도 데이터의 저장 시간에 따라 우선순위를 부여하는 우선순위 큐의 한 종류라고 할 수 있음
스택은 가장 최근에 저장된 데이터의 우선순위가 높고, 큐는 오래지난 데이터의 우선순위가 높음
일반적인 큐는 데이터의 삽입과 삭제가 한쪽 방향에서만 발생하지만 데크는 리스트의 양쪽 끝 모두에서 데이터를 삽입, 삭제 할 수 있도록 만들어진 특별한 형태의 자료구조
양쪽 방향 모두에서 삽입과 삭제가 발생할 수 있으므로 큐나 스택으로 모두 활용할 수 있음. 만약, 한쪽 방향에서 삽입과 삭제를 모두 처리하면, 스택으로 동작하는 것이고 한쪽 방향에서는 삽입만 발생하고 반대쪽 방향에서는 삭제만 발생하도록 처리하면 큐처럼 동작함