Python/Algorithm

스택, 큐, 덱

이니니 2022. 6. 12. 08:57

스택 ( stack )

스택은 데이터를 넣는 곳과 빠지는 곳의 위치가 같은 자료구조를 말한다. 즉, 맨 아래에 있는 데이터를 꺼내기 위해서는 그 위의 데이터를 모두 꺼내야 할 것이다. 이런 구조를 후입선출 구조(LIFO; Last In First Out)라고 부른다. 

stack은 크게 5가지 함수 사용이 가능하다.

1. push(x)  # x를 stack의 맨 위에 올려 놓는다.
2. size()  # stack 위에 쌓인 블럭의 개수를 반환한다.
3. empty()  # stack 위가 비어있다면 true, 비어있지 않다면 false를 반환한다.
4. top()  # stack의 맨 위에 있는 숫자 값을 반환한다. 단, stack에서 그 블럭을 제거하지는 않는다.
5. pop()  # stack의 맨 위에 있는 숫자 값을 반환한다. 단, 동시에 stack에서 그 블럭을 제거한다.

스택에서 데이터를 넣고 빼는 작업은 다른 데이터에 영향을 주지 않는다.

따라서, 스택 자료구조 또한 삽입과 삭제 연산의 시간복잡도는 O(1)이라고 볼 수 있다. 

스택은 쌍을 이루고 있는 것들이 올바르게 배치되어 있는지 확인할 때 유용하게 사용할 수 있다.

배열을 스택처럼 사용한다면, 일반적인 스택과 달리 중간 데이터를 확인할 수 있다는 엄청난 장점이 생긴다. 그렇지만 배열을 생성할 때 정의한 길이보다 스택에 들어가는 값이 더 많다면 문제가 생길 수 있다는 단점이 생긴다.

function push(arr, E)
  if arr.size == maxsize          // 배열에 이미 원소들이 가득 채워져 있다면,
    throw exception()             // 에러 메세지를 던져줍니다.
  arr.append(E)                   // 정상적인 상황이라면, E를 
                                  // 마지막 위치에 추가해줍니다.

function pop(arr) 
  if arr.size == 0                // 배열에 아무런 원소도 없다면
    throw exception()             // 에러 메세지를 던져줍니다.
  set last = arr[arr.size - 1]    // 정상적인 상황이라면, 마지막 값을 변수에 저장해두고
  delete arr[arr.size - 1]        // 맨 끝에 있는 값을 실제로 제거한 뒤
  return last                     // 마지막에 있었던 값을 반환해줍니다.

 

python에는 stack이라는 class가 따로 없다. 따라서, stack 자료구조를 사용하기 위해서는 직접 다음과 같이 class를 만들어주어야한다.

class Stack:
    def __init__(self):          # 빈 스택 하나를 생성합니다.
        self.items = []
                
    def push(self, item):        # 스택에 데이터를 추가합니다.
        self.items.append(item)
                
    def empty(self):             # 스택이 비어있으면 True를 반환합니다.
        return not self.items
                
    def size(self):              # 스택에 있는 데이터 수를 반환합니다.
        return len(self.items)
        
    def pop(self):               # 스택의 가장 위에 있는 데이터를 반환하고 제거합니다.
        if self.empty():
            raise Exception("Stack is empty")
            
        return self.items.pop()
                
    def top(self):               # 스택의 가장 위에 있는 데이터를 제거하지 않고 반환합니다.
        if self.empty():
            raise Exception("Stack is empty")
                        
        return self.items[-1]

또한, 재귀함수는 먼저 호출된 함수가 나중에 종료되어야 하므로, 스택이 표현하기에 적절한 자료구조이다.

 

문제

괄호 문자열은 두 개의 괄호 기호인 ' ( ' 와 ' ) ' 만으로 구성되어 있는 문자열 입니다. 괄호 문자열은 다음과 같이 2가지로 분류할 수 있습니다.

  1. 올바른 괄호 문자열 : 괄호를 열고 닫음이 분명한 문자열을 말합니다.

ex)

"()"

"(())"

"(()())"

  1. 올바르지 못한 괄호 문자열 : 괄호를 열고 닫음이 분명하지 못한문자열을 말합니다.

ex)

"(("

"(()(

"(((())"

입력으로 주어진 괄호 문자열이 올바른지, 그렇지 못한지를 판단하여 결과를 출력하는 프로그램을 작성하여 보세요.

import sys

class Stack:
    def __init__(self):          # 빈 스택 하나를 생성합니다.
        self.items = []
                
    def push(self, item):        # 스택에 데이터를 추가합니다.
        self.items.append(item)
                
    def empty(self):             # 스택이 비어있으면 True를 반환합니다.
        return not self.items
                
    def size(self):              # 스택에 있는 데이터 수를 반환합니다.
        return len(self.items)
        
    def pop(self):               # 스택의 가장 위에 있는 데이터를 반환하고 제거합니다.
        if self.empty():
            raise Exception("Stack is empty")
            
        return self.items.pop()
                
    def top(self):               # 스택의 가장 위에 있는 데이터를 제거하지 않고 반환합니다.
        if self.empty():
            raise Exception("Stack is empty")
                        
        return self.items[-1]


# 변수 선언 및 입력:
string = input()
s = Stack()

for char in string:
    if char == '(':
        s.push('(')
    else:
        if s.empty():
            print("No")
            sys.exit(0)

        s.pop()

if s.empty():
    print("Yes")
else:
    print("No")

큐 ( Queue )

큐는 선입선출 구조(FIFO; First In First Out) 이다.

queue는 크게 5가지 함수 사용이 가능하다.

1. push(x)  # x를 queue의 맨 뒤에 추가한다.
2. size()  # queue 안에 들어있는 데이터의 개수를 반환한다.
3. empty()  # queue 안에 남아있는 데이터가 없다면 true, 있다면 false를 반환한다.
4. front()  # queue의 맨 앞에 있는 숫자 값을 반환한다. 단, queue에서 해당 데이터를 제거하지는 않는다.
5. pop()  # queue의 맨 앞에 있는 숫자 값을 반환한다. 단, 동시에 queue에서 해당 데이터를 제거한다.

비록 데이터의 삽입과 삭제가 동일한 장소에서 수행되지는 않지만, 다른 값들의 이동이 필요하지 않기 때문에 여전히 삭제와 삽입의 시간복잡도는 으로 동일하다.


덱 ( Deque )

큐와 스택은 각각의 상황에 맞춰서 값을 넣고 뺄 수 있지만, 그외의 경우는 그러지 못한다는 단점이 있다. 이런 문제를 해결하기 위해 스택과 큐의 특성을 합친 새로운 자료구조는 덱이다.

덱은 스택과 큐와 달리 맨 앞/뒤에서 삽입/삭제가 모두 가능하며, 배열과 달리 네 메소드의 시간복잡도가 모두 다. 예를 들어 연결리스트를 사용하여 덱을 구현하면 양쪽 끝에서 삽입 삭제하는데 걸리는 시간이 이 되기 때문에 이러한 시간복잡도를 갖는 덱이라는 자료구조를 만들어낼 수 있다.

 

deque를 이용할 때 자주 사용되는 것은 다음과 같다.

python에서는 deque라는 package가 있다. deque는 collections라는 모듈에 있으며, 사용을 위해서는 from collections import deque를 가장 위에 적어줘야 한다.

dq = deque()로 정의되어있다는 가정하에 다음과 같이 이용해보자

1. dq.appendleft(a)  # 맨 앞에 데이터 a를 추가한다.
2. dq.append(a)  # 맨 뒤에 데이터 a를 추가한다.
3. dq.popleft()  # 맨 앞에 있는 데이터를 제거하며, 해당 원소의 값을 반환한다.
4. dq.pop()  # 맨 뒤에 있는 데이터를 제거하며, 해당 원소의 값을 반환한다.
5. len(dq)  # 현재 deque에 들어있는 데이터의 수를 반환한다.
6. if dq  # 현재 deque가 비어있는지 않은지 확인한다.
7. if not dq  # 현재 deque가 비어있는지 확인한다.
8. dq[0]  # deque의 맨 앞에 있는 데이터를 반환한다.
9. dq[-1]  # deque의 맨 뒤에 있는 데이터를 반환한다.

 

'Python > Algorithm' 카테고리의 다른 글

그래프  (0) 2022.06.18
트리  (0) 2022.06.18
이진탐색  (0) 2022.06.12
정렬의 종류 ( Python / 파이썬 )  (0) 2022.06.09
점근적 표기법  (0) 2022.06.04