반응형

* 핵심 개념

음수 가중치가 존재하는 경우 최단 거리를 찾기 때문에 다익스트라가 아닌 벨만포드 알고리즘을 사용해야 한다.

방향이 있는 유향그래프이기 때문에 1번 정점에서부터 시작해서

해당 정점을 갈 수 있는 최단 거리와 이전 정점까지 가중치 + 해당 정점 방문에 필요한 가중치를 더해

최소값을 저장해주면 된다.

* 문제

문제

N개의 도시가 있다. 그리고 한 도시에서 출발하여 다른 도시에 도착하는 버스가 M개 있다. 각 버스는 A, B, C로 나타낼 수 있는데, A는 시작도시, B는 도착도시, C는 버스를 타고 이동하는데 걸리는 시간이다. 시간 C가 양수가 아닌 경우가 있다. C = 0인 경우는 순간 이동을 하는 경우, C < 0인 경우는 타임머신으로 시간을 되돌아가는 경우이다.

1번 도시에서 출발해서 나머지 도시로 가는 가장 빠른 시간을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 도시의 개수 N (1 ≤ N ≤ 500), 버스 노선의 개수 M (1 ≤ M ≤ 6,000)이 주어진다. 둘째 줄부터 M개의 줄에는 버스 노선의 정보 A, B, C (1 ≤ A, B ≤ N, -10,000 ≤ C ≤ 10,000)가 주어진다. 

출력

만약 1번 도시에서 출발해 어떤 도시로 가는 과정에서 시간을 무한히 오래 전으로 되돌릴 수 있다면 첫째 줄에 -1을 출력한다. 그렇지 않다면 N-1개 줄에 걸쳐 각 줄에 1번 도시에서 출발해 2번 도시, 3번 도시, ..., N번 도시로 가는 가장 빠른 시간을 순서대로 출력한다. 만약 해당 도시로 가는 경로가 없다면 대신 -1을 출력한다.

* 소스 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.io.*;
import java.util.*;
 
public class Main {
    static int[] dist = new int[501];
 
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer stk = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(stk.nextToken());
        int m = Integer.parseInt(stk.nextToken());
        Edge[] edge = new Edge[m + 1];
 
        // 시작점에서 각 정점으로 가는 최단 거리 저장 배열 초기화
        for (int i = 1; i <= n; i++) dist[i] = Integer.MAX_VALUE;
 
        for (int i = 1; i <= m; i++) {
            stk = new StringTokenizer(br.readLine());
            int u = Integer.parseInt(stk.nextToken());
            int v = Integer.parseInt(stk.nextToken());
            int w = Integer.parseInt(stk.nextToken());
            edge[i] = new Edge(u, v, w);
 
        }
        dist[1= 0;        // 1번 정점이 시작점, 시작점까지의 최단거리는 0
        for (int i = 1; i < n; i++) {       // 정점의 수 - 1 번 수행
            for (int j = 1; j <= m; j++) {  // 모든 간선을 사용하여 최단거리가 줄어들면 정보 갱신
                Edge curr = edge[j];
                //u로 가는 최단거리가 바뀌고(무한이 아니고)
                //v로 가는 최단거리 > u까지 필요한 가중치(dist[curr.u]) + u->v 간선 가중치(curr.w)
                if (dist[curr.u] != Integer.MAX_VALUE && dist[curr.v] > dist[curr.u] + curr.w) {
                    dist[curr.v] = dist[curr.u] + curr.w;
                }
            }
        }
 
        // 음수 cycle 확인
        // 만약 음수 cycle이 없다면 시작점에서 모든 점으로 가는 최단거리는 갱신되어 있어야 한다.
        for (int j = 1; j <= m; j++) {
            // 만약 갱신되는 간선이 있다면 음수 cycle 존재
            if (dist[edge[j].u] != Integer.MAX_VALUE && dist[edge[j].v] > dist[edge[j].u] + edge[j].w) {
                bw.write("-1");
                bw.flush();
                bw.close();
                return;
            }
        }
 
        for (int i = 2; i <= n; i++) {
            if (dist[i] != Integer.MAX_VALUE) {
                bw.write(dist[i] + "\n");
            } else {
                bw.write("-1\n");
            }
        }
        bw.flush();
        bw.close();
    }
 
    public static class Edge {
        int u, v, w;
 
        public Edge(int u, int v, int w) {
            this.u = u;
            this.v = v;
            this.w = w;
        }
    }
}
cs


반응형
반응형

* 다익스트라(Dijkstra) 알고리즘

한 정점에서 다른 정점으로 가는 최단거리를 구하는 알고리즘으로 가중치가 음수면 사용이 불가능하다.

다익스트라는 최단거리는 최단거리들로 이루어져 있다는 Greedy한 아이디어로 만들어진 알고리즘이다.

주로 우선순위 큐 BFS를 이용하여 구현되며, 작동 방식은 다음과 같다.

1. 아직 방문하지 않은 정점들 중 거리가 가장 짧은 정점을 하나 선택해 방문한다.

2. 해당 정점에서 인접하고 아직 방문하지 않은 정점들의 거리를 갱신한다.


* 작동 예시


0번 정점에서 시작한다고 가정하고, 위와 같은 그래프가 있을 때 작동방식은 다음과 같다.

 작동 방식

 방문한 정점

PrioirityQueue 

 사용가능한 간선

 1. 0번 정점에서 가까운 간선 중 최소 가중치를 가진 0-1 간선을 선택한다.

 0

 [7, 9, 14]

 0-1, 0-2, 0-3

 2. 0번 정점과 1번 정점에서 가중치가 작은 간선을 선택한다. 

(단, 1번정점에서 선택하는 경우는 0-1 간선의 가중치인 7을 더해야 한다.)

 1

 [9,  14, 10+7, 15+7]

 0-2, 0-5, 0-1-2, 0-1-3

 3. 0,1,2 정점에서 사이클을 만들지 않는 간선 중 가중치가 작은 간선을 선택한다.

 2

 [9+2, 14, 9+11, 7+15]

 0-2-5, 0-5, 0-2-3, 0-1-3

 4. 0,1,2,5 정점에서 사이클을 만들지 않는 간선 중 가중치가 작은 간선을 선택한다.

 5

 [9+11, 9+2+9, 7+15]

 0-2-3, 0-2-5-4, 0-1-3

 5. 0,1,2,3,5 정점에서 사이클을 만들지 않는 간선 중 가중치가 작은 간선을 선택한다.

 3

 [9+2+9, 9+11,6]

 0-2-5-4, 0-2-3-4

 6. 0,1,2,3,5 정점에서 사이클을 만들지 않는 간선 중 가중치가 작은 간선을 선택한다.

 4

 

 

위 순서를 통해 정점과 정점사이의 최단 거리를 구한다.

핵심은 최단거리는 최단거리들로 이루어져 있다는 생각이고, 선택한 정점과 인접한 최소한의 가중치를 가진 간선을 선택하는 것은 프림 알고리즘과 비슷하다.

하지만, 프림 알고리즘과 달리 우선순위 큐에 해당 가중치를 계속 더해가며 최단 경로를 찾는 알고리즘이다.

백준/1753 :: 최단경로 문제

백준/1753 :: 최단경로 풀이

반응형
반응형

* 핵심 개념

최대값은 최소값이랑 곱하고 최소값은 최대값이랑 곱한 값을 더하면 그 값은 최소가 된다.

A,B 배열을 정렬해서 최대값과 최소값을 곱하면서 더하면 된다.

단순 Arrays.sort를 사용해 정렬해 풀 수도 있지만

MergeSort도 연습할겸 구현해서 사용했다.

* 문제

문제

옛날 옛적에 수학이 항상 큰 골칫거리였던 나라가 있었다. 이 나라의 국왕 김지민은 다음과 같은 문제를 내고 큰 상금을 걸었다.

길이가 N인 정수 배열 A와 B가 있다. 다음과 같이 함수 S를 정의하자.

S = A[0]*B[0] + ... + A[N-1]*B[N-1]

S의 값을 가장 작게 만들기 위해 A의 수를 재배열하자. 단, B에 있는 수는 재배열하면 안 된다.

S의 최솟값을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 N이 주어진다. 둘째 줄에는 A에 있는 N개의 수가 순서대로 주어지고, 셋째 줄에는 B에 있는 수가 순서대로 주어진다. N은 50보다 작거나 같은 자연수이고, A와 B의 각 원소는 100보다 작거나 같은 음이 아닌 정수이다.

출력

첫째 줄에 S의 최솟값을 출력한다.

* 소스 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import java.io.*;
import java.util.*;
 
public class Main {
    public static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StringTokenizer stk;
 
    public static void main(String[] args) throws Exception {
        int n = Integer.parseInt(br.readLine());
        int[][] num = new int[2][n];
        for (int i = 0; i < 2; i++) {
            stk = new StringTokenizer(br.readLine());
            for (int j = 0; j < n; j++) {
                num[i][j] = Integer.parseInt(stk.nextToken());
            }
            num[i] = merge(num[i], new int[n], 0, n - 1);
        }
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += num[0][i] * num[1][n - i - 1];
        }
        System.out.println(sum);
    }
 
    public static int[] merge(int[] num, int[] tmp, int start, int end) {
        int mid = (start + end) / 2;
        if (start < end) {
            merge(num, tmp, start, mid);
            merge(num, tmp, mid + 1, end);
            num = mergeSort(num, tmp, start, mid, end);
        }
        return num;
    }
 
    public static int[] mergeSort(int[] num, int[] tmp, int start, int mid, int end) {
        for (int i = start; i <= end; i++) {
            tmp[i] = num[i];
        }
        int left = start;
        int right = mid + 1;
        int idx = start;
        while (left <= mid && right <= end) {
            if (tmp[left] <= tmp[right]) {
                num[idx++= tmp[left++];
            } else {
                num[idx++= tmp[right++];
            }
        }
        for (int i = 0; i <= mid - left; i++) {
            num[i + idx] = tmp[i + left];
        }
        return num;
    }
}
cs


반응형
반응형

Java를 사용하다 보면 형변환할 일이 많이 있다.

int <> char <> String 으로의 형변환에 대해 알아보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
    public static void main(String[] args) throws Exception {
        int a = 65;
        System.out.println("Integer to Character : " + (char) a);           //A
        System.out.println("Integer to String : " + String.valueOf(a));     //65
 
        char ch = '3';
        char[] ch2 = {'a','b'};
        System.out.println("Character to Integer : " + ((int) ch - '0'));   //3
        System.out.println("Character to String : " + String.valueOf(ch));  //3
        System.out.println("Character to String : " + String.valueOf(ch2)); //ab
 
        String s = "9";
        String s2 = "123";
        System.out.println("String to Integer : " + Integer.parseInt(s));   //9
        System.out.println("String to Character : " + s.charAt(0));         //9
        System.out.println("String to Character : " + Arrays.toString(s2.toCharArray()));   // [1, 2, 3]
    }
}
cs

< 유의 사항 >

char to int에서 숫자를 넘기는 경우 '0'을 해야 한다. (아스키코드값을 넘기기 때문)

char to string에서 char배열인 경우 String.valueOf를 사용하면 char배열 글자가 서로 붙어서 문자열로 넘어간다.

String to char에서 문자 길이가 1이라면 charAt으로 넘기면 된다.

String to char에서 문자 길이가 2이상이라면 toCharArray를 이용해 char배열로 넘긴다.

반응형
반응형

* 핵심 개념

Stack을 사용해 풀 수 있는 문제다.

다양한 방법이 있지만 필자는 입력받은 문자를 Stack에 넣어가며 값을 비교했다.

Stack에 ()와 [] 짝이 안맞다면 강제로 종료하면 되고,

아니면 ()인 경우 2를, []인 경우 3을 스텍에 넣어준다.

만약, Stack에 [,2,3 이 들어있다면 2+3을 하고 현재 문자가 ] 이라면 *3을 하면 된다.

마지막에 Stack을 돌면서 숫자를 전부 더해주면 된다.


예외 처리는 맨 처음부터 ')'나 ']'가 들어왔을때 처리해야 한다.


* 문제

문제

4개의 기호 ‘(’, ‘)’, ‘[’, ‘]’를 이용해서 만들어지는 괄호열 중에서 올바른 괄호열이란 다음과 같이 정의된다.

  1. 한 쌍의 괄호로만 이루어진 ‘()’와 ‘[]’는 올바른 괄호열이다. 
  2. 만일 X가 올바른 괄호열이면 ‘(X)’이나 ‘[X]’도 모두 올바른 괄호열이 된다. 
  3. X와 Y 모두 올바른 괄호열이라면 이들을 결합한 XY도 올바른 괄호열이 된다.

예를 들어 ‘(()[[]])’나 ‘(())[][]’ 는 올바른 괄호열이지만 ‘([)]’ 나 ‘(()()[]’ 은 모두 올바른 괄호열이 아니다. 우리는 어떤 올바른 괄호열 X에 대하여 그 괄호열의 값(괄호값)을 아래와 같이 정의하고 값(X)로 표시한다. 

  1. ‘()’ 인 괄호열의 값은 2이다.
  2. ‘[]’ 인 괄호열의 값은 3이다.
  3. ‘(X)’ 의 괄호값은 2×값(X) 으로 계산된다.
  4. ‘[X]’ 의 괄호값은 3×값(X) 으로 계산된다.
  5. 올바른 괄호열 X와 Y가 결합된 XY의 괄호값은 값(XY)= 값(X)+값(Y) 로 계산된다.

예를 들어 ‘(()[[]])([])’ 의 괄호값을 구해보자.  ‘()[[]]’ 의 괄호값이 2 + 3×3=11 이므로  ‘(()[[ ]])’의 괄호값은 2×11=22 이다. 그리고  ‘([])’의 값은 2×3=6 이므로 전체 괄호열의 값은 22 + 6 = 28 이다.

여러분이 풀어야 할 문제는 주어진 괄호열을 읽고 그 괄호값을 앞에서 정의한대로 계산하여 출력하는 것이다. 

입력

첫째 줄에 괄호열을 나타내는 문자열(스트링)이 주어진다. 단 그 길이는 1 이상, 30 이하이다.

출력

첫째 줄에 그 괄호열의 값을 나타내는 정수를 출력한다. 만일 입력이 올바르지 못한 괄호열이면 반드시 0을 출력해야 한다. 

* 소스 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import java.io.*;
import java.util.*;
 
public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String input = br.readLine();
        Stack<String> stack = new Stack<>();
        int sum = 0;
        String past;
        for (int i = 0; i < input.length(); i++) {
            char ch = input.charAt(i);
            switch (ch) {
                case '(':
                case '[':
                    stack.push(Character.toString(ch));
                    break;
                case ')':
                    check(stack.size());
                    past = stack.pop();
                    if (past.equals("(")) {
                        stack.push(String.valueOf(2));
                    } else if (past.equals("[")) {  //예외 처리
                        System.out.println("0");
                        System.exit(0);
                    } else {  //stack에 숫자가 잇다면
                        int add = Integer.parseInt(past);
                        //숫자라면
                        while (check(stack.size()) && !stack.peek().equals("(")) {
                            String check = stack.pop();
                            if (!check.equals("["&& !check.equals("(")) {
                                add += Integer.parseInt(check);
                            }
                        }
                        stack.pop();
                        stack.push(String.valueOf(add * 2));
                    }
                    break;
                case ']':
                    check(stack.size());
                    past = stack.pop();
                    if (past.equals("[")) {
                        stack.push(String.valueOf(3));
                    } else if (past.equals("(")) {
                        System.out.println("0");
                        System.exit(0);
                    } else {  //stack에 숫자가 잇다면
                        int add = Integer.parseInt(past);
                        while (check(stack.size()) && !stack.peek().equals("[")) {
                            String check = stack.pop();
                            if (!check.equals("["&& !check.equals("(")) {
                                add += Integer.parseInt(check);
                            }
                        }
                        stack.pop();
                        stack.push(String.valueOf(add * 3));
                    }
                    break;
            }
        }
        while (!stack.isEmpty()) {
            try {
                sum += Integer.parseInt(stack.pop());
            } catch (Exception e) {
                sum = 0;
                break;
            }
        }
        System.out.println(sum);
    }
 
    public static boolean check(int size) {
        if (size != 0return true;
        else {
            System.out.println("0");
            System.exit(0);
            return false;
        }
    }
}
cs


반응형
반응형

* 합병 정렬(Merge Sort)    =>O(nlogn)

합병 정렬은 O(nlogn) 의 시간복잡도를 보장한다.

하지만 단점은 임시 배열 공간이 추가로 필요하다는 점이다.

분할 정복으로 구현하는데, 배열을 최대한 나누고 각 배열을 다시 정렬하면서 합병하는 방식이다.


< 작동 방식 >

1. 나눌 수 있는 만큼 끝까지 나눈다. (1개가 될때까지)

2. 나누어진 배열을 임시 배열에 저장하고 이를 비교하면서 확인한다.

모든 작동방식을 알아보는 것 보다 합치는 과정에 대표적인 예를 통해 작동방식을 알아보자.

우선 배열을 하나의 배열까지 쪼갠 후 합치면서 정렬한다.

이후 tmp 배열에 4개의 data를 복사한다.

left = start, right = mid+1로 설정하고 서로 비교해 작은 값을 data에 덮어씌운다.

left를 한칸 이동하고 작은 값을 data에 덮어씌운다.

right를 한칸 이동하고 작은 값을 data에 덮어씌운다.


right가 배열의 끝에 도달했기 때문에 남은 left를 data에 덮어씌운다.

이런 방식으로 통해 정렬하는 방식이다.

추가로, 이 방식은 left가 남았을 때만 남은 data를 덮어 씌워주면 되는데, 그 이유는 처음 시작때 임시 배열에 data를 복사했기 때문이다.

아래 예시를 살펴보자.

tmp에 data를 복사한 후 left와 right를 비교해 작은 값을 data에 넣는다.

left를 한칸 이동하고 작은 값을 data에 덮어 씌운다.

여기서 Right에 있는 값을 굳이 덮어 씌우지 않아도 원본 데이터랑 같기 때문에 Right의 경우 덮어씌울 필요가 없다.


< 소스 코드 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public static void MergeSort(int[] num, int[] tmp, int start, int end) {
    if (start < end) {
        int mid = (start + end) / 2;
        MergeSort(num, tmp, start, mid);
        MergeSort(num, tmp, mid + 1, end);
        Merge(num, tmp, start, mid, end);
    }
}
 
public static void Merge(int[] num, int[] tmp, int start, int mid, int end) {
    for (int i = start; i <= end; i++) {
        tmp[i] = num[i];
    }
    int left = start;
    int right = mid + 1;
    int idx = start;
    while (left <= mid && right <= end) {
        if (tmp[left] <= tmp[right]) {
            num[idx] = tmp[left];
            left++;
        } else {
            num[idx] = tmp[right];
            right++;
        }
        idx++;
    }
    //남은 left를 num 배열에 
    for (int i = 0; i <= mid - left; i++) {
        num[i + idx] = tmp[i + left];
    }
}
cs

반응형

'∙Algorithm Tech > Sort' 카테고리의 다른 글

[Algorithm Tech] 퀵 정렬(Quick Sort)  (0) 2019.04.28
[Algorithm Tech] 정렬 알고리즘  (0) 2019.04.27
반응형

* 퀵 정렬(Quick Sort)    =>평균 O(nlogn), 최악 O(n^2)

자바에서 Arrays.sort를 사용하변 내부는 Quick Sort로 구현되어있다.

평균 O(nlogn) 이지만 최악에 O(n^2) 이므로 데이터가 많은 경우 조심해야 한다.

최악의 경우란 아이러니하게도 정렬된 경우를 의미한다.


< 작동 방식 >

퀵 정렬은 pivot값을 중심으로 왼쪽에는 pivot 보다 작은 값들, 오른쪽에는 pivot보다 크거나 같은 값을 정렬한다.

기준을 잡고, left, right 포인터를 이동하면서 num[left] > pivot 과 num[right] < pivot 인 경우 서로 교환한다.

소스 코드와 그림으로 이해하자.

num[left] > pivot, num[right] < pivot 조건을 만족할 때까지 left, right를 움직인다.

pivot을 가운데 값으로 설정하고 Left, Right 를 각 끝의 idx를 지정한다.

둘의 위치를 바꾼다. 이렇게 되면 pivot 왼쪽에는 pivot보다 작은 값, 오른쪽에는 큰값이 정렬된다.

3번 idx를 제외하고 0~2 idx, 4~7 idx를 재귀호출한다.

왼쪽을 먼저 정렬할 때, num[left] > pivot, num[right] < pivot 조건을 만족하는 값을 찾는다.

두 값을 서로 바꾼다.

3번 idx 기준 왼쪽 정렬 끝

오른쪽을 정렬할 때 pivot, left, right를 설정한다.

num[left] > pivot, num[right] < pivot 조건을 만족하는 값을 찾는다.

두 값을 서로 바꾼다. left는 이동하지 않고, right만 이동하는데 조건을 만족하는 값이 없다.

5~7 idx 퀵소트 재귀 호출

num[left] > pivot, num[right] < pivot 조건을 만족하는 값을 찾아 다시 바꿔준다.

< 소스 코드 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static int partition(int left, int right, int[] num) {
    int pivot = num[(left + right) / 2];
    while (left <= right) {
        while (num[left] < pivot) left++;
        while (num[right] > pivot) right--;
        if (left <= right) {
            swap(left, right, num);
            left++;
            right--;
        }
    }
    return left;
}
 
public static void QuickSort(int left, int right, int[] num) {
    if (left < right) {
        int pivot = partition(left, right, num);
        if (left < pivot - 1) QuickSort(left, pivot - 1, num);
        if (right > pivot) QuickSort(pivot, right, num);
    }
}
cs


반응형

'∙Algorithm Tech > Sort' 카테고리의 다른 글

[Algorithm Tech] 합병 정렬(Merge Sort)  (0) 2019.04.28
[Algorithm Tech] 정렬 알고리즘  (0) 2019.04.27
반응형

버블 정렬(Bubble Sort)

BubbleSort

  • 인접한 두 숫자를 비교해서 변경하는 방법
  • 오른쪽부터 최대값/최소값을 정렬해 나가는 방식
1
2
3
4
5
6
7
public void BubbleSort(int[] num) {
    for (int i = 0; i < num.length; i++) {
        for (int j = 1; j < num.length - i; j++) {
            if (num[j] < num[j - 1]) swap(j - 1, j, num);
        }
    }
}

선택 정렬(Selection Sort)

SelectionSort

  • 자신보다 뒤에있는 숫자들 중 가장 작은 숫자를 발견해서 맨 앞에서부터 채우는 정렬
  • 왼쪽부터 최대값/최소값을 정렬해 나가는 방식
1
2
3
4
5
6
7
8
9
public void SelectionSort(int[] num) {
    for (int i = 0; i < num.length; i++) {
        int min = i;
        for (int j = i + 1; j < num.length; j++) {
            if (num[min] > num[j]) min = j;
        }
        if (num[min] < num[i]) swap(min, i, num);
    }
}

삽입 정렬(Insertion Sort)

InsertionSort

  • i번째 배열 값부터 앞으로 탐색하며 자신보다 작다면 swap하는 정렬
  • n번째 수행시 n번째 까지 정렬되는 방법
1
2
3
4
5
6
7
8
9
10
11
public void InsertioinSort(int[] num) {
    for (int i = 1; i < num.length; i++) {
        int tmp = num[i];
        for (int j = i - 1; j >= 0; j--) {
            if (tmp < num[j]) {
                num[j + 1] = num[j];
                num[j] = tmp;
            }
        }
    }
}

퀵 정렬(Quick Sort)

  • pivot을 정하고 pivot 좌/우를 정렬하면서 전체 배열을 정렬
  • 자바에서 Arrays.sort를 사용하변 내부는 Quick Sort로 구현되어있다.
  • 평균 O(nlogn) 이지만 최악에 O(n^2) 이므로 데이터가 많은 경우 조심해야 한다.
  • 최악의 경우란 아이러니하게도 정렬된 경우를 의미한다.

< 작동 방식 >

web_process

  • pivot을 가운데 값으로 설정하고 Left, Right 를 각 끝의 idx를 지정한다.

web_process

  • num[left] > pivot, num[right] < pivot 조건을 만족할 때까지 left, right를 움직인다.

web_process

  • 둘의 위치를 바꾼다. 이렇게 되면 pivot 왼쪽에는 pivot보다 작은 값, 오른쪽에는 큰값이 정렬된다.

< 소스 코드 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public int partition(int left, int right, int[] num) {
    int pivot = num[(left + right) / 2];
    while (left <= right) {
        while (num[left] < pivot) left++;
        while (num[right] > pivot) right--;
        if (left <= right) {
            swap(left, right, num);
            left++;
            right--;
        }
    }
    return left;
}
 
public void QuickSort(int left, int right, int[] num) {
    if (left < right) {
        int pivot = partition(left, right, num);
        if (left < pivot - 1) QuickSort(left, pivot - 1, num);
        if (right > pivot) QuickSort(pivot, right, num);
    }
}


합병 정렬(Merge Sort)

  • 합병 정렬은 O(nlogn) 의 시간복잡도를 보장한다.
  • 단점은 임시 배열 공간이 추가로 필요하다는 점이다.
  • 분할 정복으로 구현하는데 배열을 최대한 나누고, 합병하면서 값을 비교해 정렬하는 방식이다.

< 작동 방식 >

web_process

  1. left = start, right = mid+1로 설정하고 서로 비교해 작은 값을 data에 덮어씌운다.web_process
  2. left를 한칸 이동하고 작은 값을 data에 덮어씌운다.web_process
  3. right를 한칸 이동하고 작은 값을 data에 덮어씌운다.web_process
  4. right가 배열의 끝에 도달했기 때문에 남은 left를 data에 덮어씌운다.

< 소스 코드 >

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public void mergeSort(int start, int end) {
    if (start < end) {
        int mid = (start + end) / 2;
        mergeSort(start, mid);
        mergeSort(mid + 1, end);
        merge(start, mid, end);
    }
}

public void merge(int start, int mid, int end) {
    for (int i = start; i <= end; i++) {
        tmp[i] = num[i];    //tmp 배열에 값을 옮긴다.
    }
    int left = start;
    int right = mid + 1;
    int idx = start;
    while (left <= mid && right <= end) {
        //num 배열에 비교하면서 저장
        if (tmp[left] >= tmp[right]) {
            num[idx++] = tmp[right++];
        } else {
            num[idx++] = tmp[left++];
        }
    }
    //어느 한쪽에 남은 tmp 배열을 num 배열에 저장
    while (left <= mid) num[idx++] = tmp[left++];
    while (right <= end) num[idx++] = tmp[right++];
}


반응형

'∙Algorithm Tech > Sort' 카테고리의 다른 글

[Algorithm Tech] 합병 정렬(Merge Sort)  (0) 2019.04.28
[Algorithm Tech] 퀵 정렬(Quick Sort)  (0) 2019.04.28