1. 개요
큐는 대표적인 자료구조중 하나로, 가장 먼저 들어간 데이터가 가장 먼저 나오는 선입선출, FIFO
(First In First Out) 형태의 자료구조다.
활용방법으로는
- 프린터 출력 (가장 먼저 대기열에 오른 프린트를 먼저 실행)
- 대기열, 대기표 (먼저 신청한 사람이 먼저 업무를 봄)
- 순서가 보장된 처리를 제공
- 사용자가 몰린 서버에 유
기능으로는
- offer() : 데이터 삽입
- poll() : 데이터 빼기
- peek() : 데이터를 뽑지 않고, 큐 안에서 가장 먼저 들어온 데이터 확인
2. 구현
package queue;
public interface IQueue<T> {
void offer(T data);
T poll();
T peek();
int size();
void clear();
boolean isEmpty();
}
package queue;
public class MyLinkedQueue<T> implements IQueue<T> {
// 노드 기반 큐
private Node head;
private Node tail;
private int size;
public MyLinkedQueue() {
this.size = 0;
this.head = new Node(null);
this.tail = this.head; // head의 레퍼런스를 집어넣었으니 tail에서
// 다른 레퍼런스를 집어넣기 전까진, head를 조작하는 것과 같다.
}
@Override
public void offer(T data) {
Node node = new Node(data);
this.tail.next = node;
this.tail = this.tail.next;
this.size++;
}
@Override
public T poll() {
if (this.isEmpty()) {
throw new IllegalStateException();
}
Node node = this.head.next;
this.head.next = node.next;
node.next = null;
this.size--;
if (this.head.next == null) {
this.tail = this.head;
}
return node.data;
}
@Override
public T peek() {
if (this.isEmpty()) {
throw new IllegalStateException();
}
return this.head.next.data;
}
@Override
public int size() {
return this.size;
}
@Override
public void clear() {
this.head.next = null;
this.tail = head;
this.size = 0;
}
@Override
public boolean isEmpty() {
return this.head.next == null;
}
private class Node {
T data;
Node next;
Node(T data) { this.data = data; }
Node(T data, Node next) {
this.data = data;
this.next = next;
}
}
}
3. 설명
3-1. 노드 구성
data (데이터를 가지는 변수), next (다음 노드, 포인터의 역할)
인수로 data만 넣으면 노드에 데이터만 대입, data와 노드를 넣으면 노드에 데이터와 다음 노드를 대입한다.
3-2. 멤버 변수(Member Variable), 생성자(constructor)
선형 큐를 편하게 구현하려면, 데이터가 들어가지 않는 head와 tail이라는 노드가 필요하다.
그리고 큐의 크기를 나타내는 size가 있다.
인스턴스 생성 시, 생성자에서 size는 0, head에 빈 노드를 집어넣고, tail에 head를 집어넣는다.
자바에선 인스턴스 생성 시, 변수에 그 레퍼런스 주소가 들어가니, head와 tail은 같은 메모리 주소를
보고 있다.
3-3. offer()
데이터를 삽입하는 메소드다.
데이터를 집어넣기 위해, 인수로 들어온 데이터를 대입한 노드를 생성한다.
데이터가 없는 상태에선, tail은 head와 같으므로, this.tail.next = node 로 대입하면, head와 tail의 next는
데이터가 들어간 새로운 노드가 된다.
tail에 tail.next를 대입하게 되면, tail의 next는 새로운 노드이니, head는 그대로, tail은 새로운 노드가 된다.
정리하면
head(null, newNode1), tail(newNode1.data, null), newNode1(data, null);
한번 더 데이터가 들어오게 되면, 데이터가 들어간 2번째 노드가 생성되고, tail은 지금 newNode1이니, newNode1
의 next는 2번째 노드가 된다.
tail에 tail.next를 대입하면, tail은 2번째 노드가 되고, 정리하면
head(null, newNode1), tail(newNode2.data, null), newNode1(data, newNode2), newNode2(data,
null) 의 상태가 된다.
3-4. isEmpty()
큐에 데이터가 들어가 있나 확인하는 메소드다.
생성자에서 head의 next를 설정하지 않았으니, 데이터가 들어오지 않았으면 head.next는 null이고, true를
반환한다. 데이터가 있으면 head.next가 null이 아니므로, false를 반환한다.
3-5. poll()
큐에서 가장 먼저 들어온 데이터를 가져오고, 그 데이터를 큐에서 삭제하는 메소드다.
우선 큐가 비어있으면, 꺼낼 데이터가 없으니, isEmpty() 메소드로 확인하고, 없으면 에러를 발생시킨다.
데이터가 있으면, 데이터를 꺼내기 위해 새로운 노드를 생성하고, 가장 먼저 들어온 데이터가 있는, head.next
의 레퍼런스 주소를 대입한다.
가장 먼저 들어온 데이터 외에 데이터가 있다면, 가장 먼저 들어온 노드의 next에 저장되어 있으니, head.next
를 그걸로 바꿔준다.
그리고, 데이터를 꺼낼 노드의 next를 null로 바꿔주면, 이 메소드가 끝나게 되면, 이 노드를 참조하는 변수는 아무것도
없으니, 자바의 GC(Garbage Collector)에 의해 없어지게 된다.
데이터를 가진 노드가 하나 사라졌으니 사이즈를 1 줄여주고, 만약 데이터를 가진 노드가 없다면, offer() 메소드의
작업을 하기 위해, 생성자와 같이, this.tail = this.head의 형태로 레퍼런스를 대입한다.
그리고, 가장 먼저 들어온 노드의 데이터를 반환하고, 메소드가 끝나게 된다.
3-6. peek()
poll() 메소드와 다르게, 가장 먼저 들어온 노드를 삭제하지 않고, 노드의 데이터만 반환받는 메소드다.
만약, 큐가 비었으면 에러를 일으킨다. 그게 아니라면 head.next는 가장 먼저 들어온 데이터가 들어간 노드이니,
그 데이터를 반환하고 메소드가 종료된다.
3-7. size()
큐의 사이즈를 반환하는 메소드다.
3-8. clear()
큐 안에 데이터가 있는 노드를 모두 삭제하는 메소드다.
head.next에 가장 먼저 들어온 데이터를 가진 노드가 있고, 그 노드부터 다른 노드로 이어져 있는 큐인데,
가장 먼저 들어온 노드의 참조를 null로 끊고, 마지막 데이터가 있는 tail을, 데이터와 포인터가 모두 비어있는
head로 초기화하면서, 데이터가 있는 노드들은 GC에 의해 삭제되고, 큐에는 빈 노드인 head와 tail만 남는다.
데이터가 들어간 노드가 없으니 사이즈에 0을 대입하고 메소드가 종료된다.
'자료구조' 카테고리의 다른 글
자료구조 트리(Tree) 개요, 트리 탐색 방법 (0) | 2022.02.22 |
---|---|
자료구조 원형 큐 (Circular Queue) 자바로 구현 (0) | 2022.02.17 |
자료구조 스택 (Stack) 자바로 구현 (0) | 2022.02.15 |
자료구조 이중 연결 리스트(Double Linked List) 자바로 구현 (0) | 2022.02.14 |
자료구조 배열 리스트 (ArrayList) 자바로 구현 (0) | 2022.02.13 |
댓글