덱(Deque) 자료구조

덱(Deque) 자료구조

작성자
태인태인
카테고리
📗 스터디
작성일
2024년 05월 16일
태그
Java
Algorithm

덱 (Deque)

양쪽 끝에서 삽입과 삭제가 가능한 자료구조이다. 스택과 큐 자료구조를 혼합한 자료구조라고 생각하면 된다.
어느 방향으로 입력, 출력하는지에 따라 스택과 큐로 모두 사용할 수 있다는 점이 장점이다. 참고로 한 쪽으로만 입력가능하게 설정한 덱을 스크롤(scroll)이라고 하고, 한 쪽으로만 출력 가능하도록 설정한 덱은 셸프(shelf)라고 한다.
 

메소드

덱의 메소드는 큐, 스택과 유사한 부분이 많다. 이 글에서는 JAVA의 Deque Interface를 기준으로 설명한다.

삽입

  • addFirst() : 덱의 앞에 요소를 삽입한다. 전체 크기를 초과하면 예외가 발생한다.
  • addLast() : 덱의 뒤에 요소를 삽입한다. 전체 크기를 초과하면 예외가 발생한다.
  • offerFirst() : 덱의 앞에 요소를 삽입한다. 요소 삽입 성공 여부가 true/false로 반환된다.
  • offerLast() : 덱의 뒤에 요소를 삽입한다. 요소 삽입 성공 여부가 true/false로 반환된다.
  • add() : addLast()와 동일

삭제

  • removeFirst() : 덱의 앞에서 요소 하나를 제거하고, 해당 요소를 반환한다. 덱이 비어있는 경우 예외가 발생한다.
  • removeLast() : 덱의 뒤에서 요소 하나를 제거하고, 해당 요소를 반환한다. 덱이 비어있는 경우 예외가 발생한다.
  • pollFirst() : 덱의 앞에서 요소 하나를 제거하고, 해당 요소를 반환한다. 덱이 비어있는 경우 null이 반환된다.
  • pollLast() : 덱의 뒤에서 요소 하나를 제거하고, 해당 요소를 반환한다. 덱이 비어있는 경우 null이 반환된다.
  • remove() : removeFirst()와 동일
  • poll() : pollFirst()와 동일

값 확인

  • getFirst() : 덱의 앞에서 요소 하나의 값을 반환한다(제거 X). 덱이 비어있는 경우 예외가 발생한다.
  • getLast() : 덱의 뒤에서 요소 하나의 값을 반환한다(제거 X). 덱이 비어있는 경우 예외가 발생한다.
  • peekFirst() : 덱의 앞에서 요소 하나의 값을 반환한다(제거 X). 덱이 비어있는 경우 null이 반환된다.
  • peekLast() : 덱의 뒤에서 요소 하나의 값을 반환한다(제거 X). 덱이 비어있는 경우 null이 반환된다.
  • peek() : peekFirst()와 동일

기타

  • removeFirstOccurrence(Object o) : 덱의 앞에서부터 탐색하여 Object o와 동일한 첫 요소를 제거한다.
  • removeLastOccurrence(Object o) : 덱의 뒤에서부터 탐색하여 입력한 Object o와 동일한 첫 요소를 제거한다.
이때, Object o 와 같은 요소가 덱에 없으면 아무런 변경도 진행되지 않는다.
  • push() : addFirst()와 동일.
  • pop() : removeFirst()와 동일.
  • remove(Object o) : removeFirstOccurrence(Object o)와 동일.
  • contain(Object o) : 덱에 Object o와 동일한 요소가 포함되어 있는지 여부를 반환한다.
  • size() : 덱에 들어있는 요소의 개수를 반환한다.
  • isEmpty() : 덱이 비어있는 지 여부 반환한다.
 

사용

JAVA에서는 deque를 다음과 같은 방법들로 생성할 수 있다.
Deque<String> deque1 = new ArrayDeque<>(); Deque<String> deque2 = new LinkedBlockingDeque<>(); Deque<String> deque3 = new ConcurrentLinkedDeque<>(); Deque<String> linkedList = new LinkedList<>();
 

백준 #10866번 : 덱

문제

정수를 저장하는 덱(Deque)를 구현한 다음, 입력으로 주어지는 명령을 처리하는 프로그램을 작성하시오.
명령은 총 여덟 가지이다.
  • push_front X: 정수 X를 덱의 앞에 넣는다.
  • push_back X: 정수 X를 덱의 뒤에 넣는다.
  • pop_front: 덱의 가장 앞에 있는 수를 빼고, 그 수를 출력한다. 만약, 덱에 들어있는 정수가 없는 경우에는 -1을 출력한다.
  • pop_back: 덱의 가장 뒤에 있는 수를 빼고, 그 수를 출력한다. 만약, 덱에 들어있는 정수가 없는 경우에는 -1을 출력한다.
  • size: 덱에 들어있는 정수의 개수를 출력한다.
  • empty: 덱이 비어있으면 1을, 아니면 0을 출력한다.
  • front: 덱의 가장 앞에 있는 정수를 출력한다. 만약 덱에 들어있는 정수가 없는 경우에는 -1을 출력한다.
  • back: 덱의 가장 뒤에 있는 정수를 출력한다. 만약 덱에 들어있는 정수가 없는 경우에는 -1을 출력한다.

입력

첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지 않은 명령이 주어지는 경우는 없다.

출력

출력해야하는 명령이 주어질 때마다, 한 줄에 하나씩 출력한다.

위에서 살펴본 덱의 기본 메소드들을 활용하면 쉽게 풀 수 있는 문제이다.
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.util.*; public class Main { public static void main(String[] args) throws IOException { StringBuilder sb = new StringBuilder(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int N = Integer.parseInt(br.readLine()); Deque<Integer> deque = new ArrayDeque<>(); for(int i=0; i<N; i++) { StringTokenizer st = new StringTokenizer(br.readLine()); String command = st.nextToken(); switch (command) { case "push_front": int xF = Integer.parseInt(st.nextToken()); deque.addFirst(xF); break; case "push_back": int xB = Integer.parseInt(st.nextToken()); deque.addLast(xB); break; case "pop_front": Integer pF = deque.pollFirst(); if(pF == null) pF = -1; sb.append(pF).append("\n"); break; case "pop_back": Integer pB = deque.pollLast(); if(pB == null) pB = -1; sb.append(pB).append("\n"); break; case "size": sb.append(deque.size()).append("\n"); break; case "empty": if(deque.isEmpty()) sb.append(1).append("\n"); else sb.append(0).append("\n"); break; case "front": Integer vF = deque.peekFirst(); if(vF == null) vF = -1; sb.append(vF).append("\n"); break; case "back": Integer vB = deque.peekLast(); if(vB == null) vB = -1; sb.append(vB).append("\n"); break; } } System.out.println(sb); } }
 
나머지 코드는 쉽게 이해할 수 있을 것으로 생각되니, 문제의 만약, 덱에 들어있는 정수가 없는 경우에는 -1을 출력한다. 조건을 구현하는 로직만 살펴보자.
Integer pF = deque.pollFirst(); if(pF == null) pF = -1; sb.append(pF).append("\n");
여기서 int가 아닌 Integer 형 변수를 사용한 이유는 무엇일까? intnull값을 저장할 수 없기 때문이다. 따라서 Integer 형 변수가 null 이라면 -1로 바꾸어 저장하도록 코드를 작성해주었다.
 

백준 #1835번 : 카드

문제

1부터 N까지의 숫자가 적힌 카드가 있다. 찬유는 이 카드를 가지고 마술을 하려 한다. 마술을 하는 순서는 다음과 같다.
  1. 먼저 1부터 N까지의 숫자가 적힌 카드에서 첫 번째 카드를 가장 뒤로 옮긴다. 그러고 나서 첫 번째 카드를 책상 위에 올려놓는다. 그런데 그 카드는 1이 되어야 한다.
  1. 그리고 남은 카드 중에서 첫 번째 카드를 가장 뒤로 옮기고, 또 가장 앞에 있는 카드를 가장 뒤로 옮긴다.(2번 반복) 그리고 가장 앞에 있는 카드를 책상 위에 올려놓는다. 그런데 그 카드는 2가 되어야 한다.
  1. 또 남은 카드 중에서 첫 번째 카드를 가장 뒤로 옮기고... (3번 반복) 그리고 가장 앞에 있는 카드를 책상위에 올려놓는데 그것은 3이 된다.
  1. 또 남은 카드 중에서 첫 번째 카드를 가장 뒤로 옮기고.. (4번 반복) 그리고 가장 앞에 있는 카드를 책상 위에 올려놓는데 그것은 4이다.
  1. 위 과정을 계속 반복하여 N번 카드만 남을 때 까지 반복한다.
위와 같은 카드를 하려면 미리 카드의 순서를 알고 있어야 한다. 카드의 개수 N이 주어져 있을 때 위의 마술을 하기 위한 카드의 초기 순서를 구하는 프로그램을 작성하시오.

입력

첫 번째 줄에 카드의 개수 N(1 ≤ N ≤ 1,000)이 주어진다.

출력

첫 번째 줄부터 N번째 줄까지 차례로 카드의 순서를 출력한다.

풀이

N값으로 4를 입력했다고 생각해보자.
먼저 덱에 다음과 같이 값을 추가해보자.
[0, 1, 2, 3]
이 덱에서 맨 앞에 있는 값을 뒤로 보내는 시행을 주어진 규칙에 따라 반복한 후, 최종적으로 맨 앞에 남게 되는 값을 index 값으로 설정할 수 있다.
처음 시행이라면, [1, 2, 3, 0]과 같이 덱이 변경될 것이다. 이때의 첫번째 값인 1 번째 index로 하는 정답배열에 1 을 넣어보자. 물론, 이 덱에서 1은 제거해야 한다.
그럼 정답배열은 다음과 같을 것이다.
[?, 1, ?, ?]
두번째 시행도 생각해보자. 현재 덱은 [2, 3, 0]이다. 주어진 규칙에 따라 카드를 옮기고 나면 다음과 같이 덱이 남아있을 것이다.
[2, 3, 0] → [3, 0, 2] → [0, 2, 3]
마찬가지로 첫번째 값인 0 번째 index에 2를 넣는다. 그럼 정답배열은 다음과 같아진다.
[2, 1, ?, ?]
이를 반복하면 처음 카드의 순서를 찾을 수 있다.
 
로직을 일반화하여 다시 표현해보면 다음과 같다.
  1. 덱을 선언하고, 0부터 N-1까지의 인덱스를 덱에 추가한다.
  1. 인덱스가 저장되어 있는 덱에서 맨 앞에 있는 카드를 뒤로 보내는 시행을 단계 만큼 반복한 후, 그 다음 맨 앞에 있는 카드를 덱에서 제거하고, 제거한 카드의 인덱스 위치에 1부터 1씩 증가시키며 값을 배치한다.
  1. ans 배열에 저장된 숫자를 출력하면 주어진 조건에 맞는 카드의 초기 순서를 출력할 수 있다.
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.util.*; public class Main { public static void main(String[] args) throws IOException { StringBuilder sb = new StringBuilder(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int N = Integer.parseInt(br.readLine()); int ans[] = new int[N]; Deque<Integer> deque = new ArrayDeque<>(); for(int i=0; i<N; i++) { deque.addLast(i); } int value = 1; for(int i=0; i<N; i++) { for(int j=0; j<i+1; j++) { int f = deque.pollFirst(); deque.addLast(f); } int index = deque.pollFirst(); ans[index] = value++; } for(int i=0; i<N; i++) { sb.append(ans[i]).append(" "); } System.out.println(sb.toString().trim()); } }
결과
메모리
시간
맞았습니다!!
22596 KB
176 ms

댓글

guest