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 도 참 간단하다. 완성된 코드는 여기 에 있다.


참고한 자료

Comments

Yaboong's Picture

Yaboong

오스카 쉰들러는 흔해빠진 기회주의자요 부패한 사업가였다. 그러나 거대한 악이 세상을 점령하는 것처럼 보일 때 그 악에 대항해서 사람의 생명을 구한 것은 귀족도 지식인도 종교인도 아닌 부패한 기회주의자 오스카 쉰들러였다.

Seoul, South Korea https://github.com/yaboong