[자료구조] 스택, 큐 (feat. java)

zl존석동

·

2022. 1. 12. 20:20

 

스택에 대해 공부해보고

자바로 간단하게 구현해보자

 

 


 

 

 

 

Stack? Queue?

 

 

데이터를 저장할 수 있는 기본적인 구조로 선형 자료구조입니다.

 

선형 자료구조란?

하나의 자료 뒤에 하나의 자료가 있는 데이터가 순차적으로 나열되어 있는 것을 말한다.

 

 

 

 

 

Stack? 

 

 

출처 : 위키백과

 

 

- 한 쪽 끝에서만 데이터를 넣거나 뺄 수 있는 LIFO(Last In First Out) 자료구조입니다.

 

- 데이터를 추가할 때는 밑에서부터 쌓아올리고 뺄 때는 위에 있는 것 부터 뺀다고 생각하면 될 것 같습니다.

 

 


어릴 때 먹던 콘 아이스크림을 생각해봅시다.

 

콘을 손에 들고 색색의 아이스크림들을 조금이라도 더 먹어 보겠다고

 

동그란 모양으로 퍼다가 미친듯이 쌓아올릴 것입니다.

 

이렇게 아이스크림을 쌓아 올리는 과정(데이터의 삽입)을 Push 라고 하며

 

쌓은 아이스크림을 먹는 과정(데이터의 삭제)를 Pop 이라고 합니다. 


 

 

스택 활용

 

- 재귀 알고리즘

 

- 웹 사이트 검색 뒤로가기

 

- 아이스크림 통을 들고 퍼먹는 나의 모습

 

 

 

자바 배열로 구현하기

 

public class Main {
    public static void main(String[] args) {

        MyStack<Integer> myIntStack = new MyStack<>(5);
        myIntStack.push(5);
        myIntStack.peek();
        myIntStack.pop();
    }
}
class MyStack<T> {

    T[] stack;
    int top;
    int size;

    public MyStack(int size) {
        this.size = size;
        stack = (T[]) new Object[size];
        top = -1;
    }

    public void push(T item) {
        if (!isFull()) {
            stack[++top] = item;
        }
    }

    public T pop() {
        if (!isEmpty()) {
            return stack[top--];
        }
        return null;
    }

    public T peek() {
        if (!isEmpty()) {
            return stack[top];
        }
        return null;
    }

    private boolean isFull() {
        return this.top == size - 1;
    }

    public boolean isEmpty() {
        return top == -1;
    }
}

 

배열 방식은 구현도 쉽고 처리속도가 빠르지만 최대 데이터 개수가 한정되어있습니다.

 

스택에 저장하여 처리해야할 데이터 개수가 명확할 때 사용하면 좋지 않을까 생각합니다.

 

 

 

LinkedList 로 구현하기

 

public class Main2 {
    public static void main(String[] args) {
        MyLLStack<Integer> myl = new MyLLStack<>();
        myl.push(5);
        myl.push(4);
        myl.push(3);
        myl.pop();
        System.out.println(myl.peek());
    }
}

class Node<T> {
    T item;
    Node next;

    public Node(T item) {
        this.item = item;
        this.next = null;
    }

    void linkNext(Node node) {
        this.next = node;
    }

    Node getNext() {
        return next;
    }
}

class MyLLStack<T> {
    Node top;

    public MyLLStack() {
        this.top = null;
    }

    public void push(T data) {
        // 입력받은 새로운 데이터에 대한 노드 생성
        Node n = new Node(data);
        //현재 top 위치에 있는 노드에 새로 생성된 노드를 다음 데이터로써 연결함
        n.linkNext(top);
        top = n;
    }

    public void pop() {
        top = top.getNext();
    }

    public T peek() {
        return (T) top.item;
    }
}

 

배열보다 복잡하긴 하지만 스택의 크기가 고정되어있지 않다는 장점을 가집니다.

 

 

물론 자바에서 제공해주는 Stack 클래스나 Deque 인터페이스를 활용하면 훨씬 더 쉽게 사용할 수 있습니다.

 

 

 

 

 

Queue?

 

 

출처: 나무위키

 

먼저 들어온 데이터가 먼저 나가는 FIFO(First In First Out) 의 자료구조입니다.

 

큐의 한쪽 끝인 Front 는 삭제연산만 수행하고

 

다른 한쪽 끝인 Rear 는 삽입연산만 수행하게 됩니다.

 

 

큐 활용

 

- 프로세스 관리

 

- 프린터 출력 대기

 

- 코로나 검사 받으려고 줄 서 있는 나

 

 

자바 배열로 구현하기

 

스택과 구현이 비슷하지만 스택에서 top 이라는 속성만으로 입력 삭제가 모두 가능했던 것과 달리

 

큐에서는 입력과 삭제가 수행되는 위치가 달라 front, rear 두가지 속성이 필요합니다.

 

 

public class QueueExam1 {
    public static void main(String[] args) {
        MyQueue<Integer> mq = new MyQueue<>(5);
        mq.add(5);
        mq.add(4);
        mq.add(3);
        System.out.println(mq.pop());
        System.out.println(mq.peek());
    }
}

class MyQueue<T> {
    private int rear;
    private int front;
    private T[] queue;
    private int size;

    public MyQueue(int size) {
        this.size = size;
        queue = (T[]) new Object[size];
        front = 0;
        rear = 0;
    }

    public void add(T data) {
        if (!isFull()) {
            this.queue[rear++] = data;
        }
    }

    public T pop() {
        if (!isEmpty()) {
            return queue[front++];
        }
        return null;
    }

    public T peek() {
        if (!isEmpty()) {
            return queue[front];
        }
        return null;
    }

    private boolean isFull() {
        return rear == size - 1;
    }

    public boolean isEmpty() {
        if (front == rear) {
            front = 0;
            rear = 0;
        }
        return front == rear;
    }
}

 

역시나 배열로 구현하면 처리할 수 있는 데이터 수에 제한이 있습니다.

 

또한 (front == rear) : 배열이 비었을 때 다시 frontrear 를 다시 초기값으로 설정해줘야 지속적으로 사용할 수 있습니다.

 

 

LinkedList 로 구현하기

 

public class QueueExam2 {
    public static void main(String[] args) {
        MyLLQueue<Integer> ml = new MyLLQueue<>();
        ml.add(5);
        ml.add(4);
        ml.add(3);
        System.out.println(ml.pop());
        System.out.println(ml.peek());
    }
}

class MyLLQueue<T> {
    // 삭제연산만 할 front
    private Node front;
    // 삽입연산만 할 rear
    private Node rear;

    public MyLLQueue() {
        this.front = null;
        this.rear = null;
    }

    public void add(T data) {
        Node n = new Node(data);
        if (isEmpty()) {
            front = n;
            rear = n;
            return;
        }
        rear.linkNext(n);
        rear = n;
    }

    public T peek() {
        return (T) front.item;
    }

    public T pop() {
        Node n = front;
        front = front.getNext();
        return (T) n.item;
    }
    
    public boolean isEmpty() {
        return front == null && rear == null;
    }
}

 

Queue 도 자바에서 제공해주는 Queue<E> 인터페이스를 통해 쉽게 활용할 수 있습니다.

 

 

 

양쪽 끝에서 삽입과 삭제가 모두 가능한 Stack 과 Queue 를 합쳐 놓은 개념인 Deque 도 있습니다.

 

상황에 맞게 어떤 자료구조를 어떤 방법으로 활용할지 잘 판단하는 것이 중요하지 않을까 싶습니다.

 

 

 

 

 

 

https://ko.wikipedia.org/wiki/%EC%8A%A4%ED%83%9D

 

https://devuna.tistory.com/22