반응형

 

 

 

1) 최소 신장트리의 이해

신장트리란?

  • Spanning Tree, 또는 신장 트리 라고 불리움
  • 원래의 그래프의 모든 노드가 연결되어 있으면서 트리의 속성을 만족하는 그래프
  • 신장 트리의 조건
    • 본래의 그래프의 모든 노드를 포함해야 함
    • 모든 노드가 서로 연결
    • 트리의 속성을 만족시킴 (사이클이 존재하지 않음)

 

최소 신장트리란?

  • Minimum Spanning Tree, MST 라고 불리움
  • 가능한 Spanning Tree 중에서, 간선의 가중치 합이 최소인 Spanning Tree를 지칭함

 

최소 신장트리 알고리즘

  • 그래프에서 최소 신장 트리를 찾을 수 있는 알고리즘이 존재함
  • 대표적인 최소 신장 트리 알고리즘
    • Kruskal’s algorithm (크루스칼 알고리즘)
    • Prim's algorithm (프림 알고리즘)

 

 

2) 크루스칼 알고리즘

  • 모든 정점을 독립적인 집합으로 만든다.
  • 모든 간선을 비용을 기준으로 정렬하고, 비용이 작은 간선부터 양 끝의 두 정점을 비교한다.
  • 두 정점의 최상위 정점을 확인하고, 서로 다른 집합일 경우 두 정점을 연결한다. (최소 신장 트리는 사이클이 없으므로, 사이클이 생기지 않도록 하는 것임)
  • 탐욕 알고리즘을 기초로 하고 있음

 

 

 

3) 프림 알고리즘

  • 시작 정점을 선택한 후, 정점에 인접한 간선중 최소 간선으로 연결된 정점을 선택하고, 해당 정점에서 다시 최소 간선으로 연결된 정점을 선택하는 방식으로 최소 신장 트리를 확장해가는 방식
  • 크루스칼 알고리즘과 프림 알고리즘 비교
    • 둘다, 탐욕 알고리즘을 기초로 하고 있음 (당장 눈 앞의 최소 비용을 선택해서, 결과적으로 최적의 솔루션을 찾음)
    • Kruskal's algorithm은 전체 간선중에서, 가장 가중치가 작은 간선부터 선택하면서 MST를 구함
    • Prim's algorithm은 특정 정점에서 시작, 해당 정점에 연결된 가장 가중치가 작은 간선을 선택, 간선으로 연결된 정점들에 연결된 간선 중에서 가장 가중치가 작은 간선을 택하는 방식으로 MST를 구함

 

 

 

 

 

반응형
반응형

 

 

[ 정렬의 종류 ]

재귀를 이용하지 않는 정렬

1) Selection Sort(선택정렬)

2) Bubble Sort(버블정렬)

3) Insertion Sort(삽입정렬)

 

재귀를 이용하는 정렬

4) Merge Sort

5) Quick Sort

 

 

 

 

 

1. Selection Sort

가장 큰 값을 찾아 맨 오른쪽으로 보낸다.

 

 

ex) 학생의 성적을 정렬하는 프로그램

학생은 학번, 영어, 수학, 국어 성적을 가지고 있음

국어 성적을 기준으로 내림차순 정렬 (selection sort)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

struct Student {
    int stu_id;
    int eng;
    int math;
    int korean;
};

struct Student* selectSort(struct Student *arr_std, int n){
    
    int i, j, max, maxId;
    struct Student tmp;
    
    for(j=0; j<n-1; j++){
        max = arr_std[0].eng;
        maxId = 0;
        for(i=0; i<n-j; i++){
            if(arr_std[i].eng > max){
                max = arr_std[i].eng;
                maxId = i;
            }
        }
        tmp = arr_std[n-1-j];
        arr_std[n-1-j] = arr_std[maxId];
        arr_std[maxId] = tmp;
    }
    
    return arr_std;
}

int main(void) {
    
    int n, i;
    scanf("%d", &n);

    struct Student *arr_std = (struct Student *)malloc(sizeof(struct Student) * n);

    int stu_id_first = 20210001;
    srand((unsigned)time(NULL));
    
    for(i=0; i<n; i++){
        arr_std[i].stu_id = stu_id_first + i;
        arr_std[i].eng = rand() % 101;
        arr_std[i].math = rand() % 101;
        arr_std[i].korean = rand() % 101;
    }
    
    arr_std = selectSort(arr_std, n);
    
    for(i=0; i<n; i++){
        printf("%d %d %d %d\n", arr_std[i].stu_id, arr_std[i].eng, arr_std[i].math, arr_std[i].korean);
    }
    
    return 0;
}

 

 

 

 

2. Bubble Sort

두 개 중에 큰 값을 오른쪽에 배치한다.

 

 

 

 

 

 

3. Insertion Sort (삽입정렬)

왼쪽부터 한 숫자씩 선택하면서 자리를 찾고 바꾼다.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int* insertSort(int *arr, int n){
    int i,j,tmp;
    
    for(i=1; i<n; i++){
        for(j=0; j<i; j++){
            if(arr[j] > arr[i]){
                tmp = arr[j];
                arr[j] = arr[i];
                arr[i] = tmp;
                continue;
            }
        }
    }
    return arr;
}

int main(void) {
    
    int i,n;
    scanf("%d", &n);
    
    int *arr = (int *)malloc(sizeof(int) * n);
    srand((unsigned)time(NULL));
    
    
    for(i=0; i<n; i++){
        arr[i] = rand() % 100;
        printf("%d ",arr[i]);
    }
    printf("\n");
 
    arr = insertSort(arr, n);
    
    for(i=0; i<n; i++){
        printf("%d ",arr[i]);
    }
    printf("\n");
    return 0;
}

 

 

 

 

4. Quick Sort

평균 수행시간 : nlogn

최악 수행시간 : n^2

 

 

1) j는 0부터 n-1까지 돈다.

2) 값이 젤 오른쪽 값보다(pivot) 작으면 i와 j 위치의 값을 서로 바꿔준다. i++

3) j loop가 끝나면 i와 pivot 자리의 값을 바꾼다.

 

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int partition(int *arr, int s, int e){
    
    int i, j, tmp;
    
    i = s;
    for(j=s; j<e; j++){
        if(arr[j] < arr[e]){
            tmp = arr[j];
            arr[j] = arr[i];
            arr[i] = tmp;
            i++;
        }
    }
    
    tmp = arr[e];
    arr[e] = arr[i];
    arr[i] = tmp;
    
    return i;
}


void quickSort(int *arr, int p, int r){
    int q;
    
    if(p < r){
        q = partition(arr, p, r);
        quickSort(arr, q+1, r);
        quickSort(arr, p, q-1);
    }
}

int main(void) {
    
    int n;
    scanf("%d", &n);
    
    int *arr = (int *)malloc(sizeof(int) * n);
    
    int i;
    srand((unsigned)time(NULL));
    
    for(i=0; i<n; i++){
        arr[i] = rand()%100;
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    quickSort(arr, 0, n-1);
    
    
    
    for(i=0; i<n; i++){
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

 

 

 

 

 

 

 

5. Merge Sort

1) 반으로 반으로 끝까지 나눈다.

2) 나눠진 두 그룹을 합치면서 정렬한다.

3) 나눴던 것들을 계속 합치면서 정렬한다.

 

합할 때는 i = s, j = q + 1, t = s 로 시작해서 

arr[i] 와 arr[j] 중에 더 작은 것을 새로운 배열에 넣는다. 이 때 새로운 배열의 인덱스는 t이다.

 

 

 

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

void merge(int *arr, int s, int q, int e, int n){
    int *new_arr = (int *)malloc(sizeof(int) * n);
    
    int i = s, j = q+1, k = s;
    
    while(i <= q && j <= e){
        if(arr[i] > arr[j]){
            new_arr[k++] = arr[j++];
        } else {
            new_arr[k++] = arr[i++];
        }
    }
    
    while(i <= q){
        new_arr[k++] = arr[i++];
    }
    while(j <= e){
        new_arr[k++] = arr[j++];
    }
    
    for(i=s; i<=e; i++){
        arr[i] = new_arr[i];
    }
}

void mergeSort(int *arr, int s, int e, int n){
    int q;
    if (s < e){
        q = (s + e) / 2;
        mergeSort(arr, s, q, n);
        mergeSort(arr, q+1, e, n);
        merge(arr, s, q, e, n);
    }
}

int main(void) {
    int n;
    scanf("%d", &n);
    
    int *arr = (int *)malloc(sizeof(int) * n);
    int i;
    srand((unsigned)time(NULL));
    
    for(i=0; i<n; i++){
        arr[i] = rand()%100;
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    mergeSort(arr, 0, n-1, n);
    
    
    for(i=0; i<n; i++){
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    
    return 0;
}

 

 

* 퀵 소트는 먼저 정렬을 하고 나서 나눠가는 방식인데 비해, 머지 소트는 먼저 나누고 정렬을 해 나간다.

 

반응형
반응형

 

 

 

1. 개념

- 정적 할당 : 변수 선언을 통해 필요한 메모리 할당

- 동적 할당 : 실행 중에 운영체제로부터 할당 받음 (by Heap)

 

2. 사용법

malloc() / free()

* stdlib.h 을 include 해야 함

 

 

1) 기본 타입 메모리

int *pInt = (int *)malloc(sizeof(int));
char *pChar = (char *)malloc(sizeof(char));

 

 

2) 배열 타입 메모리

int *p = (int *)malloc(sizeof(int) * 5);

 

 

 

 

3. 예시 문제

1) 임의의 정수 n개를 입력받아 n개의 난수를 만들어(0~999) 이를 오름차순을 정렬하는 프로그램

(동적할당 + 선택정렬)


#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    
    int n, i;
    
    scanf("%d", &n);
    
    int *arr = (int *)malloc(sizeof(int) * n);
    
    srand((unsigned)time(NULL));
    
    for(i=0; i<n; i++){
        arr[i] = rand() % 1000;
        printf("%d ", arr[i]);
    }
    printf("\n\n");
    
    
    // selection sort
    int j, k, max, maxIdx, tmp;
    
    for(k=0; k<n-1; k++){
        max = arr[0];
        maxIdx = 0;
        for(j=0; j<n-k; j++){
            if(max < arr[j]){
                max = arr[j];
                maxIdx = j;
            }
        }
        
        // 젤 뒤로 보내기
        tmp = arr[n-1-k];
        arr[n-1-k] = arr[maxIdx];
        arr[maxIdx] = tmp;
    }
    
    for(i=0; i<n; i++){
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    
    return 0;
}

 

 

(동적할당 + 버블정렬)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    
    int n, i;
    scanf("%d", &n);
    
    int *arr = (int *)malloc(sizeof(int) * n);
    srand((unsigned)time(NULL));
    
    for(i=0; i<n; i++){
        arr[i] = rand() % 1000;
        printf("%d ",arr[i]);
    }
    printf("\n");
    
    // bubble sort
    int j, k, tmp;
    
    for(k=0; k<n-1; k++){
        for(j=0; j<n-1-k; j++){
            if(arr[j] > arr[j+1]){
                tmp = arr[j+1];
                arr[j+1] = arr[j];
                arr[j] = tmp;
            }
        }
    }
    
    for(i=0; i<n; i++){
        printf("%d ",arr[i]);
    }
    printf("\n");
    
    return 0;
}

 

 

 

 

 

 

 

 

 

 

반응형
반응형

 

1. 개념 정리

조합 : 순서가 상관없음

순열 : 순서가 상관있음

중복 : 같은 아이템을 여러 번 뽑을 수 있는지 (중복조합 / 중복순열)

 

 

 

2. 문제 해결 방법

- k : 앞으로 뽑아야 할 크기

- Trivial Case : k가 0일 때

- Recursive Case (if k > 0) : 앞으로 K 개를 더 뽑아야 하므로 일단 1개를 뽑고 같은 함수를 이용해 k-1개를 더 뽑는다.

 

 

3. 예시

1) 공뽑기 : A, B, C, D, E, F,G가 적혀 있는 공 7개에서 3개를 뽑는 경우를 구하라

=> 순서가 상관없고 중복이 없으므로 "조합" 문제이다.


#include <stdio.h>

void pick(int n, int* bucket, int bucketSize, int k, char* balls){
    int i, lastIndex,smallest, item;
    if(k == 0){
        for(i=0; i<bucketSize; i++){
            printf("%c ", balls[bucket[i]]);
        }
        printf("\n");
        return;
    }
    
    lastIndex = bucketSize - k - 1;
    
    if(bucketSize == k){
        smallest = 0;
    } else {
        smallest = bucket[lastIndex] + 1;
    }
    
    for(item=smallest; item<n; item++){
        bucket[lastIndex+1] = item;
        pick(n, bucket, bucketSize, k-1, balls);
    }
}

int main(void) {
    
    int n = 7;
    char balls[7] = {'A','B','C','D','E','F','G'};
    int bucket[3];
    
    pick(n, bucket, 3, 3, balls);
    
    
    return 0;
}

 

 

 

 

2) 배우들 중 n명을 뽑아서 최우수, 우수상을 수여하기로 한다. 1명은 단 하나의 상만 받을 수 있다.

배우는 공유, 김수현, 송중기, 지성, 현빈 5명 중에서 선택한다고 하자. 어떤 경우가 가능한가?

=> 순서가 중요하고, 중복이 허용되지 않으므로 "순열" 문제이다.

 


#include <stdio.h>

int isIn(int* bucket, int size, int num){
    int i;
    for(i=0; i<size; i++){
        if(bucket[i] == num)
            return 1;
    }
    return 0;
}

void pick(int n, int* bucket, int bucketSize, int k, char balls[][10]){
    int i, lastIndex, item;
    if(k == 0){
        for(i=0; i<bucketSize; i++){
            printf("%10s", balls[bucket[i]]);
        }
        printf("\n");
        return;
    }
    
    lastIndex = bucketSize - k - 1;
    
    for(item=0; item<n; item++){
        if(!isIn(bucket, lastIndex+1, item)){
            bucket[lastIndex+1] = item;
            pick(n, bucket, bucketSize, k-1, balls);
        }
    }
}

int main(void) {
    
    int n = 5;
    char actors[5][10] = {"공유", "김수현", "송중기","지성", "현빈"};
    
    
    int bucket[2];
    
    pick(n, bucket, 2, 2, actors);
    
    
    return 0;
}

 

 

 

3) 4진법으로 만들 수 있는 n자리 수를 모두 나열하는 프로그램 작성하시오

입력 : 3  (3자리 수)

출력 : 000, 001, ..., 333 으로 나오면 된다.

=> 순서가 중요하고, 중복이 가능하므로 "중복순열" 문제이다. 

#include <stdio.h>

void pick(int n, int* bucket, int bucketSize, int k, int* balls){
    int i, lastIndex, item;
    if(k == 0){
        for(i=0; i<bucketSize; i++){
            printf("%d", balls[bucket[i]]);
        }
        printf("\n");
        return;
    }
    
    lastIndex = bucketSize - k - 1;
    
    for(item=0; item<n; item++){

        bucket[lastIndex+1] = item;
        pick(n, bucket, bucketSize, k-1, balls);
    
    }
}

int main(void) {
    
    int n = 4;
    int num[4] = {0,1,2,3};
    
    int bucket[3];
    
    pick(n, bucket, 3, 3, num);
    
    
    return 0;
}

 

 

 

4) 1부터 n까지의 연속되어 있는 수와 +/-를 이용해서 만들 수 있는 모든 수식을 그 결과와 함께 나열하시오

- 입력 : 2

- 출력:

 + 1 + 2 = 3

+ 1 -2 = -1

-1 + 2 = 1

-1 -2 = -3

 

=> 1과 2는 그대로 있고, +와 -를 이용해 중복순열을 처리하는 문제이다.

#include <stdio.h>

int charToInt(char x, int sum, int n){
    if(x == '+'){
        sum += n;
    } else {
        sum -= n;
    }
    return sum;
}

void func(int n, char bucket[2], int bucketSize, int k, char *balls){
    
    int i, j;
    int balls_idx = bucketSize - k;
    
    if(k == 0){
        int sum = 0;
        for(j=0; j<bucketSize; j++){
           
            printf("%c", balls[j]);
            printf("%d", j+1);
            sum = charToInt(balls[j], sum, j+1);
            
        }
        printf("=%d\n", sum);
        return;
    }
    
    for(i=0; i<bucketSize; i++){
        balls[balls_idx] = bucket[i];
        func(n, bucket, bucketSize, k-1, balls);
    }
}

int main(void) {
    
    char bucket[2] = {'+','-'};
    
    char answer[2];
    
    int n;
    
    scanf("%d", &n);
    
    func(n, bucket, 2, n, answer);
    
    
    return 0;
}

 

 

 

 

 

5) 1000,5000,10000원 짜리 지폐로 입력값을 만들 수 있는 모든 방법을 출력하시오

입력 : 6000

출력 :

- 1000 1000 1000 1000 1000 1000

- 5000 1000

 

=> 순서가 중요하지 않고, 중복을 허용하므로 "중복조합"이다. 

#include <stdio.h>

int sumBalls(int *balls, int ballSize){
    int i, sum = 0;
    for(i=0; i<ballSize; i++){
        sum += balls[i];
    }
    return sum;
}

void func(int input, int *bucket, int bucketSize, int k, int *balls, int ballSize){
    int i, j;
    
    int sum_balls;
    sum_balls = sumBalls(balls, ballSize);
    
    if(sum_balls == input){
        for(j=0; j<ballSize; j++){
            printf("%d ", balls[j]);
        }
        printf("\n");
        return;
    } else if(sum_balls > input){
        return;
    }
    
    for(i=k; i<bucketSize; i++){
        balls[ballSize] = bucket[i];
        func(input, bucket, bucketSize, i, balls, ballSize+1);
    }
    
}

int main(void) {
    
    int moneys[3] = {1000,5000,10000};
    int answer[1000];
    int input;
    
    scanf("%d", &input);
    
    func(input, moneys, 3, 0,  answer, 0);
    
    return 0;
}

 

 

 

 

 

 

 

 

 

반응형
반응형

 

 

 

 

1. 순환 이란?

: 알고리즘이나 함수가 수행 도중에 자기 자신을 다시 호출하여 문제를 해결하는 기법

순환하는 부분 / 순환을 멈추는 부분으로 구성

 

2. 팩토리얼

 

 

3. 피보나치 수열

 

* 이렇게 구현할 경우, 같은 항을 여러 번 호출하게 되므로 비효율적이다.

-> 이미 계산한 항의 경우 값이 있으면 재귀 호출을 하지 않고 배열의 값을 반환하는 식으로 작성하는 것이 좋다.

 

 

4. 하노이 탑 문제

 

 

 

 

 

 

 

반응형
반응형

 

 

 

 

 

 

 

1. 순차 탐색

: 정렬되지 않은 배열을 처음부터 마지막까지 하나씩 검사한다.

 

1) 시간 복잡도

- 평균 비교 횟수

if 탐색 성공 : (n+1)/2

if 탐색 실패 : n번 비교

따라서 시간복잡도는 O(n)이다.

 

 

2) 코드

 

 

 

 

 

 

 

2. 개선된 순차 탐색

: 리스트 끝에 탐색 키를 저장한다.

(* 탐색 키 : 찾는 값 )

 

 

 

1) 코드

 

 

 

 

 

 

 

3. 이진 탐색(binary search)

 

1) 동작 방식

: 정렬된 배열의 중앙에 있는 값을 조사하여 찾고자 하는 항목이 왼쪽 또는 오른쪽 부분에 배열에 있는지 알아내어

탐색의 범위를 반으로 줄여 가며 탐색을 진행한다.

 

 

 

 

 

 

2) 코드

 

 

3) 시간 복잡도

: log2(N)

 

 

 

 

 

 

4. 색인 순차탐색

: 인덱스 테이블을 사용하여 탐색의 효율을 증대시킨다.

-> 주 자료 리스트와 인덱스 테이블은 모두 정렬되어 있어야 한다.

 

 

 

1) 시간 복잡도

O( (m+n)/m )

 

 

 

 

 

 

5. 보간 탐색

: 사전이나 전화번호부를 탐색하는 방법이다.

 

1) 작동 방식

: 탐색 키가 존재할 위치를 예측하여 탐색한다.

이진 탐색과 유사하나, 리스트를 불균등 분할하여 탐색한다.

 

2) 시간 복잡도

O(log(n))

 

3) 탐색 위치

 

 

 

 

 

 

4) 코드

 

 

 

 

 

 

 

 

 

 

 

6. 균형 이진탐색트리

 

- 이진탐색 vs 이진탐색트리 

1) 이진탐색 : 배열 사용하므로 삽입 / 삭제가 매우 비효율

2) 이진탐색트리 : 트리를 사용하므로 삽입 / 삭제가 매우 빠르다.

 

 

 

3) 시간 복잡도

- 균형 트리 : O(logn)

- 불균형 트리 : O(n), 순차탐색과 동일하다.

 

 

 

 

 

 

 

7. AVL 트리

: 모든 노드의 왼쪽과 오른쪽 서브트리의 높이 차이가 1 이하인 이진탐색트리이다.

- 트리가 비균형 상태이면 스스로 노드를 재배치하여 균형 상태로 유지시킨다.

 

 

1) 개념

균형 인수 : (왼쪽 서브트리 높이 - 오른쪽 서브트리 높이)

모든 노드의 균형 인수가 +-1 이하이면 AVL 트리이다.

 

 

2) 시간복잡도

O(logn) : 균형 트리이므로

 

 

 

 

3) 삽입 연산 시,

 

 

 

 

 

 

Ex 1) LL

 

 

 

 

Ex 2) LR

 

 

 

 

Ex 3) RL

 

 

 

 

Ex 4) RR

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형

+ Recent posts