알고리즘

스택, 큐, 덱

D_Helloper 2023. 9. 23. 12:49

java.util

  • 자바의 자료 구조를 담고 있는 패키지
  • import java.util.*;

Stack(스택)

  • 한쪽 끝에서만 자료를 넣고 뺄 수 있는 LIFO(Last In First Out) 형식의 구조
  • 동적 배열, 단일 연결 리스트로 구현할 수 있음

push

Stack<Integer> stack = new Stack<>();
for (int i = 1; i < 5; i++) {
		stack.push(i);
}
System.out.println(stack);    // [1, 2, 3, 4, 5]
  • 스택의 top 에 데이터를 추가
  • 시간복잡도 : O(1)

pop

Stack<Integer> stack = new Stack<>();
for (int i = 1; i <= 5; i++) {
		stack.push(i);
}
for (int i = 1; i <= 3; i++) {
		stack.pop();
}
System.out.println(stack);    // [1, 2]
  • 스택의 top 에서 데이터를 제거하고 반환
  • 시간복잡도 : O(1)
  • 비어있는 스택에서 pop 메소드를 호출하면 EmptyStackException 발생

peek

Stack<Integer> stack = new Stack<>();
for (int i = 1; i <= 5; i++) {
		stack.push(i);
}
System.out.println(stack.peek());    // 5
  • 스택의 top 에서 데이터를 제거하지 않고 반환
  • 시간복잡도 : O(1)
  • 비어있는 스택에서 peek 메소드를 호출하면 EmptyStackException 발생

isEmpty

Stack<Integer> stack = new Stack<>();
System.out.println(stack.isEmpty());    // true
stack.push(10);
System.out.println(stack.isEmpty());    // false
  • 스택이 비어있는 지 여부를 boolean 으로 반환

size

Stack<Integer> stack = new Stack<>();
for (int i = 1; i <= 5; i++) {
		stack.push(i);
}
System.out.println(stack.size());
  • 스택의 요소의 개수를 반환

활용

스택에서 가장 중요한 것은 이 문제를 스택으로 풀어야겠다는 생각을 떠올리는 것

Queue(큐)

  • 한쪽 끝에서 자료를 추가하고 반대쪽 끝에서 자료를 꺼내는 FIFO(First In First Out) 형식의 구조
  • 원형 배열, 단일 연결 리스트로 구현할 수 있음
  • 자바에서 Queue 는 인터페이스이기 때문에 일반적으로 ArrayDeque 또는 LinkedList 로 구현하여 사용함

offer

Queue<Integer> queue = new LinkedList<>();
for (int i = 1; i <= 5; i++) {
		queue.**offer**(i);
}
System.out.println(queue);
  • 큐의 back 에 데이터를 추가
  • 시간복잡도 : O(1)
  • add 메소드는 데이터 추가 불가 시 예외를 던지지만, offer 메소드는 false 를 리턴함

poll

Queue<Integer> queue = new LinkedList<>();
for (int i = 1; i <= 5; i++) {
		queue.offer(i);
}
for (int i = 1; i <= 3; i++) {
		int p = queue.**poll**();
		System.out.print(p + " ");    // 1 2 3
}
System.out.println(queue);        // [4, 5]
  • 큐의 front 에서 데이터를 제거하고 반환
  • 시간복잡도 : O(1)
  • remove 메소드는 데이터 삭제 불가 시 예외를 던지지만, poll 메소드는 false 를 리턴함

peek

Queue<Integer> queue = new LinkedList<>();
for (int i = 1; i <= 5; i++) {
		queue.offer(i);
}
System.out.println(queue.**peek**());    // 1
  • 큐의 front 에서 데이터를 제거하지 않고 반환
  • 시간복잡도 : O(1)
  • element 메소드는 큐가 비어있으면 예외를 던지지만, peek 메소드는 null 을 리턴함

PriorityQueue(우선순위 큐)

PriorityQueue<Integer> pq = new PriorityQueue<>();
for (int i = 5; i >= 1; i--) {
		pq.offer(i);
		System.out.println(pq);
}
while (!pq.isEmpty()) {
		System.out.print(pq.poll() + " ");
}
  • heap 자료구조 사용하여 구현됨
  • 데이터가 들어온 순서대로 나가는 것이 아닌 우선순위가 높은 데이터가 먼저 나가는 구조

Max-heap(내림차순)

PriorityQueue<Integer> pq = new PriorityQueue<>(**Collections.reverseOrder()**);
for (int i = 5; i >= 1; i--) {
		pq.offer(i);
		System.out.println(pq);
}
while (!pq.isEmpty()) {
		System.out.print(pq.poll() + " ");
}
  • 우선 순위 큐의 디폴트 설정은 오름차순(min-heap, 작은 값이 우선 순위를 가짐)
  • Collections.reverseOrder() 를 사용하여 내림차순(max-heap) 설정

시간 복잡도

  • 주어진 input 의 개수 : N 개
  • 우선순위 큐에 하나의 input 을 저장 or 삭제 : O(logN)
  • 우선순위 큐를 이용한 정렬
    • N 개의 input 을 모두 큐에 저장 한 뒤 하나씩 꺼내기만 해도 알아서 정렬이 진행됨
    • N 개를 큐에 저장하는 시간 + N 개를 큐에서 빼는 시간 = 2 * N * logN = O(NlogN)
    • 따라서 우선순위 큐를 이용한 정렬은 O(NlogN) 의 시간 복잡도를 가짐

정렬 기준 설정(Comparable, Comparator)

  • 우선순위 큐에 저장할 객체는 2 가지 방법으로 우선 순위 조건을 설정할 수 있음
    1. Comparable 인터페이스를 구현해서 compareTo 메소드를 오버라이드
    2. Comparator 인터페이스를 구현해서 compare 메소드를 오버라이드

1. Comparable 인터페이스 활용

// 2차원 배열의 좌표를 저장하는 객체
class Pair implements Comparable<Pair>{

		int r;
		int c;
		
		public Pair(int r, int c) {
				this.r = r;
				this.c = c;
		}

		// 2차원 배열에서 가장 위쪽에 있는 좌표를,
		// 그러한 좌표가 많다면 가장 왼족에 있는 좌표를 먼저 큐에서 뽑아냄
		**@Override
		public int compareTo(Pair o) {
				if (this.r != o.r) {
						return this.r - o.r;	
				} else {
						return this.c - o.c;	
				}
		}**
}

2. Comparator 활용

// 방법 1
PriorityQueue<Pair> pq1 = new PriorityQueue<>(new Comparator<Pair>() {
    @Override
		public int compare(Pair o1, Pair o2) {
				if (o1.r != o2.r) {
						return o1.r - o2.r;
				} else {
						return o1.c - o2.c;
				}
		}
});

// 방법 2
PriorityQueue<Pair> pq2 = new PriorityQueue<>((o1, o2) -> {
    if (o1.r != o2.r) {
				return o1.r - o2.r;
		} else {
				return o1.c - o2.c;
		}
});

둘 중 편한 방법으로 사용!

활용