Permutation(순열)

1. next_permutation

c++에는 algorithm헤더에 매개변수의 배열/iterator의 다음 순열을 적용시켜 바뀌었다면 true/false를 반환해주는 메서드가 존재해서 이를 do~while문으로 쉽게 순열문제를 해결할 수 있다.

하지만, 자바는 존재하지 않기 때문에 다음과 같이 구현할 수 있다.

public boolean next_permutation(int[] arr){
    //뒤에서부터 탐색해서 내림차순이 깨지는 지점(a) 찾기
    int a = arr.length-2;
    while(a>=0 && arr[a] >= arr[a+1]) a--;
    if(a < 0) return false; //a가 -1이라면 배열전체가 내림차순정렬된 상태로 순열탐색이 끝난다.

    //뒤에서부터 탐색하면서 a보다 큰 첫번째 인덱스(b) 찾기
    int b = arr.length-1;
    while(arr[a] >= arr[b])b--;
    //찾은 a와 b swap
    swap(arr,a,b);

    //a+1부터 끝까지 오름차순으로 재정렬
    a += 1;
    b = arr.length-1;
    while(a< b){
        swap(arr,a++,b--);
    }
    return true;
}

private void swap(int[] arr, int src, int target){
    int tmp = arr[src];
    arr[src] = arr[target];
    arr[target] = tmp;
}
@Test
void test(){
    int[] arr = new int[]{1,2,3,4};

    do{
        System.out.println(Arrays.toString(arr));
    }while(next_permutation(arr));
}

2. swap

구현하기가 가장 쉬워 보통 알고리즘문제에 순열이 등장할때 많이쓰는 방식중 하나로 재귀함수를 이용한 백트래킹을 이용한 방법이다.

public void permutation(int[] arr, int depth, int n,int r){
    if(depth == r){
        System.out.println(Arrays.toString(arr));
        return;
    }

    for(int i=depth; i < n; i++){
        swap(arr,depth,i);
        permutation(arr, depth+1,n,r);
        swap(arr,depth,i);
    }
}
  private void swap(int[] arr, int src, int target){
    int tmp = arr[src];
    arr[src] = arr[target];
    arr[target] = tmp;
}

@Test
void test(){
    int[] arr = new int[]{1,2,3,4};
    permutation(arr,0,4,4);
}

3. visited

swap과 구현코드는 비슷하기 때문에 구현하는 것은 쉽게 할 수 있다. 하지만 swap과 다르게 배열의 앞에서부터 값을 쌓아나가는 방식으로 List를 이용해 값을 쌓아 나갈 수도 있고 depth가 r과 같아질때가 아니더라도 조건처리하기가 훨씬 쉽다.

if(visited[i]) continue; 부분만 지워주면 중복순열(자기자신을 포함)을 구할수 있다.

public void permutation(int arr[],int tmp[],boolean[] visited,int depth, int n, int r){
    if(depth == r){
     System.out.println(cnt++ + " " + Arrays.toString(tmp));
        return;
    }
    for(int i=0; i < n; i++){
        if(visited[i])continue;
        visited[i] = true;
        tmp[depth] = arr[i];
        visitedPermutation(arr,tmp,visited,depth+1,n,r);
        visited[i] =false;
    }
}

public void listPermutation(int arr[], List<Integer> tmp, boolean[] visited, int depth, int n, int r){
    if(depth == r){
        System.out.println(tmp.toString());
        return;
    }
    for(int i=0; i < n; i++){
        if(visited[i])continue;
        visited[i] = true;
        tmp.add(arr[i]);
        listPermutation(arr,tmp,visited,depth+1,n,r);
        visited[i] =false;
        tmp.remove(tmp.size()-1);
    }
}
public void rePermutation(int arr[],int tmp[],boolean[] visited,int depth, int n, int r){
    if(depth == r){
     System.out.println(Arrays.toString(tmp));
        return;
    }
    for(int i=0; i < n; i++){
        visited[i] = true;
        tmp[depth] = arr[i];
        visitedPermutation(arr,tmp,visited,depth+1,n,r);
        visited[i] =false;
    }
}

@Test
void test(){
    int[] arr = new int[]{1,2,3};
    int[]tmp = new int[3];
    List<Integer> list = new ArrayList<>();
    boolean[] visited = new boolean[3];
    System.out.println("---Array---");
    permutation(arr,tmp,visited,0,3,3);
    System.out.println("----List---");
    listPermutation(arr,list,visited,0,3,3);
    System.out.println("--중복순열--");
    rePermutation(arr,tmp,visited,0,3,3);
}

Related Posts

I/O

I/O

  • Java
  • 2021년 2월 9일

컴퓨터의 5대 기능인 입력/출력/연산/저장/제어 중 입력(Input)과 출력(Ouput)을 줄여 I/O라고 말한다. 1. 스트림 / 버퍼 / 채널 기반의 I/O 1) 스트림 입출력을 도와주는 모듈로써 Stream이라는 단어 그대로 흐름을 의미하며, 한 방향으로만 진행하는 단방향통신이다. 2) 버퍼 일종의 데이터 공간으로 메모리간, 컴퓨터와 사용자간의 속도차이로 생기는 병목현상을 줄이기 위한 공간...

Read More
CustomContext 생성하기

CustomContext 생성하기

GraphQL의 요청을 핸들링하는 GraphQLServletContextBuilder를 implements하여 grpahQL요청에 대해 커스텀Context를 반환하도록 만들 수 있다. 예를들어 요청의 헤더에 접근하여 Context에 특정 헤더값을 저장하는 식으로의 custom이 가능하다. 이번 예시에서는 헤더에 api-key가 포함되어있고 이 key를 분리하여 context도 저장하고있는 context를 만들어보려고 한다....

Read More
연산자

연산자

  • Java
  • 2021년 1월 23일

백기선님의 유튜브 로 진행하시는 스터디를 진행하며 올리는 정리 블로그입니다. 산술 연산자 두개의 피연산자를 갖는 이항 연산자로써, 기본적인 사칙연산을 다루는 연산자 ◾ 더하기 (+) 왼쪽의 피연산자에 오른쪽 피연산자를 더하는 연산자로 숫자+숫자, 문자열+문자열이 가능하고 문자열+숫자를 할 시 숫자를 자동으로 문자열로 변환하여 덧셈이 가능하다. 문자+숫자를 할 경우에는 아스키 코드를 이용하여 문자로 결과가 출력 된다. 문자에 맞는 아스키 코드값과 숫자를 더한 결과값에 해당하는 아스키코드를 return하기 때문이다....

Read More