스택 & 큐
스택

특징
- 입력과 출력이 한 방향으로 제한된다.
- LIFO (Last In First Out) 구조이다.
사용처
- 함수의 콜스택
- 문자열 역순 출력
- 연산자 후위표기법
연산 종류
push: 데이터 삽입pop: 최상위 데이터 추출isEmpty: 비어있는지 확인isFull: 꽉 차있는지 확인
스택 포인터 (SP)
push와pop연산을 하려면 최상위 데이터의 위치를 알아야 한다.- 최상위 데이터의 위치를 스택 포인터 (SP)가 가지고 있다.
- 초기값은 -1

push 연산
public void push(Object o) {
if (isFull()) return;
stack[++sp] = o;
}
- 스택이 꽉 차있다면 return
- 아니면 스택의 최상위 요소 (
sp) 위에 요소 삽입
pop 연산
public Object pop() {
if (isEmpty()) return null;
return stack[sp--];
}
- 스택이 비어있다면 return null
- 아니면 스택의 최상위 요소 (
sp) return- 최상위 요소를 추출했으므로,
sp는 1 감소
- 최상위 요소를 추출했으므로,
isEmpty 연산
private boolean isEmpty() {
return sp == -1;
}
- 스택이 비어있다면
sp는 -1
isFull 연산
private boolean isFull() {
return sp == MAX_SIZE - 1;
}
- 스택의 최대 크기만큼 요소가 채워져 있다면,
sp는MAX_SIZE - 1
크기 제한이 없는 스택
- 동적 배열을 활용하여, 크기 제한이 없는 스택을 구현할 수 있음
public void push(Object o) {
if (isFull()) {
Object[] newStack = new Obejct[MAX_SIZE * 2]; //크기가 2배인 새 스택 생성
copyAry(stack, newStack); //기존 스택의 요소 복사
stack = newStack; //새로 만든 스택으로 교체
MAX_SIZE *= 2; //최대 크기를 2배로 증가
}
stack[++sp] = 0; //요소 추가
}
private void copyAry(Object[] fromAry, Object[] toAry) {
for (int i = 0; i < fromAry.length; i++) {
toAry[i] = fromAry[i];
}
}
큐

특징
- 입력과 출력을 한 쪽 끝 (front, rear)으로 제한한다.
- FIFO (First In First Out) 구조를 갖는다.
사용처
- 버퍼 (Buffer)
- 빠르게 입력된 작업들을 임시로 가지고 있는 공간
- BFS
연산 종류
enqueue: 데이터 삽입dequeue: 데이터 추출isEmpty: 비어있는지 확인isFull: 꽉 차있는지 확인
front, rear, size
- front
- 큐의 가장 첫 요소 (가장 먼저 들어온 요소)
dequeue할 위치 기억- 초기값 = -1
- rear
- 큐의 가장 마지막 요소 (가장 늦게 들어온 요소)
enqueue할 위치 기억- 초기값 = -1
- size
- 큐에 저장된 총 요소의 개수
- 초기값 = 0
enqueue 연산
public void enqueue(Object o) {
if (isFull()) return;
queue[++rear] = o;
}
- 큐가 꽉 차있다면 return
- 아니면 rear를 1 증가시키고, 해당 위치에 요소 추가

dequeue 연산
public Object dequeue() {
if (isEmpty()) return null;
return queue[++front];
}
- 큐가 비어있다면 return
- 아니면 front를 1 증가시키고, 해당 위치의 요소 추출

isEmpty 연산
private boolean isEmpty() {
return front == rear;
}
- front 와 rear 가 같다면 빈 상태로 판단
isFull 연산
//선형큐의 한계가 있는 코드
private boolean isFull() {
return rear == MAX_SIZE - 1;
}
선형큐의 문제
-
enqueue와dequeue연산을 반복하다보면 아래와 같은 상황이 발생한다.MAX_SIZE = 2 라고 가정 [1. enqueue 연산] front = -1 rear = 0 [2. enqueue 연산] front = -1 rear = 1 [3. dequeue 연산] front = 0 rear = 1 [4. dequeue 연산] front = 1 rear = 1 결과적으로 rear == MAX_SIZE - 1 을 만족하게 됨 - 2번
enqueue하고 2번dequeue했다면, 큐가 비어있어야 정상이다. - 하지만
rear == MAX_SIZE - 1을 만족하며, 큐가 가득 찬 상태로 판단하게 된다. -
dequeue를 할 때마다, 모든 요소들을 앞으로 이동시켜주면 문제를 해결할 수 있다.
- 하지만 이런 방식은 비효율적이다.
- 원형큐를 통해 문제를 해결할 수 있다.
원형큐

- 논리적으로 배열의 처음과 끝이 연결되어 있다고 간주한다.
- 초기 공백 상태인 경우, front와 rear가 0이다.
- 공백, 포화 상태를 구분하기 위해, 항상 자리를 하나 비워둔다.

원형큐 - enqueue 연산
public void enqueue(Object o) {
if (isFull()) return;
rear = (rear + 1) % MAX_SIZE;
queue[rear] = o;
}
(rear + 1) % MAX_SIZE을 통해, 순환할 수 있다.
원형큐 - dequeue 연산
public Object dequeue() {
if (isEmpty()) return null;
front = (front + 1) % size;
return queue[front];
}
(front + 1) % MAX_SIZE을 통해, 순환할 수 있다.
원형큐 - isEmpty 연산
private boolean isEmpty() {
return front == rear;
}
원형큐 - isFull 연산
private boolean isFull() {
return ((rear+1) % MAX_SIZE) == front;
}