IT 프로그래밍/자료구조

자료구조 - 예상문제 3 스택, 큐, 직접 구현하기

기술1 2024. 12. 15. 10:28

1. 스택(Stack) 구현

기본적인 스택

#include <iostream>
using namespace std;

class Stack {
private:
    int* arr;
    int top;
    int capacity;

public:
    Stack(int size) {
        capacity = size;
        arr = new int[capacity];
        top = -1;
    }

    ~Stack() {
        delete[] arr;
    }

    bool isEmpty() {
        return top == -1;
    }

    bool isFull() {
        return top == capacity - 1;
    }

    void push(int value) {
        if (isFull()) {
            cout << "Stack Overflow!" << endl;
            return;
        }
        arr[++top] = value;
    }

    int pop() {
        if (isEmpty()) {
            cout << "Stack Underflow!" << endl;
            return -1;
        }
        return arr[top--];
    }

    int peek() {
        if (isEmpty()) {
            cout << "Stack is empty!" << endl;
            return -1;
        }
        return arr[top];
    }
};

int main() {
    Stack stack(5);

    stack.push(10);
    stack.push(20);
    stack.push(30);

    cout << "Top element: " << stack.peek() << endl;
    cout << "Popped element: " << stack.pop() << endl;
    cout << "Top element after pop: " << stack.peek() << endl;

    return 0;
}

 

배열을 이용한 동적 크기 조정 가능한 스택 (크기 변화가 가능하도록 구현)

#include <iostream>
using namespace std;

class DynamicStack {
private:
    int* arr;
    int top;
    int capacity;

public:
    DynamicStack(int size = 2) { // 기본 크기 2
        capacity = size;
        arr = new int[capacity];
        top = -1;
    }

    ~DynamicStack() {
        delete[] arr;
    }

    bool isEmpty() {
        return top == -1;
    }

    bool isFull() {
        return top == capacity - 1;
    }

    void push(int value) {
        if (isFull()) {
            // 배열 크기 두 배로 확장
            int* newArr = new int[capacity * 2];
            for (int i = 0; i < capacity; i++) {
                newArr[i] = arr[i];
            }
            delete[] arr;
            arr = newArr;
            capacity *= 2;
        }
        arr[++top] = value;
    }

    int pop() {
        if (isEmpty()) {
            cout << "Stack Underflow!" << endl;
            return -1;
        }
        return arr[top--];
    }

    int peek() {
        if (isEmpty()) {
            cout << "Stack is empty!" << endl;
            return -1;
        }
        return arr[top];
    }
};

int main() {
    DynamicStack stack;

    stack.push(10);
    stack.push(20);
    stack.push(30);
    stack.push(40);  // 크기 증가

    cout << "Top element: " << stack.peek() << endl;
    cout << "Popped element: " << stack.pop() << endl;
    cout << "Top element after pop: " << stack.peek() << endl;

    return 0;
}

 

2. 큐(Queue) 구현

기본적인 큐

#include <iostream>
using namespace std;

class Queue {
private:
    int* arr;
    int front;
    int rear;
    int capacity;

public:
    Queue(int size) {
        capacity = size;
        arr = new int[capacity];
        front = -1;
        rear = -1;
    }

    ~Queue() {
        delete[] arr;
    }

    bool isEmpty() {
        return front == -1;
    }

    bool isFull() {
        return (rear + 1) % capacity == front;
    }

    void enqueue(int value) {
        if (isFull()) {
            cout << "Queue Overflow!" << endl;
            return;
        }
        if (front == -1) front = 0;
        rear = (rear + 1) % capacity;
        arr[rear] = value;
    }

    int dequeue() {
        if (isEmpty()) {
            cout << "Queue Underflow!" << endl;
            return -1;
        }
        int dequeued = arr[front];
        if (front == rear) {
            front = rear = -1; // 큐가 비었을 경우
        } else {
            front = (front + 1) % capacity;
        }
        return dequeued;
    }

    int peek() {
        if (isEmpty()) {
            cout << "Queue is empty!" << endl;
            return -1;
        }
        return arr[front];
    }
};

int main() {
    Queue q(5);

    q.enqueue(10);
    q.enqueue(20);
    q.enqueue(30);

    cout << "Front element: " << q.peek() << endl;
    cout << "Dequeued element: " << q.dequeue() << endl;
    cout << "Front element after dequeue: " << q.peek() << endl;

    return 0;
}

 

동적 크기 조정 큐

.

#include <iostream>
using namespace std;

class DynamicQueue {
private:
    int* arr;
    int front;
    int rear;
    int capacity;

public:
    DynamicQueue(int size = 2) { // 기본 크기 2
        capacity = size;
        arr = new int[capacity];
        front = -1;
        rear = -1;
    }

    ~DynamicQueue() {
        delete[] arr;
    }

    bool isEmpty() {
        return front == -1;
    }

    bool isFull() {
        return (rear + 1) % capacity == front;
    }

    void enqueue(int value) {
        if (isFull()) {
            // 배열 크기 두 배로 확장
            int* newArr = new int[capacity * 2];
            int j = 0;
            for (int i = front; i <= rear; i++) {
                newArr[j++] = arr[i % capacity];
            }
            delete[] arr;
            arr = newArr;
            capacity *= 2;
            front = 0;
            rear = j - 1;
        }
        if (front == -1) front = 0;
        rear = (rear + 1) % capacity;
        arr[rear] = value;
    }

    int dequeue() {
        if (isEmpty()) {
            cout << "Queue Underflow!" << endl;
            return -1;
        }
        int dequeued = arr[front];
        if (front == rear) {
            front = rear = -1; // 큐가 비었을 경우
        } else {
            front = (front + 1) % capacity;
        }
        return dequeued;
    }

    int peek() {
        if (isEmpty()) {
            cout << "Queue is empty!" << endl;
            return -1;
        }
        return arr[front];
    }
};

int main() {
    DynamicQueue q;

    q.enqueue(10);
    q.enqueue(20);
    q.enqueue(30);
    q.enqueue(40);  // 크기 증가

    cout << "Front element: " << q.peek() << endl;
    cout << "Dequeued element: " << q.dequeue() << endl;
    cout << "Front element after dequeue: " << q.peek() << endl;

    return 0;
}

(2) 최소값 스택(Min Stack)

이 스택은 추가적으로 현재 스택에서의 최소값을 추적하는 기능을 가집니다.

#include <iostream>
#include <stack>
using namespace std;

class MinStack {
private:
    stack<int> mainStack;
    stack<int> minStack;

public:
    void push(int value) {
        mainStack.push(value);
        if (minStack.empty() || value <= minStack.top()) {
            minStack.push(value);
        }
    }

    int pop() {
        if (mainStack.empty()) {
            cout << "Stack Underflow!" << endl;
            return -1;
        }
        int poppedValue = mainStack.top();
        if (poppedValue == minStack.top()) {
            minStack.pop();
        }
        mainStack.pop();
        return poppedValue;
    }

    int peek() {
        if (mainStack.empty()) {
            cout << "Stack is empty!" << endl;
            return -1;
        }
        return mainStack.top();
    }

    int getMin() {
        if (minStack.empty()) {
            cout << "Stack is empty!" << endl;
            return -1;
        }
        return minStack.top();
    }
};

int main() {
    MinStack stack;
    stack.push(10);
    stack.push(20);
    stack.push(5);
    cout << "Min element: " << stack.getMin() << endl;
    stack.pop();
    cout << "Min element after pop: " << stack.getMin() << endl;

    return 0;
}

 

(2) 우선순위 큐(Priority Queue)

우선순위 큐는 일반적인 큐와 다르게 데이터를 넣을 때 우선순위가 높은 데이터가 먼저 나오는 큐입니다. 우선순위 큐는 일반적으로 heap을 사용하여 구현됩니다.

여기서는 최대 우선순위 큐를 직접 구현한 예시입니다.

#include <iostream>
#include <vector>
using namespace std;

class PriorityQueue {
private:
    vector<int> heap;

    void heapifyUp(int index) {
        while (index > 0 && heap[index] > heap[(index - 1) / 2]) {
            swap(heap[index], heap[(index - 1) / 2]);
            index = (index - 1) / 2;
        }
    }

    void heapifyDown(int index) {
        int leftChild = 2 * index + 1;
        int rightChild = 2 * index + 2;
        int largest = index;

        if (leftChild < heap.size() && heap[leftChild] > heap[largest]) {
            largest = leftChild;
        }

        if (rightChild < heap.size() && heap[rightChild] > heap[largest]) {
            largest = rightChild;
        }

        if (largest != index) {
            swap(heap[index], heap[largest]);
            heapifyDown(largest);
        }
    }

public:
    void enqueue(int value) {
        heap.push_back(value);
        heapifyUp(heap.size() - 1);
    }

    int dequeue() {
        if (heap.empty()) {
            cout << "Queue Underflow!" << endl;
            return -1;
        }
        int root = heap[0];
        heap[0] = heap[heap.size() - 1];
        heap.pop_back();
        heapifyDown(0);
        return root;
    }

    int peek() {
        if (heap.empty()) {
            cout << "Queue is empty!" << endl;
            return -1;
        }
        return heap[0];
    }
};

int main() {
    PriorityQueue pq;
    pq.enqueue(10);
    pq.enqueue(20);
    pq.enqueue(5);
    cout << "Max element: " << pq.peek() << endl;
    pq.dequeue();
    cout << "Max element after dequeue: " << pq.peek() << endl;

    return 0;
}

 

(3) 원형 큐(Circular Queue)

원형 큐는 큐에서 데이터를 넣고 뺄 때 배열의 끝에서 처음으로 돌아가서 공간을 재활용하는 큐입니다

#include <iostream>
using namespace std;

class CircularQueue {
private:
    int* arr;
    int front;
    int rear;
    int capacity;

public:
    CircularQueue(int size) {
        capacity = size;
        arr = new int[capacity];
        front = -1;
        rear = -1;
    }

    ~CircularQueue() {
        delete[] arr;
    }

    bool isEmpty() {
        return front == -1;
    }

    bool isFull() {
        return (rear + 1) % capacity == front;
    }

    void enqueue(int value) {
        if (isFull()) {
            cout << "Queue Overflow!" << endl;
            return;
        }
        if (front == -1) front = 0;
        rear = (rear + 1) % capacity;
        arr[rear] = value;
    }

    int dequeue() {
        if (isEmpty()) {
            cout << "Queue Underflow!" << endl;
            return -1;
        }
        int dequeued = arr[front];
        if (front == rear) {
            front = rear = -1; // 큐가 비었을 경우
        } else {
            front = (front + 1) % capacity;
        }
        return dequeued;
    }

    int peek() {
        if (isEmpty()) {
            cout << "Queue is empty!" << endl;
            return -1;
        }
        return arr[front];
    }
};

int main() {
    CircularQueue q(5);

    q.enqueue(10);
    q.enqueue(20);
    q.enqueue(30);
    q.enqueue(40);

    cout << "Front element: " << q.peek() << endl;
    q.dequeue();
    cout << "Front element after dequeue: " << q.peek() << endl;

    q.enqueue(50);
    cout << "Front element after enqueue: " << q.peek() << endl;

    return 0;
}

 

1. 덱(Deque)

덱(Deque)은 "Double-Ended Queue"의 줄임말로, 양쪽 끝에서 삽입과 삭제가 가능한 큐입니다. 덱은 스택처럼 양쪽 끝에서 데이터를 삽입하거나 삭제할 수 있습니다.

#include <iostream>
using namespace std;

class Deque {
private:
    int* arr;
    int front, rear;
    int capacity;

public:
    Deque(int size) {
        capacity = size;
        arr = new int[capacity];
        front = -1;
        rear = 0;
    }

    ~Deque() {
        delete[] arr;
    }

    bool isFull() {
        return (front == 0 && rear == capacity - 1) || (front == rear + 1);
    }

    bool isEmpty() {
        return front == -1;
    }

    void insertFront(int value) {
        if (isFull()) {
            cout << "Deque Overflow!" << endl;
            return;
        }
        if (front == -1) {
            front = 0;
            rear = 0;
        } else if (front == 0) {
            front = capacity - 1;
        } else {
            front--;
        }
        arr[front] = value;
    }

    void insertRear(int value) {
        if (isFull()) {
            cout << "Deque Overflow!" << endl;
            return;
        }
        if (front == -1) {
            front = 0;
            rear = 0;
        } else if (rear == capacity - 1) {
            rear = 0;
        } else {
            rear++;
        }
        arr[rear] = value;
    }

    int deleteFront() {
        if (isEmpty()) {
            cout << "Deque Underflow!" << endl;
            return -1;
        }
        int value = arr[front];
        if (front == rear) {
            front = rear = -1;
        } else if (front == capacity - 1) {
            front = 0;
        } else {
            front++;
        }
        return value;
    }

    int deleteRear() {
        if (isEmpty()) {
            cout << "Deque Underflow!" << endl;
            return -1;
        }
        int value = arr[rear];
        if (front == rear) {
            front = rear = -1;
        } else if (rear == 0) {
            rear = capacity - 1;
        } else {
            rear--;
        }
        return value;
    }

    int getFront() {
        if (isEmpty()) {
            cout << "Deque is empty!" << endl;
            return -1;
        }
        return arr[front];
    }

    int getRear() {
        if (isEmpty()) {
            cout << "Deque is empty!" << endl;
            return -1;
        }
        return arr[rear];
    }
};

int main() {
    Deque dq(5);

    dq.insertRear(10);
    dq.insertRear(20);
    dq.insertFront(5);
    dq.insertFront(2);

    cout << "Front element: " << dq.getFront() << endl;
    cout << "Rear element: " << dq.getRear() << endl;

    dq.deleteRear();
    cout << "Rear element after delete: " << dq.getRear() << endl;

    dq.deleteFront();
    cout << "Front element after delete: " << dq.getFront() << endl;

    return 0;
}

 

2. 큐를 이용한 스택 (Queue to Stack)

이 구현은 두 개의 큐를 사용하여 스택을 구현하는 방식입니다. enqueue와 dequeue를 적절히 사용하여 큐를 이용한 스택 동작을 만듭니다.

#include <iostream>
#include <queue>
using namespace std;

class StackUsingQueue {
private:
    queue<int> q1, q2;

public:
    void push(int value) {
        q2.push(value);

        while (!q1.empty()) {
            q2.push(q1.front());
            q1.pop();
        }

        // 두 큐를 교환하여 q1을 스택처럼 사용
        swap(q1, q2);
    }

    int pop() {
        if (q1.empty()) {
            cout << "Stack Underflow!" << endl;
            return -1;
        }
        int top = q1.front();
        q1.pop();
        return top;
    }

    int peek() {
        if (q1.empty()) {
            cout << "Stack is empty!" << endl;
            return -1;
        }
        return q1.front();
    }
};

int main() {
    StackUsingQueue stack;

    stack.push(10);
    stack.push(20);
    stack.push(30);
    cout << "Top element: " << stack.peek() << endl;
    stack.pop();
    cout << "Top element after pop: " << stack.peek() << endl;

    return 0;
}

 

3. 스택을 이용한 큐 (Stack to Queue)

스택을 이용하여 큐를 구현하는 방식입니다. 두 개의 스택을 사용하여 enqueue와 dequeue 작업을 처리합니다.

#include <iostream>
#include <stack>
using namespace std;

class QueueUsingStack {
private:
    stack<int> s1, s2;

public:
    void enqueue(int value) {
        s1.push(value);
    }

    int dequeue() {
        if (s1.empty() && s2.empty()) {
            cout << "Queue Underflow!" << endl;
            return -1;
        }

        if (s2.empty()) {
            while (!s1.empty()) {
                s2.push(s1.top());
                s1.pop();
            }
        }

        int front = s2.top();
        s2.pop();
        return front;
    }

    int peek() {
        if (s1.empty() && s2.empty()) {
            cout << "Queue is empty!" << endl;
            return -1;
        }

        if (s2.empty()) {
            while (!s1.empty()) {
                s2.push(s1.top());
                s1.pop();
            }
        }

        return s2.top();
    }
};

int main() {
    QueueUsingStack q;

    q.enqueue(10);
    q.enqueue(20);
    q.enqueue(30);
    cout << "Front element: " << q.peek() << endl;
    q.dequeue();
    cout << "Front element after dequeue: " << q.peek() << endl;

    return 0;
}

 

4. 이중 큐 (Double Queue)

이중 큐는 두 개의 큐를 동시에 사용하여 데이터를 양방향으로 처리할 수 있는 구조입니다.

#include <iostream>
#include <queue>
using namespace std;

class DoubleQueue {
private:
    queue<int> q1, q2;

public:
    void insertFront(int value) {
        q2.push(value);
    }

    void insertRear(int value) {
        q1.push(value);
    }

    int deleteFront() {
        if (q2.empty() && q1.empty()) {
            cout << "Deque Underflow!" << endl;
            return -1;
        }
        if (!q2.empty()) {
            int front = q2.front();
            q2.pop();
            return front;
        }
        int front = q1.front();
        q1.pop();
        return front;
    }

    int deleteRear() {
        if (q2.empty() && q1.empty()) {
            cout << "Deque Underflow!" << endl;
            return -1;
        }
        if (!q1.empty()) {
            int rear = q1.back();
            q1.pop();
            return rear;
        }
        int rear = q2.back();
        q2.pop();
        return rear;
    }

    int peekFront() {
        if (q2.empty() && q1.empty()) {
            cout << "Deque is empty!" << endl;
            return -1;
        }
        if (!q2.empty()) {
            return q2.front();
        }
        return q1.front();
    }

    int peekRear() {
        if (q2.empty() && q1.empty()) {
            cout << "Deque is empty!" << endl;
            return -1;
        }
        if (!q1.empty()) {
            return q1.back();
        }
        return q2.back();
    }
};

int main() {
    DoubleQueue dq;

    dq.insertFront(10);
    dq.insertRear(20);
    dq.insertFront(5);
    dq.insertRear(30);

    cout << "Front element: " << dq.peekFront() << endl;
    cout << "Rear element: " << dq.peekRear() << endl;

    dq.deleteFront();
    cout << "Front element after delete: " << dq.peekFront() << endl;

    dq.deleteRear();
    cout << "Rear element after delete: " << dq.peekRear() << endl;

    return 0;
}