본문 바로가기

CS/Algorithm

스택(Stack)

🥪

Stack

목표
  • 스택(Stack)의 기본 연산을 이해한다.
  • Kotlin과 Python으로 Stack을 구현할 수 있다.

📖개념


한쪽 끝에서만 자료를 넣고 뺼 수 있는 LIFO(Last In First Out) 형식의 자료구조

🧮연산


스택(Stack)은 LIFO를 따른다.

즉, 가장 최근에 스택에 추가한 항목이 가장 먼저 제거될 항목이다.

  • pop(): 스택에서 가장 위에 있는 항목을 제거한다.
  • push(item): item 하나를 스택의 가장 윗 부분에 추가한다.
  • peek(): 스택의 가장 위에 있는 항목을 반환한다.
  • isEmpyty(): 스택이 비어있을 때 true를 반환한다.

⚒️구현


문제의 종류에 따라 배열보다 스택에 데이터를 저장하는 것이 더 적합한 방법일 수 있다.

  • 배열과 달리 스택은 상수시간(O(1)에 i번째 항목에 접근할 수 없다.
    • 배열처럼 원소들을 하나씩 옆으로 밀어 줄 필요가 없다.
  • 하지만, 스택에서 데이터를 추가하거나 삭제하는 연산은 상수 시간에 가능하다.

스택은 연결리스트(Linked List)로 구현할 수 있다.

연결리스트의 같은 방향에서 아이템을 추가하고 삭제하도록 구현한다.

kotlin Code

package com.parade621.stack_kotlin

import java.util.NoSuchElementException

class MyStack(val num: Int){

    private var top: StackNode?

    init{
        val t: StackNode = StackNode(num)
        top = t
    }

    // linked list
    private class StackNode(val data: Int) {
        var next: StackNode? = null
    }

    fun pop(): Int {
        if (top == null) throw NoSuchElementException()
        val item: Int = top!!.data
        top = top!!.next
        return item
    }

    fun push(item: Int) {
        val t: StackNode = StackNode(item)
        t.next = top
        top = t
    }

    fun peek(): Int {
        if (top == null) throw NoSuchElementException()
        return top!!.data
    }

    val isEmpty: Boolean
        get() = top == null
}

fun main(){

    var stack = MyStack(0)
    stack?.push(1)
    stack?.push(5)
println(stack?.pop())
println(stack?.pop())
println(stack?.pop())
println(stack?.isEmpty)
}

타입을 제네릭을 사용해서 구현해 보고 싶었는데, 아직 제네릭을 원활하게 사용하지 못해서 그냥 Int 타입의 클래스를 만들었다.

  • 위 코드를 제네릭으로 작성한 것
    package Basic
    
    import java.util.NoSuchElementException
    
    class MyStack<T>(val num: T){
    
        private var top: StackNode<T>?
    
        init{
            val t: StackNode<T> = StackNode(num)
            top = t
        }
    
        // 링크드 리스트
        private class StackNode<T>(val data: T) {
            var next: StackNode<T>? = null
        }
    
        fun pop(): T {
            if (top == null) throw NoSuchElementException()
            val item: T = top!!.data
            top = top!!.next
            return item
        }
    
        fun push(item: T) {
            val t: StackNode<T> = StackNode<T>(item)
            t.next = top
            top = t
        }
    
        fun peek(): T {
            if (top == null) throw NoSuchElementException()
            return top!!.data
        }
    
        val isEmpty: Boolean
            get() = top == null
    }
    
    fun main(){
    
        var stack = MyStack<Int>(0)
        stack?.push(1)
        stack?.push(5)
        println(stack?.pop())
        println(stack?.pop())
        println(stack?.pop())
        println(stack?.isEmpty)
    }

python Code

파이썬에서는 그냥 List를 스택처럼 사용하는 것이 가장 편한 듯 합니다.

s = []
s.append("Hello, World!")
s.append("안녕하세요!")
print(s[-1]) # 안녕하세요!
s.pop()
print(s[-1]) # Hello, World!
if s:
    print("Stack is not Empty") # Stack is not Empty

🔡사용 사례


후입선출이 필요한 경우에 스택 자료구조는 유용하게 사용된다.

  • 재귀 알고리즘(Recursive Algorithm)
    • 재귀적으로 함수를 호출해야 하는 경우에 임시 데이터를 스택에 넣어준다.
    • 재귀 함수를 빠져나와 퇴각 검색(Backtrack)을 할 때는 스택에 넣어 두었던 임시데이터를 빼줘야한다.
    • 스택은 이러한 일련의 과정을 직관적으로 가능하게 해준다.
    • 또한 스택은 재귀 알고리즘을 반복적 형태(iterative)를 통해서 구현할 수 있게 해준다.
  • 탐색 알고리즘에서 깊이 우선 탐색(DFS) 알고리즘 구현에 사용.
  • 웹 브라우저의 방문기록(뒤로가기)
  • 실행 취소(Undo)
  • 역순 문자열 만들기
  • 수식의 괄로 검사(연산자 우선순위 표현을 위한 괄호 검사)
    • ex) 올바른 괄호 문자열(VPS, Valid Parenthesis String) 판단하기
    • 괄호 짝 맞추기
  • 후위 표기법 계산(Postfix)


Uploaded by N2T

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

유클리드 호제법(Euclidean algorithm)  (2) 2023.10.14
N진법  (1) 2023.10.04
힙(Heap)  (0) 2023.03.01
큐(Queue)  (0) 2023.02.27
시간 복잡도와 공간 복잡도  (0) 2022.12.31