Deque(데크)란 Double Ended Queue의 줄임말로, 양쪽 끝에서 삽입과 삭제가 모두 가능한 선형 자료구조를 의미합니다. 즉, 스택과 큐를 합쳐 놓은 것과 같은 형태를 가지고 있습니다.
Deque는 배열(Array)과 연결 리스트(Linked List)를 이용해 구현할 수 있습니다. 이 중 배열로 구현된 Deque는 원형 배열을 이용하여 구현하는 것이 일반적입니다. 데크에서 가장 중요한 점은 삽입과 삭제 연산이 양쪽 끝에서 모두 O(1)의 시간 복잡도를 가진다는 것입니다.
슬라이딩 윈도우(Sliding Window) 문제 해결
슬라이딩 윈도우란, 배열이나 연속된 데이터에서 일정 범위의 부분집합을 연속적으로 탐색하는 방법을 의미합니다. 이때 Deque를 이용하면 O(n)의 시간 복잡도로 슬라이딩 윈도우 문제를 해결할 수 있습니다.
매치 메이킹(Match Making) 시스템에서 매칭 대기열 관리
온라인 게임에서 매치 메이킹 시스템은 여러 사용자를 매칭하여 게임을 시작할 수 있는 상태로 만듭니다. 이때 Deque를 사용하여 매칭 대기열을 관리하면, 매칭 대기열에서 매칭이 되지 않은 사용자를 쉽게 제거하고 새로운 사용자를 추가할 수 있습니다.
브라우저의 뒤로 가기, 앞으로 가기 기능 구현
브라우저의 뒤로 가기, 앞으로 가기 기능은 방문한 페이지들을 스택(Stack)에 저장하여 관리합니다. 이때 Deque를 사용하여 스택을 구현하면, 뒤로 가기, 앞으로 가기를 O(1)의 시간 복잡도로 처리할 수 있습니다.
Java에서 Deque를 구현하는 방법은 크게 2가지가 있습니다.
ArrayDeque는 내부적으로 배열을 사용하여 Deque를 구현하고, LinkedList는 내부적으로 연결 리스트를 사용하여 Deque를 구현합니다.
import java.util.ArrayDeque;
public class ArrayDequeExample {
public static void main(String[] args) {
// create a new ArrayDeque
ArrayDeque<String> deque = new ArrayDeque<>();
// add elements to the deque
deque.addFirst("One");
deque.addLast("Two");
deque.offerFirst("Three");
deque.offerLast("Four");
// print the deque
System.out.println("Deque: " + deque);
// remove elements from the deque
deque.removeFirst();
deque.removeLast();
// print the deque after removing elements
System.out.println("Deque after removing elements: " + deque);
}
}
import java.util.LinkedList;
public class LinkedListExample {
public static void main(String[] args) {
// create a new LinkedList
LinkedList<String> deque = new LinkedList<>();
// add elements to the deque
deque.addFirst("One");
deque.addLast("Two");
deque.offerFirst("Three");
deque.offerLast("Four");
// print the deque
System.out.println("Deque: " + deque);
// remove elements from the deque
deque.removeFirst();
deque.removeLast();
// print the deque after removing elements
System.out.println("Deque after removing elements: " + deque);
}
}
Deque를 구현할 때, 요소 추가와 삭제가 자주 일어나는 경우 ArrayDeque를 사용하면 좋고, 요소 조회가 더 많이 일어나는 경우 LinkedList를 사용하는 것이 좋습니다.
ArrayDeque에서 맨 마지막 요소를 pop으로 삭제하고 반환하면 해당 공간은 비어 있지만, 배열의 크기는 줄어들지 않습니다. ArrayDeque 내부에서는 삭제된 위치에 null 값을 저장하여 해당 요소가 더 이상 사용되지 않도록 표시하는 방식으로 요소를 제거합니다.
ArrayDeque는 내부적으로 Array(고정 크기)을 사용하지만, 배열의 크기를 동적으로 조절할 수 있습니다. 따라서, 요소를 추가할 때 배열이 꽉 차면 더 큰 배열을 만들어 기존 요소를 복사하고, 요소를 삭제할 때 배열의 크기가 사용 중인 요소의 수보다 크면 배열의 크기를 줄이는 등 동적인 크기 조절이 가능합니다.
이와 다르게 LinkedList는 removeFirst(), removeLast() 메소드를 사용해서 맨 앞이나 뒤의 요소를 제거하여 반환하고 난 후 해당 노드가 가비지 컬렉션에 의해 메모리에서 제거됩니다. 새로운 요소를 추가할 때는 새로운 노드를 생성하고 이전 노드와 연결하는 과정을 거칩니다.
정리하자면 ArrayDeque는 고정 길이인 Array를 통해 구현됐기 때문에 크기가 변해야 할 때는 새로운 Array를 생성하고 원소를 넣는 과정이 필요하고 LinkedList는 작업하고자 하는 요소가 존재하는 노드를 생성하거나 제거하고, 생성한 후에는 이전 노드와 연결하는 과정이 있고, 제거할때는 가바지 컬렉션이 해당 노드를 제거하게 됩니다.