알고리즘/풀이

leetCode 46. Permutations

민-민 2023. 1. 29. 23:20

https://leetcode.com/problems/permutations/description/

 

Permutations - LeetCode

Permutations - Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.   Example 1: Input: nums = [1,2,3] Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] Example 2: Input: nums = [0

leetcode.com

 

Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.

Input: nums = [1,2,3]
Output: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

 

주어진 숫자의 조합을 생성하는 문제이다.

 

숫자별로 탐색하며 만들거나, 아니면 itertools 라이브러리를 사용한다.

 

[로직]

# 주어진 리스트 숫자별로 시작하는 순열을 탐색한다.

# 이미 탐색한 숫자를 제외하고 다음 탐색할 리스트를 만든다. (next_elements)

# 탐색한 숫자는 다른 리스트에 저장한다. (prev_elements)

# 다음 탐색할 next_elements를 가지고 dfs를 시작한다.

# 처음 주어진 elements 를 다 탐색해서 받은 다음에 탐색할 리스트 개수가 0이 되면,

prev_elements에는 elements의 전체 숫자가 탐색한 순서대로 들어 있다. 

results 리스트에 prev_elements를 복사해서 추가한다. (리스트 내 리스트로)

# 끝까지 다 돌고 나왔으면 하나 위 단계(트리로 치면 부모 노드)에서 선택하지 않은 숫자(트리로 치면 노드)로 가서 탐색한다. prev_elements에서 제일 끝 숫자를 pop 한다. (이미 방문한 자식노드 기록을 없앤 채로 같은 레벨의 다른 자식노드부터 시작)

# 제일 부모 리스트를 끝까지 다 돌았으면 전체 탐색 끝. 

# 전체 탐색한 결과 반환

 

[ 방문 과정 ]

#1

elements = [1, 2, 3]

e = 1

next_elements = [2, 3]

prev_elements = [1]  ---> prev_elements는 dfs 밖에 선언되어 모든 dfs 재귀로 도는 애들이 같은 리스트를 바라본다.

dfs 1-2 호출

 

#1-2

elements = [2, 3]

e = 2

next_elements = [3]

prev_elements = [1, 2]

dfs 1-3 호출

 

#1-3

elements = [3]

e = 3

next_elements = []

prev_elements = [1, 2 ,3]

dfs 1-4 호출

 

#1-4

elements = []

results.append([1,2,3])

 

--> 더 수행할 게 없으므로 dfs 1-3으로 돌아감

 

#1-3 

여기서 주어진 리스트 elements에서 3 말고 다른 선택지가 있는지 확인

이미 방문한 노드 prev_elements에서 가장 마지막에 방문한 3을 뺌

prev_elements = [1,2]를 가지고, dfs 1-3에서 3 말고 방문할 수 있는 노드 확인

없음

 

-> dfs 1-2로 돌아감

 

#1-2

주어진 리스트 elements에서 2를 방문하지 않았을 때의 경우로 탐색 들어가야 함

prev_elements에서 2를 뺌

elements = [2, 3] 을 가지고, 다음 탐색 숫자가 2가 아닌 경우 (다음 for문) 탐색

elements = [2, 3]

e = 3

next_elements = [2]

prev_elements = [1, 3]

dfs 1-3 호출

..

 

 

dfs를 끝까지 가고 재귀 반환되어 부모 노드로 돌아간 다음에는, 이미 방문한 자식노드 말고

다음 자식노드부터 시작했을때의 경우를 탐색해야 함.

이미 방문한 리스트에서, 기존 방문했던 자식노드를 뺀 다음, 새로 방문하는 자식노드를 추가해서 탐색을 시작함.

 

반복하다가 제일 부모 노드에서 전체 자식을 다 탐색하고 나면

모든 자식노드를 끝까지 탐색했을때의 순서를 가진 results가 생김.

 

result를 반환하면, 전체 가능한 순열 리스트임

 

#leetcode 46.Permutations
from typing import List

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        results = []
        prev_elements = []

        def dfs(elements):
            if len(elements) == 0:
                results.append(prev_elements[:])

            for e in elements:
                next_elements = elements[:]
                next_elements.remove(e)

                prev_elements.append(e)
                dfs(next_elements)
                prev_elements.pop()

        dfs(nums)
        return results

    #        return list(map(list, itertools.permutations(nums)))

solution = Solution()
print(solution.permute([1,2,3]))

 

아니면 itertools 모듈을 사용해서 간단하게 구현할 수 있다.

C라이브러리로 구현되었으므로 속도도 빠르고 이미 잘 구현되어 만들어서 쓰는 것보단 문제생길 일도 없다.

 

itertools.permutations(리스트)

로 하면 (1,2,3), (1,3,2) , .. 로 튜플 모음이 생성된다.

리스트 반환하라고 문제에 정의 되어 있으므로 map을 사용하여 list로 변환해준뒤

다시 리스트로 감싸서 반환한다.

return list(map(list, itertools.permutations(nums)))

 

 

 

# 파이썬 알고리즘 인터뷰 책 참고