LinkedList Queue - Java

LinkedList Queue - Java

개요

Queue

  • FIFO (First In First Out)
  • head, tail 두 개의 포인터 사용
queue
Queue

Queue 는 stack 과 구조적으로 다른 점은 먼저 넣은 데이터가 먼저 나온다는 것이다. FIFO (First In First Out). Stack 은 head 포인터 하나만을 사용했다. head 만 사용해도 삽입/삭제 작업 모두 O(1) 의 시간복잡도를 갖는 것이 가능했다. head 에 추가하고 head 를 제거하기 때문이다.

하지만 linked list 로 구현한 queue 에서는 하나의 포인터만 사용하면 insert, delete 중 하나는 반드시 O(n) 의 시간복잡도를 갖게 된다. 어느 한쪽으로 데이터가 들어가서 반대쪽으로 데이터가 나와야 하는데, 한쪽의 포인터로 반대쪽 끝까지 이동하는 것이 필요하기 때문이다.

그래서 linked list 로 queue 를 구현할 때는 tail 에 추가하고 head 에 있는 것을 꺼낸다. 추가하는 작업을 enqueue, 꺼내는 작업을 dequeue 라고 한다.


Linked List Queue 구현 - Java

Queue 의 기본 메서드인 enqueue(), dequeue() 만 구현해 보자. 완성된 코드는 여기 에 있다.

LinkedListQueue, Node class 생성
public class LinkedListQueue<E extends Comparable<E>> {
    private Node head, tail;

    private class Node{
        E item;
        Node next;
    }
}

<E extends Comparable<E>> 이는 Java 1.5 부터 지원되는 Generics 라는 것인데, 간단히 설명하면 Comparable 인터페이스를 구현한 클래스들을 타입으로 사용하겠다는 것이다. Comparable 인터페이스는 compareTo 라는 메서드 하나만을 가지는 단순한 인터페이스인데, Comparable 인터페이스를 를 구현한 클래스는 compareTo 라는 메서드도 override 해서 구현해야 하고, 모두 구현 하고나면 객체간에 비교가 가능한 클래스가 된다. 대략적인 사용 예제는 algospot 에서 이 문제를 풀 때 이런방식 으로 PriorityQueue 를 구현할 때 사용했었다. 일단은 Queue 구현과는 크게 상관 없으니 이 포스팅에서는 넘어가도록 한다.

enqueue() 구현
  • 데이터의 추가는 tail 에 한다 (들어온 순서대로 줄을 세우는 셈)
  • 기존의 tail 을 잠시 보관해두고 새로운 tail 을 생성한다
  • queue 가 비어있으면 head = tail 로 head 와 tail 이 같은 node 를 가리키게 한다
  • queue 가 비어있지 않으면 기존 tail 의 next = 새로운 tail 로 해주면 된다
// 데이터의 추가는 tail 에 한다 (들어온 순서대로 줄을 세우는 셈)
public void enqueue(E item){
    Node oldlast = tail;        // 기존의 tail 을 잠시 보관해두고
    tail = new Node();          // 새로운 tail 을 생성한다
    tail.item = item;
    tail.next = null;
    if(isEmpty()) head = tail;  // queue 가 비어있으면 head = tail 로 head 와 tail 이 같은 node 를 가리키게 한다
    else oldlast.next = tail;   // queue 가 비어있지 않으면 기존 tail 의 next = 새로운 tail 로 해주면 된다
}
dequeue() 구현
  • 데이터 꺼내는 작업은 head 에서 한다 (먼저 들어왔던 데이터부터 꺼낸다)
  • head 의 데이터를 어딘가 에 저장
  • 기존 head 다음 node (혹은 null) 를 head 로 설정해준다
  • 어딘가 를 반환
// 데이터 꺼내는 작업은 head 에서 한다 (먼저 들어왔던 데이터부터 꺼낸다)
public E dequeue(){
    // 비어있는 경우
    if(isEmpty()){
        tail = head;
        System.out.println("Queue is empty");
        return null;
    }
    // 비어있지 않으면
    else{
        E item = head.item;     // head 의 데이터를 저장
        head = head.next;       // 기존 head 다음 node (혹은 null) 를 head 로 설정해준다 
        return item;
    }
}

삽입은 tail 포인터로, 제거는 head 포인터로 함으로써 삽입, 삭제 작업 모두 O(1) 로 가능하다. 하고 보니 queue 도 참 간단하다. 완성된 코드는 여기 에 있다.


참고한 자료

Yaboong's Picture

Yaboong

Oskar Schindler was a mere opportunist and a corrupt businessman. Yet, when it seemed that great evil was taking over the world, it was not nobles, intellectuals, or religious leaders who rose to defy it and save lives—it was a corrupt opportunist, Oskar Schindler.

Massachusetts, US linkedin github