부부의 코딩 성장 일기

LeetCode 71(Simplify Path, Python) 본문

Algorithm/LeetCode

LeetCode 71(Simplify Path, Python)

펩시_콜라 2024. 1. 18. 19:00

1. 문제 링크

 

Simplify Path - LeetCode

Can you solve this real interview question? Simplify Path - Given a string path, which is an absolute path (starting with a slash '/') to a file or directory in a Unix-style file system, convert it to the simplified canonical path. In a Unix-style file sys

leetcode.com

2. 문제 설명

  • '/'로 시작하는 절대경로 (absolute path) path라는 문자열이 주어졌을 때, 이를 canonical path로 변환하여 반환
    • Unix-style file system에서 '.'는 현재 경로를 의미하고, '..'는 현재의 상위 경로를 의미하고, //는 /와 동일하게 여겨진다.
    • 이 문제에서 '...'와 같은, 위를 제외한 포멧은 파일/경로명을 의미 
  • canonical path는 아래의 format을 따른다.
    • path가 '/'로 시작할 것
    • 두개의 경로는 '/'로 분리된다.
    • path는 '/'로 끝나지 않는다.
    • path는 root 경로에서 target 경로까지의 path 경로만을 포함한다. 
  • 예시1) path = "/home/" → output = "/home"
    • path가 /로 끝나지 않아야 하므로, 마지막 /는 지워져야 함
  • 예시2) path = "/.."/ → output = "/"
    • 루트 디렉토리는 최상위 디렉토리로, 더 이상 위로 올라갈 수 없음
  • 예시3) path = "/home//foo/" → output = "/home/foo"
    • //는 /와 동일하게 여겨지며 canonical path는 /로 분리가 되므로, /로 변경
  • 이렇게 까지 봐도 정확히 어떤 규칙인건지가 헷갈렸고, 결국 discussion에서 좀 더 문제를 잘 이해시켜주는 글을 발견했다.
    • 결국 '.'이면 continue, '..'는 이전 directory를 지우는 것, 그리고 //는 /로 replace하는 게 규칙으로 이해했다.
      • 여기서 아래 이미지의 5번은 누군가 reply를 달아주긴 했는데, output이 /home/usr/local/bin이 되는게 맞다.

 

3. 처음 풀이 

  • '..'를 마주쳤을 때, 그 직전 directory를 제외하는 것을 어떻게 해야 깔끔하게 짤 수 있을까 하다가, 
  • gpt에게 힌트를 구했는데.. 답을 알려주었다... (그래도 stack 자체로 다룬 문제는 오늘이 처음이라 답을 못찾았으면 좀 헤맬 뻔했다.)
  • stack이라는 자료구조에 대해서는 바로 아래에서 부연 설명할 예정
  • 우선 코드를 설명하면,
    • '//'는 바로 '/'로 replace를 시키고, path를 '/'를 기준으로 split시킨 리스트를 path_list로 저장해두었다.
    • 그리고 for문을 돌리면서, token이 ''이거나 '.'이면 stack에 쌓지 않고 continue를 하고, 
    • 만약 token이 '..'인데 stack에 값이 있으면 stack.pop을 해서 before directory를 제거하고, 
    • 그 이외의 케이스는 stack에 token을 append하는 구조로 작성되었다. 
    • 그릐고 마지막으로 '/'를 기준으로 join하여 output을 반환
class Solution:
    def simplifyPath(self, path: str) -> str:
        path = path.replace('//', '/')

        path_list = path.split('/')

        stack = []
        for token in path_list:
            if token == '' or token == '.':
                continue
            elif token == '..':
                if stack:
                    stack.pop()
            else:
                stack.append(token)

        output = '/' + '/'.join(stack)

        return output

 

4. stack 자료구조에 대한 설명

  • stack은 데이터를 저장하는 데 사용되는 선형 자료구조로, 데이터를 차곡차곡 쌓을 수 있는 구조. 
  • 데이터를 쌓는 동작과 꺼내는 동작을 제공하며 후입선출(LIFO, Last In First Out) 방식을 따름
    • 반대로 queue의 경우 FIFO(First In First Out) 방식 
  • stack은 주로 두가지 연산을 지원
    • push: 스택에 데이터를 추가하는 작업. 데이터는 스택의 맨 위에 추가됨
    • pop: 스택에서 데이터를 꺼내는 작업으로, 스택의 맨 위에 있는 데이터가 제거되고 반환 
  • 일상에서 비유하면, 책을 쌓거나 접시를 쌓는 것과 유사한 개념. 가장 최근에 쌓은 책이나 접시가 가장 먼저 꺼내지게 됨
  • 파이썬에서는 stack 자료형을 별도로 제공하진 않지만, 리스트가 사실 stack의 모든 연산을 지원
    • 결국 append()로 push연산을 하고, pop()으로 pop연산을 수행할 수 있음.
    • 함수 호출의 재귀적 동작, 브라우저의 뒤로가기 버튼과 같은 여러 상황에서 적용가능
  • 여기에서도 '..'를 마주치면, 그 이전에 쌓았던 것들 중 가장 마지막 것을 꺼내는 pop연산이 적용이 되고 있음

5. 배운 점

  • canonical path, stack

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

LeetCode 73(Set Matrix Zeros, Python)  (1) 2024.01.21
LeetCode 89(Gray Code, Python)  (0) 2024.01.20
LeetCode 64(Minimum Path Sum, Python)  (0) 2024.01.17
LeetCode 63(Unique Paths II, Python)  (0) 2024.01.16
LeetCode 62(Unique Paths, Python)  (0) 2024.01.15