배열을 쓰다보면 정렬을 빼놓을수없다.

애초에 배열이 오름차순 내림차순 정렬이 되어있어야 쓰기 편할뿐 아니라

탐색이나 다른 알고리즘을 최적화하는데 아주 중요하다.


거품 선택 삽입 힙 퀵 만 보도록 한다.


거품(bubble) 정렬.

뽀글뽀글이다. 1번2번 비교해서 정렬하고, 2번 3번비교해서 정렬하고.. ...쭈욱가다보면 마지막 원소만 원하는값으로 정렬이 되었을것이다. 만약 이 마지막 인덱스를 n이라고 치면

n-1번만에 된것. 그다음은 n-2번만에 될것이고 ...쭉가다보면 한번만에 정렬이 끝날것이다.

그렇게되면 정렬은 n(n-1) / 2 번만에 이루어질것이다. 

이 정렬은 거의 모든상황에서 최악의 성능을 보여준다. 물론 이미 정렬된자료에서는 1번만 돌면되기때문에 최고의 성능을 보여주지만, 이미정렬된자료를 정렬한다고? 여기까지.


1
2
3
4
5
6
7
8
9
10
11
void Bubble_Sort(int len)
{
     for(int i=0; i<len; i++)
          for (int j = 0; j < len - i - 1; j++)
               if (a[j] > a[j+1])
               {
                    int t = a[j];
                    a[j] = a[j + 1];
                    a[j + 1= t;
               }
}
cs
이경우 클수록 제일 큰 인덱스로 넣는 코드를 보여준다.


선택(selection) 정렬

하나하나 꼽아넣는 형태이다.

처음부터 끝까지 다 훑어서 제일작은걸 첫번쨰. 두번째부터 쭉 훑어서 제일작은걸 그다음. 이렇게 놓는방식인데

어떻게보면 버블이랑 비슷해 보일수도있다.

버블정렬보다 swap을 많이 안한다는게 장점이며, 평균적인 시간도 빠르다.

하지만 중복된 값들을 여러번 swap할 가능성이있어서 불안정하다고 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void SelectionSort(int arr[], int len) {
    int i, j;
    int min, temp;
    for(i=0; i<len-1; i++) {
        min = i;
        for(j=i+1; j<len; j++) {
            if(arr[j] < arr[min]) min = j;
        }
        temp = arr[i];
        arr[i] = arr[min];
        arr[min] = temp;
    }
}
 
cs

이렇게 돌아가는구나..정도만 알면 될것같다.

애초에 정렬자체가 요즘엔 버블 선택이런것보다 인클루드해서 쉽게 정렬하는방법이 많으니..


삽입(insertion) 정렬


하나뽑아서 적절한위치에 넣고 나머지 자리를 당기는 방식이다.

6 8 5 7 4 9 3 2 1 이런식으로 되어있을때

6을 먼저뽑아서 그자리에 놓는다. 이미 정렬된게 없기때문.

다음 8 을뽑아서 8의 적절한자리인 6다음에 놓도록 한다. 그럼 6 8 그대로 된다.

그다음 5를 뽑아서 적절한위치를 찾기위해 8을 당기고 6을 당겨서 제일 앞자리로 위치할수있게한다. 그러면 5 6 8 7 4.... 이 된다. 이런식으로 진행한다.

평균적으로는 빠른편이긴한데(거품 선택 삽입 중에서) 위 예시처럼 작은수가 뒤에 몰려있으면 당겨야하는 수도 그만큼많아지기때문에 지옥..


힙(heap) 정렬

시간복잡도 O(N*logN)

힙 정렬은 힙을사용한건데

자료구조에서 힙이란 완전이진트리의 일종으로 우선순위큐를 위한것이다.

최대값 최소값을 쉽게 얻어낼수있는데 

제일 root에 가장큰값이 있다면 최대힙, 가장작으면 최소힙이라고 한다.


최대힙기준

모든 원소를 힙에 삽입하고

루트에있는값은 최대값이므로 출력한후에 루트에서 제거한다.

삭제된 루트노드에는 그 힙의 마지막 노드를 넣는다. 그러고선 다시 힙을 완성시키고 루트값을 출력하고 제거하는 반복적인 일만 남았다.


코드로 짤경우 2세트 정도가 필요하다.

힙삽입, 루트제거

다음 문제로 알아보도록한다.


퀵(quick) 정렬

퀵정렬은 평균적인 상황에서 최고의 성능을 자랑한다.

피벗을정해서 양쪽을 기준으로 또 피벗정하고의 반복인데. 피벗이 계속 제일작은값이거나 제일큰값이면 시간복잡도 O(n^2)의 성능을 가지게되서 엄청구리다.

이를 보완하기위한 방법으로는 random quicksort나 전체수에서 임의의 3개나 9개값을 뽑아서 그들의 중간값으로 피벗을 정하는것.

최악의 경우를 막기위해서 생긴 방법이라고는 하나, 저렇게 해도 빠르다.

3 2 1 6 5 4 9 8 7 

1.피벗을하나뽑는다. -> 제일왼쪽 3으로 하겠다.

2.피벗을제외한 2 1 6 5 4 9 8 7 을 봤을때 제일 왼쪽값을 left, 오른쪽값을 right로 하겠다. 지금은 left =2, right =7

3.순서는 반복적이다. 피벗과 left비교 -> 피벗과 right비교-> 자리바꿀지여부

4.피벗과 left를 비교하여 left가 더 크다면 right와 피벗을비교 or  피벗과 left를 비교하여 left가 더 작다면 left를 한칸 우측으로 이동시킨다.

5. 지금은 피벗인 3이 left인 2보다 더 크므로, left를 한칸 우측으로 이동시켜 1로 한다. 

6. 현재 left = 1 , right = 7, 피벗 = 3

7. left가 피벗보다 작으므로 그대로 두고 right를 비교해본다. 

8. right(7)보다 피벗(3)이 더작으므로, right를 한칸 좌측으로 이동시킨다. 그렇다면 right=8이 된다.


말은 복잡하지만( 필자가 잘 쓰질 못함..)

해보면 엄청 간단하다.

이렇게하게되면 피벗 (3)으로 기준이 정해지고, 그러면 그 피벗을 기준으로 큰값들 작은값들로 나뉘게된다.

그러면 또 그 작은값들, 큰값들에서 각각 피벗을 정하고 똑같이 정렬을하면 퀵정렬이 완료된다.

코딩할때는 이부분을 재귀로해서 간단하게 구현할 수 있다.

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
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
 
void swap(int arr[], int a, int b) {
    int temp = arr[a];
    arr[a] = arr[b];
    arr[b] = temp;
}
 
int cut(int arr[] , int cutstart, int cutend) {
    int pivot = arr[cutstart];
    int left = cutstart + 1;
    int right = cutend;
 
    while (left <= right) {
        while (pivot >= arr[left] && left <= cutend){
            left++// left를 우측으로
        }
        while (pivot <= arr[right] && right >= cutstart +1){
            right--// right를 좌측으로
        }
        if (left <= right){
            swap(arr, left, right); //left,right스왑
        }
    }
    swap(arr, cutstart, right); // 피벗과 high가 가리키는 대상을 교환 
    return right;
}
 
void quicksort(int arr[], int start, int end) {
    if (start <= end) {
        int pivot = cut(arr, start, end);
        quicksort(arr, start, pivot - 1);
        quicksort(arr, pivot + 1end);
    }
}
int main() {
    int arr[9= { 3,2,1,6,5,4,9,8,7 };
    cout << "3 2 1 6 5 4 9 8 7 을 정렬 합니다." << endl;;
    quicksort(arr, 08);
 
    for (int i = 0; i < 9; i++) {
        cout << arr[i] << " ";
    }
    cout << " << 정렬 결과 " << endl;
}
cs

이런식으로 구현했다. cut 이 자르기위해 피버를정해서 정렬하는 과정이고 quicksort에서는 이걸 재귀로하여 정렬하였다.





'재미있는공부이야기 > 알고리듬' 카테고리의 다른 글

KMP 알고리즘  (0) 2019.02.15
3장. 문자열  (0) 2019.02.13
백준 2846번 오르막길  (0) 2019.02.09
백준 2920번 음계  (0) 2019.02.07
1장. 배열  (0) 2019.02.07

https://www.acmicpc.net/problem/2846

문제

상근이는 자전거를 타고 등교한다. 자전거 길은 오르막길, 내리막길, 평지로 이루어져 있다. 상근이는 개강 첫 날 자전거를 타고 가면서 일정 거리마다 높이를 측정했다. 상근이는 가장 큰 오르막길의 크기를 구하려고 한다.

측정한 높이는 길이가 N인 수열로 나타낼 수 있다. 여기서 오르막길은 적어도 2개의 수로 이루어진 높이가 증가하는 부분 수열이다. 오르막길의 크기는 부분 수열의 첫 번째 숫자와 마지막 숫자의 차이이다.

예를 들어, 높이가 다음과 같은 길이 있다고 하자. 12 3 5 7 10 6 1 11. 이 길에는 2 개의 오르막길이 있다. 밑 줄로 표시된 부분 수열이 오르막길이다. 첫 번째 오르막길의 크기는 7이고, 두 번째 오르막길의 크기는 10이다. 높이가 12와 6인 곳은 오르막길에 속하지 않는다.

가장 큰 오르막길을 구하는 프로그램을 작성하시오.


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
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
 
int num = 0, bottom = 0,top = 0;
int maxgap = 0;
int numarr[1004= { 0, };
 
int main() {
    //freopen("test.txt", "r", stdin);
    cin >> num;
    for (int i = 0; i < num; i++) {
        cin >> numarr[i];
    }
 
    bottom = numarr[0];
    for (int i = 0; i < num; i++) {
        //cout << "현재바텀 : " << bottom << endl;
        if (numarr[i] < numarr[i + 1]) {
            top = numarr[i + 1];
            //cout << "현재탑 : " << top << endl;
        }
        else {
            if (maxgap < top - bottom) {
                maxgap = top - bottom;
                //cout << "최대차이는 : " << maxgap << endl;
            }
            top = 0;
            bottom = numarr[i+1];
        }
    }
    cout << maxgap;
}
cs


이렇게 풀어주었습니다.

첫번째 인덱스를 가지고 바텀에 넣어준뒤에

그뒤의 요소와 비교를해서 크면 바텀은유지, 탑은 그다음큰값으로 쭉쭉 나아가게해서

같거나 다른수가나왔을때는 가장큰 오르막 경사와 비교를하여(gap) 최대 gap을 지정해줍니다.

그리곤 이미 오르막이 끝났다는 경우니, 탑을 초기화 시키고 그 끝난 다음의 요소를 바텀으로 지정해줍니다.

증가수열판단이 적용되는 문제라 생각됩니다.

'재미있는공부이야기 > 알고리듬' 카테고리의 다른 글

KMP 알고리즘  (0) 2019.02.15
3장. 문자열  (0) 2019.02.13
2장. 정렬  (0) 2019.02.09
백준 2920번 음계  (0) 2019.02.07
1장. 배열  (0) 2019.02.07
https://www.acmicpc.net/problem/2920

문제

다장조는 c d e f g a b C, 총 8개 음으로 이루어져있다. 이 문제에서 8개 음은 다음과 같이 숫자로 바꾸어 표현한다. c는 1로, d는 2로, ..., C를 8로 바꾼다.

1부터 8까지 차례대로 연주한다면 ascending, 8부터 1까지 차례대로 연주한다면 descending, 둘 다 아니라면 mixed 이다.

연주한 순서가 주어졌을 때, 이것이 ascending인지, descending인지, 아니면 mixed인지 판별하는 프로그램을 작성하시오.










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
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
 
int now = 0;
int umgye[8= { 0, };
 
int main() {
    //freopen("test.txt", "r", stdin);
    for (int i = 0; i < 8; i++) {
        cin >> umgye[i];
    }
    for (int i = 0; i < 7; i++) {
        if (umgye[i + 1== umgye[i] + 1)now = 1;
        else if (umgye[i + 1== umgye[i] - 1)now = 2;
        else {
            now = 3;
            break;
        }
    }
    switch (now) {
    case 1cout << "ascending";
        break;
    case 2cout << "descending";
        break;
    case 3cout << "mixed";
        break;
    }
}
cs


이렇게 풀이했습니다.

현재상태를 now로 저장을해서 1인경우는 ascending 2인경우에는 descending 3인경우는 mixed로 출력하게했습니다

다음요소가 현재요소보다 1이 크다면 계속 1을 대입하고 1보다 작은순간은 2를 넣습니다. 

이게 1씩증가하는 수만의 조합이라면 2차이가 날 수 없기 때문에 차이가 2가 나는순간 바로 mixed로 판별이되고 break로 구문을 빠져나오게 됩니다.


'재미있는공부이야기 > 알고리듬' 카테고리의 다른 글

KMP 알고리즘  (0) 2019.02.15
3장. 문자열  (0) 2019.02.13
2장. 정렬  (0) 2019.02.09
백준 2846번 오르막길  (0) 2019.02.09
1장. 배열  (0) 2019.02.07

배열은 알고리즘에서 자주쓰인다.

하지만 취뽀용으로는 사용하는 방법정도만 알고있으면된다. 탐색 정렬 수열응용 등으로 응용될수있다.

배열의 선언

1
2
int num[0]; // 컴파일에러 . 크기가 0인배열은 선언할 수 없음
int num[1]; // 크기가 1인 배열. 요소가 1개



배열을 0으로 모두 초기화하기.

1
2
3
4
int num[] = { 11,22,33,44 }; //배열의 크기생략
int num2[]; // 컴파일에러

int num[10= { 0, }; // 배열의 요소를 모두 0으로 초기화

배열의 크기. 그리고 for문을 이용한 배열의 요소 출력

1
2
3
4
5
6
7
sizeof(num) // 4바이트 크기의 요소가 10개이므로 40
sizeof(num) /sizeof(int// 배열의 크기를 구할때는 전체공간을 요소의 크기로나눔
 
for (int i = 0; i < sizeof(num) / sizeof(int); i++) {
    printf("%d\n", num[i]);
}
 r


같은 느낌으로 2차원배열에서

1
2
3
4
5
6
7
int num[3][4= { 0, };       // 2차원 배열의 요소를 모두 0으로 초기화
 
num[0][0= 11;    // 세로 인덱스 0, 가로 인덱스 0인 요소에 값 할당
num[0][1= 22;    // 세로 인덱스 0, 가로 인덱스 1인 요소에 값 할당
sizeof(num); // 4바이트 크기의 요소가 (4x3)개 이므로 (4x4x3) = 48.
int col = sizeof(num[0]) / sizeof(int); // 가로
int row = sizeof(num) / sizeof(num[0]); // 세로


배열은 동적할당이나 포인터, 함수의 매개변수 등등으로 안쓰면 어지러워질 일이없다. 전역변수로 이를 해결한다.


백준 2846번 오르막길, 2920번 음계를 보도록 한다.

'재미있는공부이야기 > 알고리듬' 카테고리의 다른 글

KMP 알고리즘  (0) 2019.02.15
3장. 문자열  (0) 2019.02.13
2장. 정렬  (0) 2019.02.09
백준 2846번 오르막길  (0) 2019.02.09
백준 2920번 음계  (0) 2019.02.07

+ Recent posts