자료구조&알고리즘/이론공부
05_큐(queue)
yong_ღ'ᴗ'ღ
2022. 8. 19. 16:47
- 스택과 마찬가지로 데이터를 일시적으로 쌓아 두기 위한 자료구조
- 선입선출(FIFO, First In First Out / LILO, Last In Last Out)
- 실생활에서 많이 보는 예) 은행 창구 차례, 마트 계산 대기열 등등
- 기본 용어
- enqueue: 인큐(큐에 데이터 넣기) → O(1)
- dequeue: 디큐(큐에서 데이터 꺼내기 → O(n) : 맨 앞 데이터 꺼내고, 나머지 데이터들 다 한 칸씩 앞으로 이동
- front: 데이터를 꺼내는 쪽
- rear: 데이터를 넣는 쪽
- 링 버퍼(ring buffer)를 사용한 Queue: dequeue 하고도 배열 요소를 앞으로 옮기지 않는 큐
- ring처럼 처음과 끝이 연결되어 있음
- enqueue(), dequeue() 모두 O(1)
- **주의할 점
- enqueue하고 rear가 max와 같아지면 rear = 0; 처리 (원형으로 돌아야하니까)
- dequeue하고 front가 max와 같아지면 front = 0; 처리
- indexOf 할 때, 인덱스의 계산 『(i + front) % max』
- dump할 때에도, 인덱스 계산 『(i + front) % max』
- 링 버퍼로 큐 만들어보기
public class RingBufferQueue {
private int max; // 큐의 용량
private int front; // 첫 번째 요소 커서
private int rear; // 마지막 요소 커서
//-- 다음 인큐할 인덱스 미리 준비해 두는 것
private int num; // 현재 데이터 수
//-- front == rear 경우, 큐가 빈 건지/가득 찬 건지 구별할 수 없는 상황을 위해 필요
private int[] que; // 큐 본체
// 실행 시 예외: 큐가 비어 있음
public class EmptyIntQueueException extends RuntimeException {
public EmptyIntQueueException() {}
}
// 실행 시 예외: 큐가 가득 참
public class OverflowIntQueueException extends RuntimeException {
public OverflowIntQueueException() {}
}
// 생성자
public ringBufferQueue(int capacity) {
num = front = rear = 0;
max = capacity;
try {
que = new int[max];
} catch (OutOfMemoryError e) {
max = 0;
}
}
// enque() : 큐의 맨 뒤에 데이터 넣음
public int enque(int x) throws OverflowIntQueueException {
if (num >= max)
throw new OverflowIntQueueException();
que[rear++] = x;
num++;
if (rear == max) // *rear가 최대 용량의 max와 같을 경우, rear = 0으로 설정
rear = 0;
return x;
}
// deque() : 큐의 맨 앞 데이터 꺼냄
public int deque() throws EmptyIntQueueException {
if (num <= 0)
throw new EmptyIntQueueException();
int x = que[front++];
num--;
if (front == max) // *front가 max와 같을 경우, front = 0으로 설정
front = 0;
return x;
}
// peek() : 큐의 맨 앞 데이터 확인 (꺼내지는 X → front, rear, num 값 변화 없음)
public int peek() throws EmptyIntQueueException {
if (num <= 0)
throw new EmptyIntQueueException();
return que[front];
}
// indexOf() : 검색 값의 인덱스 반환 (없으면 -1 반환)
public int indexOf(int x) {
// front → rear 선형 검색
for (int i = 0; i < num; i++) {
int idx = (i + front) % max; // ★
if (que[idx] == x)
return idx;
}
return -1;
}
// clear() : 큐의 모든 데이터 삭제
public void clear() {
num = front = rear = 0; // → 큐의 요솟값 바꿀 필요 X
}
// capacity() : 최대 용량 확인
public int capacity() {
return max;
}
// size() : 현재 데이터 개수 확인
public int size() {
return num;
}
// isEmpty() : 큐 비어 있음? (true/false 반환)
public boolean isEmpty() {
return num <= 0;
}
// isFull() : 큐 가득 참? (true/false 반환)
public boolean isFull() {
return num >= max;
}
// dump() : 큐의 모든 데이터 front → rear 순으로 출력
public void dump() {
if (num <= 0)
System.out.println("큐 비어있음");
else {
for (int i = 0; i < num; i++)
System.out.print(que[(i + front) % max] + " ");
System.out.println();
}
}
}
- 링 버퍼: 오래된 데이터를 버리는 용도로도 사용할 수 있음
ex) 용량 10으로 해놓고 enqueue는 무한히 가능하게
> 데이터는 10개만 저장하고 10개 초과 시, 오래된 데이터는 새로 입력된 데이터로 덮어쓰기
> 가장 최근에 입력한 10개의 데이터만 링 버퍼에 남게 된다.
코드 예시)
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
final int N = 10;
int[] a = new int[N];
int cnt = 0; // 입력 받은 개수
int retry;
System.out.println("정수를 입력하세요.");
do {
System.out.printf("%d번째 정수 : ", cnt + 1);
a[cnt++ % N] = sc.nextInt();
//-- 『cnt++ % N』 : 링 버퍼(배열)에 순환하며 저장되고 있음
System.out.print("계속? (예.1/아니오.0) : ");
retry = sc.nextInt();
} while (retry == 1);
int i = cnt - N; // 최근 입력한 N개 중 첫번째 번호 구함
if (i < 0) i = 0;
for ( ; i < cnt; i++)
System.out.printf("%2d번째 정수 = %d\n", i + 1, a[i % N]);
}
- 덱(deck): 양방향 대기열(deque/double ended queue), 시작과 끝 지점에서 양쪽으로 데이터를 인큐, 디큐하는 자료구조
- 덱 만들어보기
public class Deck {
private int max; // 덱(deck)의 용량
private int num; // 현재 데이터 개수
private int front; // 맨 앞 커서
private int rear; // 맨 뒤 커서
private int[] que; // 덱(deck)의 본체
// 실행 시 예외: deck 비어 있음
public class EmptyDeckException extends RuntimeException {
public EmptyDeckException() {}
}
// 실행 시 예외: deck 가득 참
public class OverflowDeckException extends RuntimeException {
public OverflowDeckException() {}
}
// 생성자
public deck(int capacity) {
num = front = rear = 0;
max = capacity;
try {
que = new int[max];
} catch (OutOfMemoryError e) {
max = 0;
}
}
// enqueFront() : 데이터를 front에 enque
public int enqueFront(int x) throws OverflowDeckException {
if (num >= max)
throw new OverflowDeckException();
if (--front < 0)
front = max - 1;
que[front] = x;
num++;
return x;
}
// enqueRear() : 데이터를 rear에 enque
public int enqueRear(int x) throws OverflowDeckException {
if (num >= max)
throw new OverflowDeckException();
que[rear++] = x;
if (rear == max)
rear = 0;
num++;
return x;
}
// dequeFront() : front에서 deque
public int dequeFront() throws EmptyDeckException {
if (num <= 0)
throw new EmptyDeckException();
int x = que[front++];
if (front == max)
front = 0;
num--;
return x;
}
// dequeRear() : rear에서 deque
public int dequeRear() throws EmptyDeckException {
if (num <= 0)
throw new EmptyDeckException();
if (--rear < 0)
rear = max - 1;
num--;
return que[rear];
}
// peekFront() : front 데이터를 peek
public int peekFront() throws EmptyDeckException {
if (num <= 0)
throw new EmptyDeckException();
return que[front];
}
// peekRear() : rear 데이터를 peek
public int peekRear() throws EmptyDeckException {
if (num <= 0)
throw new EmptyDeckException();
return que[rear == 0 ? max - 1 : rear - 1];
}
// indexOf()
public int indexOf(int x) {
for (int i = 0; i < num; i++)
if (que[(i + front) % max] == x)
return i + front;
return -1;
}
// 아래 메서드는 RingBufferQueue 코드와 동일
// search()
// clear()
// capacity()
// size()
// isEmpty()
// isFull()
// dump()
}