https://leetcode.com/problems/next-permutation/
Next Permutation - LeetCode
Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.
leetcode.com
아 진짜 permutation 문제는 너무 어렵다.. 내가 수학쪽에 약해서 그런가 ㅋㅋ..
이것까지만 풀고 회사 일 시작해야겠다.
이 문제는 시퀀스가 주어졌을 때 그 시퀀스의 다음 permutation을 찾는 문제다.
가령 1,2,3 이 주어지면 1,3,2를 정답으로 내놓아야되고 2,3,1이 주어지면 3,1,2를 내놓아야 되는 식이다.
근데 이게 참 까다로운게 조건이 붙어서다. 그냥 쌩노가다면 누구나 하지. 조건은 O(1)의 공간복잡도다. 당연히 정답리스트를 따로 만들면 안되고 인자로 주어진 리스트를 in-place로 수정해야된다. ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ예아
일단 1~4의 숫자를 가지고 permutation을 나열해보자. 규칙을 찾아야되는 문제니까.
1234
1243
1324
1342
1423
1432
2134
2143
2314
2341
...
4123
4132
4213
4231
4312
4321
일단 눈에 보이는 규칙은, 각 자리의 숫자가 작은 숫자부터 시작해서 점점 커진다는 것이다.
가령 첫 번째 자리 숫자는 1->2->3->4로 바뀌고, 첫 번째 숫자가 fix되어 있을 때 그 다음인 두 번째 자리 숫자는 2 -> 3 -> 4, 두 번째 숫자가 fix되어 있을 때 세 번째 자리 숫자는 3 -> 4.. 이런식이다.
특정 시퀀스가 주어졌을 때, 다음 시퀀스에서 몇 번째 자리 숫자가 바뀌어야 되는지는 어떻게 알 수 있을까?
실마리는 숫자의 '순서'에 있다. 위 permutation 시퀀스를 다시보자. 힌트를 드리겠다
123/4
12/43
132/4
13/42
142/3
1/432
213/4
21/43
231/4
23/41
..
저 슬래시를 무슨 기준으로 넣었는지랑 그 다음에 어떤 자리 숫자가 바뀌는지를 유심히 보자.
아직 규칙을 못 찾았는가?
극단적인 케이스를 살펴보자. (역시 규칙 찾을 땐 '극단'이 최고야)
가장 마지막 시퀀스는 뭘까? 4321이다. 4321이 주어졌을 때 이게 가장 마지막 시퀀스라는건 어떻게 알 수 있을까?
모든 숫자가 내림차순으로 쭉 나열되어있다! 그럼 가장 마지막 시퀀스인 것이다. 그럼 그 담엔 모든 숫자가 오름차순으로 나열되어있는 첫 시퀀스로 다시 돌아가야겠지.
자 다시! 핵심은 4개의 숫자의 시퀀스의 맨 끝은 그 숫자들이 모두 내림차순으로 나열되어 있는 경우라는 것이다.
그럼 첫 번째 숫자가 픽스된 상태일 때(가령, 1일 때) 마지막 시퀀스는 무엇일까? 1432이다. 1을 제외한 나머지 숫자들이 내림차순으로 나열된 것이다.
다시보자
123/4
12/43
132/4
13/42
142/3
1/432
213/4
21/43
231/4
23/41
굵은 글씨 친 부분의 순서가 어떻게 되어있는가?
내림차순이다!!
right-to-left 스캔을 해서 내림차순인 부분을 찾고나면 그 다음은 쉽다. 내림차순인 부분의 바로 왼쪽 숫자 (142/3의 경우 2, 21/43의 경우 1) 가 바뀔 차례다. 어떤 것으로 바뀔까? '내림차순인 부분의 바로 왼쪽 숫자'보다 큰 숫자로 바뀐다. 그 숫자는 내림차순인 부분에서 찾을 수 있다. 내림차순인 부분에 있는 숫자들 중에서 '내림차순인 부분의 바로 왼쪽 숫자'의 다음으로 큰 숫자를 찾으면 된다. 가령 21/43에서는 3,4중에 1 다음으로 큰 숫자는 3이므로 그 다음 시퀀스에서 1은 3으로 바뀌어야 한다. 실제로 2143 다음의 시퀀스는 2314로, 1이 3으로 바뀐 것을 확인할 수 있다.
정리해보자.
2143을 예시로 들겠다
1. 시퀀스를 '내림차순이 아닌 부분' / '내림차순인 부분'으로 나눈다.
2,1 / 4,3
2. 이걸 편의상 FIXED / DESC 파트라고 부르겠다.
FIXED = 2,1
DESC = 4,3
3. FIXED의 맨 오른쪽 끝 숫자를 보자. DESC에 있는 숫자들 중에서 FIXED오른쪽끝숫자 바로 다음으로 큰 숫자를 찾는다.
이 때, DESC 파트를 정렬을 해야 한다. 그래야 바로 다음으로 큰 숫자를 찾을 수 있다.
FIXED = 2,1
DESC = 4,3 -> 정렬 -> 3,4
2,1 / 3,4
4. FIXED의 맨 오른쪽 끝 숫자랑 DESC에 있는 숫자들 중에서 그보다 바로 큰 숫자를 스왑한다.
2, 1 / 3, 4 -> 2, 3 / 1, 4
아 근데 이게 in-place로 작업을 해야 되다 보니까 정렬을 내가 따로 구현해줘야되는 단점이..
제엔장! 퀵정렬 구현을 쉽게 하려면 이 글 참고 https://keep-your-pace.tistory.com/22
class Solution:
def quicksort(self, array, start, end):
...어찌저찌 퀵정렬이 구현되어 있다 가정. 구현은 위 링크 참고
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if len(nums) == 1:
return
fixed_end = len(nums) - 1
# fixed 파트의 오른쪽 끝 부분
# right-to-left 스캔을 해야되니까 리스트의 맨 끝 인덱스로 초기화한다
#fixed 파트의 오른쪽 끝을 찾는다
while fixed_end > 0:
if nums[fixed_end - 1] < nums[fixed_end]:
break
fixed_end -= 1
fixed_end -= 1
#모든 요소가 내림차순이면 시퀀스 끝이란 뜻
if fixed_end == -1:
nums.sort()
return
#정렬한다.
self.quicksort(nums, fixed_end + 1, len(nums) - 1)
#fixed_end 오른쪽 끝숫자보다 바로 다음으로 큰 숫자를 찾는다
nextnum = fixed_end
while nums[nextnum] <= nums[fixed_end]:
nextnum += 1
#fixed_end 오른쪽 끝숫자랑 위에서 찾은 숫자랑 스왑한다.
tmp = nums[nextnum]
nums[nextnum] = nums[fixed_end]
nums[fixed_end] = tmp
참고로 이 문제는 다른 사람들의 풀이를 찾아봐도 짧고 단순하고 간결한 코드는 찾기 힘들다... 내가 잘했다는 건 아니지만 이 정도면 나쁘지 않은 것 같다.
'코딩 테스트 및 알고리즘 > leetcode for google' 카테고리의 다른 글
leetcode medium : Spiral Matrix (0) | 2022.05.06 |
---|---|
leetcode : word break 2 (0) | 2022.03.22 |
leetcode : Permutations II (0) | 2022.03.03 |
leetcode : Permutations [backtracking] 모든 순열 찾기 (0) | 2022.03.02 |
leetcode hard : wildcard matching (0) | 2022.03.01 |