next permutation이란?
보통 순열과 조합을 구현할 때에는 재귀 함수를 사용하여 구현한다.
하지만 재귀적인 함수의 호출은 구하고자 하는 범위가 넓어질 경우 사용하기 힘들다는 단점이 있다.
이런 단점을 해결할 수 있는 것이 next permutation이라는 알고리즘이다.
단순 일차원 탐색을 통해서 순열, 조합을 구할 수 있다는 장점이 있다.
반복문을 통해서 값을 구하기 때문에 원소의 개수가 N이라 할 때 O(N) 만큼의 시간 복잡도를 가진다.
값을 바꿔주는 swap 과정을 거쳐서 순열을 생성하기 때문에 자연스럽게 순열의 순서도 파악할 수 있다는 장점이 있다.
알고리즘 진행
1. 아래와 같은 배열이 주어졌을 때 배열의 맨 뒤에서부터 탐색을 시작하여 N[index] < N[index+1]인 가장 마지막 인덱스를 구한다.
위의 예시로 들면, 뒤에서부터 탐색을 시작하여 N[index] < N[index+1]을 만족하는 index를 pivotIndex라 한다.
여기서 pivotIndex 값은 2이다.
만약 pivotIndex가 음수가 나온다면, 뒤에 값이 앞의 값보다 큰 게 없다는 의미가 되므로, 마지막 순열임을 의미한다.
2. pivotIndex의 바로 뒤 인덱스부터, 맨 끝까지 탐색을 진행한다. 이 때, N[pivotIndex]의 값인 1보다 큰 값을 가지면서 가장 뒤에 있는 인덱스를 구한다. 이 인덱스를 j 라 하겠다.
위의 예시에서, 1보다 큰 값을 가지면서 가장 뒤에있는 인덱스는 맨 끝인 4이다. 따라서 j값은 4가 된다.
3. N[j] 가 N[pivotIndex] 보다 큰 값을 가진다는 것은 보장되어있는 상태이다. 따라서 N[j]와 N[pivotIndex]의 값을 교환해준다.
4. pivotIndex+1 부터, 맨 뒤까지 순서를 거꾸로 하는 reverse 작업을 진행해준다. 마지막 과정으로 나온 순열이 바로 그다음 순열이 된다.
문제 풀이
위 로직을 바탕으로, 구현은 해당 사이트 를 참고했다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class N10972 {
// 다음 순열
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());
StringTokenizer st = new StringTokenizer(br.readLine());
int[] givenArray = new int[N];
int idx = 0;
while(st.hasMoreTokens()) {
givenArray[idx++] = Integer.parseInt(st.nextToken());
}
findNextPermutation(givenArray);
}
public static void findNextPermutation(int[] givenArray) {
// 주어진 배열의 크기가 비어있거나, 원소가 1개인 경우 next_permutation은 불가능하다.
if(givenArray.length <= 1) {
System.out.println("-1");
return;
}
// index + 1을 할 것이기 때문에, 마지막 인덱스인 givenArray.length - 1 을 넘지 않기 위해 -2 를 마지막 인덱스로 설정해준다.
int pivotIndex = givenArray.length - 2;
// 교체할 값인 pivotIndex를 찾지 못했을 경우, pivotIndex는 음수가 되며 while문을 나오게 된다.
while(pivotIndex >= 0) {
if(givenArray[pivotIndex] < givenArray[pivotIndex+1]) {
break;
}
pivotIndex--;
}
// pivotIndex가 0보다 작을 경우, 다음 순열이 없는 마지막 순열이 된다.
if(pivotIndex < 0) {
System.out.println("-1");
return;
}
int nextIndex = givenArray.length - 1;
// 가장 마지막 원소부터, N[i] < N[i+1]을 만족하는 index 값보다 큰 인덱스인 nextIndex를 구한다.
for(int i=nextIndex; i > pivotIndex; i--) {
if(givenArray[i] > givenArray[pivotIndex]) {
nextIndex = i;
break;
}
}
// nextIndex와 pivotIndex의 값을 교체한다.
int tmp = givenArray[nextIndex];
givenArray[nextIndex] = givenArray[pivotIndex];
givenArray[pivotIndex] = tmp;
// pivotIndex 오른쪽에 있는 값부터 마지막 인덱스까지 오름차순 정렬한다.
int start = pivotIndex + 1;
int end = givenArray.length - 1;
while(start < end) {
int swapTmp = givenArray[start];
givenArray[start] = givenArray[end];
givenArray[end] = swapTmp;
start += 1;
end -= 1;
}
for(int value : givenArray) {
System.out.print(value + " ");
}
}
}
'study > algorithm' 카테고리의 다른 글
그래프 이론 - BFS/DFS 구현해보기 (0) | 2022.04.22 |
---|---|
이진 검색트리(Binary Search Tree) 구현해보기 (0) | 2022.04.16 |
선형 자료구조 - 배열/연결 리스트/큐/스택 (0) | 2022.04.02 |