힙 (heap)

IT/알고리즘 2008. 7. 18. 16:06

1. Max Heap 소스

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
#define SIZE 20
 
void print_arr(int arr[], int size)
{
        int i = 0;
 
 
        for( i = 1; i < size; i++)
                printf("%d ", arr[i]);
 
        printf("\n");
 
}
 
void swap(int *arr, int i, int j)
{
        int tmp;
 
 
        tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
 
}
 
void heap(int *arr, int n, int m)
{
        int i;
        int j, parent;
 
 
        for( i = 2 * n; i <= m; i+=2) {
 
                j = i;
                parent = j/2; // this is parent
 
                if(arr[j] < arr[j+1]) // this is children --> 자식 중에 큰 자식을 찾는다.
                        j++;
 
                if( j == m+1)
                        j--;
 
                while( arr[parent] < arr[j]) { // parent와 자식을 비교하여 자식이 크다면 바꿔준다.
                        swap(arr, parent, j);
                        j = parent;
 
                        if( j == 1)
                                break;
 
                        parent = j/2;
                } // while
 
        } // for(i)
 
}

int main(int argc, char **argv)
{
        int arr[SIZE], i;
 
 
        arr[0] = -1;
 
        for( i = 1; i < SIZE; i++)
                arr[i] = rand() % 100;
 
        printf("------------ input data -------------\n");
        print_arr(arr, SIZE);
 
        for( i = SIZE - 1; i >= 1; i--) {
                heap(arr, 1, i); // 제일 큰 놈을 제외한 나머지 요소들로 heapify한다.
                swap(arr, 1, i); // 제일 큰 놈을 뒤로 보낸다. --> 제일 작은 놈을 앞으로 보낸다.
        }
 
 
        printf("------------ sorted data -------------\n");
        print_arr(arr, SIZE);
 
        return 0;
 
}

2. Min Heap 소스
  Max Heap 소스에서 일부만 수정
        for( i = 2 * n; i <= m; i+=2) {
 
                j = i;
                parent = j/2; // this is parent
 
                if(arr[j] > arr[j+1]) // this is children --> 자식 중에 작은 자식을 찾는다.
                        j++;
 
                if( j == m+1)
                        j--;
 
                while( arr[parent] > arr[j]) { // parent와 자식을 비교하여 자식이 작다면 바꿔준다.
                        swap(arr, parent, j);
                        j = parent;
 
                        if( j == 1)
                                break;
 
                        parent = j/2;
                } // while
 
        } // for(i)

블로그 이미지

쩐의시대

나답게 살아가고 나답게 살아가자

,
1. data가 char형인 binary search tree의 자료구조를 작성하라.
typedef struct BTREE {
     char data;
     struct BTREE *left, *right;
} BTREE;


2. 위 binary search tree의 preorder, inorder, postorder traverse를 프로그램하라.
int preorder(BTREE *t)
{
     if ( t != NULL) {
         printf("%c", t->data);
         preorder(t->left);
         preorder(t->right);
     }
}

int inorder(BTREE *t)
{
     if ( t != NULL) {
         inorder(t->left);
         printf("%c", t->data);
         inorder(t->right);
     }
}

int postorder(BTREE *t)
{
     if ( t != NULL) {
         postorder(t->left);
         postorder(t->right);
         printf("%c", t->data);
     }
}

3. swap_tree를 하나의 binary search tree에 대해 대칭이 되는 tree라고 정의할 때, 위 자료구조를 이용해서
   swap_tree를 프로그램하라.
int swap_tree(BTREE *t)
{
    BTREE *p;

    if( t == NULL)
        return NULL;
    else {
        p = (BTREE *) malloc(sizeof(BTREE));
        p->left = swap_tree(t->right);
        p->right = swap_tree(t->left);
        p->data = t->data;
        return p;
    }
}

4. 위 자료구조를 이용해서 tree를 복사하는 프로그램
int tree_copy(BTREE *t)
{
    BTREE *p;

    if( t == NULL)
        return NULL;
    else {
        p = (BTREE *) malloc(sizeof(BTREE));
        p->left = tree_copy(t->left);
        p->right = tree_copy(t->right);
        p->data = t->data;
        return p;
    }
}

5. 위 자료구조를 이용하여 tree의 max depth를 구하는 프로그램을 작성하라.
int max_depth(BTREE *t)
{
    int max = 0, depth;

    if( t == NULL)
        return 0;
    else {
         depth = max_depth(t->left) + 1;
         if( depth > max)
              max = depth;
         depth = max_depth(t->right) + 1;
         if( depth > max)
              max = depth;
         return max;
    }


}
블로그 이미지

쩐의시대

나답게 살아가고 나답게 살아가자

,
a = b-a + (b=a)
블로그 이미지

쩐의시대

나답게 살아가고 나답게 살아가자

,
** Exclusive OR 이용
    --> 양쪽 모두 같은 Bit는 0
          다른 한 쪽이 0 그리고 다른 한 쪽이 1의 비트는 1

int trans(int in)
{

    return (in ^ 0xffffffff) + 1;

}

블로그 이미지

쩐의시대

나답게 살아가고 나답게 살아가자

,
소수(솟수) : 나머지가 0이 나오고 자기자신으로만 나누어지는 수
                예)  2,  3,  5,  7,  11,  13,  17,  19,  23,  29,  31,  37,  41,
                     43,  47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103,
                     107, 109, 113,  ...

#include <stdio.h>
int nextprime(int prime)
{
     int next, isprime, k;
     isprime = 1;
     if( isprime == 1)
         return 2;
     else if( isprime == 2)
         return 3;
     else {
         // 주어진 솟수에 2씩 증가시키며 솟수인지 테스트
        // 소수의 특성상 "2"를 제외한 끝자리는 항상 1, 3, 5, 7, 9 중에 하나이다.
         for( next = prime+2;   ; next += 2, isprime =1 ) {
               // 예) 소수 "13"의 반 값인 7이상으로 제산 연산할 의미가 없다.
               for( k = next/2; k >= 2; k--) {
                 if( next % k == 0 ) {
                     isprime = 0;
                     break;
                 }
             } // for(k)
             if( isprime == 1)
                 return next;
         } // for(next)
         return 0;
     } // else
}
블로그 이미지

쩐의시대

나답게 살아가고 나답게 살아가자

,

<재귀 : pivot을 배열의 중간값으로>

void qsort(int a[], int left, int right)
{
        int p, t;
        int i, j;
 
 
        if( left < right) {
                p = a[(left + right) / 2];
                i = left - 1;
                j = right + 1;
 
                while(1) {
                        while(a[++i] < p);
                        while(a[--j] > p);
 
                        if( i >= j)
                                break;
 
                        t = a[i];
                        a[i] = a[j];
                        a[j] = t;
                }
                qsort(a, left, i-1);
                qsort(a, j+1, right);
        }
 }


<재귀 : pivot을 배열의 첫 번째 값으로>

void qsort(int a[], int left, int right)
{
        int p, t;
        int i, j;
 
 
        if( left < right) {
                p = a[left];
                i = left;
                j = right + 1;
 
                while(1) {
                        while(a[++i] < p);
                        while(a[--j] > p);
 
                        if( i >= j)
                                break;
 
                        t = a[i];
                        a[i] = a[j];
                        a[j] = t;
                }
                /* pivot 값을 교환 */
                a[left] = a[j];
                a[j] = p;
 
                qsort(a, left, j-1);
                qsort(a, j+1, right);
        }
 }

 ** 재귀 함수의 문제점
    배열이 아주 클 경우 스택 오버플로우가 발생할 위험
    --> 자체 스택을 만들어 비재귀하는 방식으로 개선
    --> 구간이 작을 경우 더 이상 재귀 호출을 하지 말고 선택 정렬이나 삽입 정렬 이용


<비재귀 함수>

#include <stdio.h>
 
#define MAX_NUM 50
int stack[MAX_NUM];
int top = 0;
 
void init_stack()
{
        top = 0;
}
 
void push(int i)
{
 
        if( top >= MAX_NUM) {
                printf("stack full\n");
                return;
        }
        else
                stack[top++] = i;
}
 
int pop()
{
 
        if( top == 0)
                return 0;
        else
                return stack[--top];
 
}
 
int is_stack_empty()
{
 
        if( top == 0)
                return 1;
        else
                return 0;
 
}
void qsort(int a[], int n)
{
        int p, t;
        int i, j;
        int l, r;
 
        init_stack();  
 
        l = 0;
        r = n - 1;
 
        push(r);
        push(l);
 
        while(!is_stack_empty()) {
                l = pop();
                r = pop();
 
                if( r-l+1 > 1) {  // 종료조건 :: 남아 있는 분할의 원소 개수가 1개 이상일 경우
                        p = a[r];
                        i = l - 1;
                        j = r;
 
                        while(1) {
                                while(a[++i] < p);
                                while(a[--j] > p);
 
                                if( i >= j)
                                        break;
                       
                                t = a[i];
                                a[i] = a[j];
                                a[j] = t;
 
                        }
 
                        // pivot과 i값을 교환
                        t = a[i];
                        a[i] = a[r];
                        a[r] = t;
 
                        push(r);     // 오른쪽 분할의 오른쪽 끝
                        push(i+1);  // 오른쪽 분할의 왼쪽 끝
                        push(i-1);  // 왼쪽 분할의 오른쪽 끝
                        push(l);     // 왼쪽 분할의 왼쪽 끝
 
                } // if
        } // while
 }

** 비재귀 함수의 문제점
    재귀함수에 비해 실행시간이 약간 빠르다. 그러나, push(), pop()을 사용하므로 함수호출에 의한 오버헤드
    현상으로 속도가 확 달라지지는 않는다.
    정렬된 배열과, 역순 배열에서는 그 속도가 40배 가량 느리다. ( O(n^2) )
    ?? 가장 큰 값이나 가장 작은 값이 pivot으로 되기 때문에 속도 저하
   --> pivot 값을 난수로 지정하는 방법


< 난수 발생>

int get_random(int range)
{
 
        return rand() % range;
 
}
 
 
 
void qsort3(int a[], int n)
{
        int p, t;
        int i, j;
        int l, r;
 
 
        init_stack();  
 
        l = 0;
        r = n - 1;
 
        push(r);
        push(l);
 
        while(!is_stack_empty()) {
 
                l = pop();
                r = pop();
 
                if( r-l+1 > 1) {
                        t = get_random(r-l+1) + l; // 난수 발생
 
                        p = a[t];
                        a[t] = a[r];
                        a[r] = p;
 
                        i = l - 1;
                        j = r;
 
                        while(1) {
                                while(a[++i] < p);
                                while(a[--j] > p);
 
                                if( i >= j)
                                        break;
 
                                t = a[i];
                                a[i] = a[j];
                                a[j] = t;
 
                        }
 
                        t = a[i];
                        a[i] = a[r];
                        a[r] = t;
 
                        push(r);
                        push(i+1);
                        push(i-1);
                        push(l);
 
                } // if
        } // while
}

 이 경우는 최악의 경우가 발생하지 않는다.


<삽입정렬 추가>

void insert_sort(int a[], int n)
{
        int i, j, t;
 
 
        for( i = 1; i < n; i++) {
                t = a[i];
                j = i;
 
                while( a[j-1] > t && j > 0) {
                        a[j] = a[j-1];
                        j--;
                }
 
                a[j] = t;
        }
 
        return;
 
}
 
void qsort4(int a[], int n)
{
        int p, t;
        int i, j;
        int l, r;
 
        init_stack();
 
        l = 0;
        r = n - 1;
 
        push(r);
        push(l);
 
        while(!is_stack_empty()) {
 
                l = pop();
                r = pop();
 
                if( r-l+1 > 200) {
                        t = get_random(r-l+1) + l; // 난수 발생
 
                        p = a[t];
                        a[t] = a[r];
                        a[r] = p;
 
                        i = l - 1;
                        j = r;
 
                        while(1) {
                                while(a[++i] < p);
                                while(a[--j] > p);
 
                                if( i >= j)
                                        break;
 
                                t = a[i];
                                a[i] = a[j];
                                a[j] = t;
 
                        }
                        t = a[i];
                        a[i] = a[r];
                        a[r] = t;
 
                        push(r);
                        push(i+1);
                        push(i-1);
                        push(l);
 
                } // if
                else
                        insert_sort(a+l, r-l+1);
 
        } // while
 }



삽입 정렬은 어느 정도 정렬된 배열에서 아주 큰 효과를 내기 때문에 사용...
메모리의 소모도 줄어듬...


<세 값의 중위>

void qsort4(int a[], int n)
{
        int p, t;
        int i, j;
        int l, r;
 
 
        init_stack();
 
        l = 0;
        r = n - 1;
 
        push(r);
        push(l);
 
        while(!is_stack_empty()) {
 
                l = pop();
                r = pop();
 
                if( r-l+1 > 200) {
                        t = (r+1) >> 1; // 나누기 2의 의미
 
                        if( a[l] > a[t]) {
                                p = a[l];
                                a[l] = a[t];
                                a[t] = p;
                        }
 
                        if( a[l] > a[r]) {
                                p = a[l];
                                a[l] = a[r];
                                a[r] = p;
                        }
 
                        if( a[t] > a[r]) {
                                p = a[t];
                                a[t] = a[r];
                                a[r] = p;
                        }
 
                        p = a[t];
                        a[t] = a[r-1];
                        a[r-1] = p;
 
                        i = l - 1;
                        j = r;
 
                        while(1) {
                                while(a[++i] < p);
                                while(a[--j] > p);
 
                                if( i >= j)
                                        break;
 
                                t = a[i];
                                a[i] = a[j];
                                a[j] = t;
 
                        }
                        t = a[i];
                        a[i] = a[r];
                        a[r] = t;
 
                        push(r);
                        push(i+1);
                        push(i-1);
                        push(l);
 
                } // if
                else
                        insert_sort(a+l, r-l+1);
 
        } // while
 
}


* 복잡도
 - Best Case
   T(n) = n + 2T(n/2)
          = n + 2(n/2 + 2T(n/4)) = 2n + 4T(n/4)
          = ...
          = n log n
 - Worst Case
   T(n) = n + T(n-1)
          = n + (n-1) + T(n-2)
          = ...
          = O(n^2)

블로그 이미지

쩐의시대

나답게 살아가고 나답게 살아가자

,
예를 들어, abc --> a, b, c, ab, bc, ac, abc

#include <stdio.h>
#include <string.h>


void swap(char *p1, char *p2)
{
     char t;

     t = *p1;
     *p1 = *p2;
     *p2 = t;
}


void perm(char *in, int st, int en)
{
    int j = 0;

    if( st == en) {
        for( j = 0; j < en; j++)
            printf("%c", in[j]);
        printf("\n");
    }
    else {
        /* in[st]에서 in[en]까지에 둘 이상의 순열이 있으면 이것을 순환적으로 생성 */
        for( j = st; j < en; j++) {
            SWAP(&in[st], &in[j]);
            perm(in, st+1, en);
            /* 인덱스 i와 인덱스 j의 데이터 위치의 위치 교환을 위해 원래 위치로 복귀*/
            SWAP(&in[st], &in[j]);
        }
    }

}
블로그 이미지

쩐의시대

나답게 살아가고 나답게 살아가자

,
1. mmap이란?
   메모리의 내용을 파일이나 디바이스에 대응하기 위해서 사용하는 시스템 호출이다.

2. 메모리 관리
   프로세스 메모리는 기본적으로 다른 프로세스 메모리와 공유되지 않는다. 이것은 프로세스의 데이터를
   보호하기 위해서 반드시 필요한 기능이긴 하지만 다른 프로세스와 특정 데이터를 공유하기 위해서는
   귀찮은 기능이 되기도 한다.
   이 때문에 IPC를 사용하게 된다.
   mmap은 메모리의 특정영역을 파일로 대응시킬 수 있도록 도아준다.
   파일은 시스템 전역적인 개체이므로 다른 프로세스에서 접근가능하도록 할 수 있으면,
   이러한 mmap의 특징 때문에 IPC용도로 사용가능하다.
   (mmap이 IPC용도로 사용가능하지만 IPC 설비는 아니다.)

   ==> mmap은 프로세스의 주소공간을 파일에 대응시킨다.
        파일은 운영체제 전역적인 자원이므로 당연히 어렵잖게 다른 프로세스와 공유해서 사용할 수 있을 것이다.

3. 활용용도
   1. 메모리의 내용을 파일에 대응시킬 수 있다면 프로세스간 데이터의 교환을 위한 용도로 사용가능 할 것이다.
      접근 제어를 통해 프로세스간 공유하고자 하는 데이터를 파일에 대응시키고 이것을 읽고 쓰면 된다.
   2. 성능향상.
      고전적인 방법은 파일지정자를 얻어서 직접 입출력하는 방식으로 open, read, write, lseek와 같은 함수를
      이용하나, 상당한 비용을 지불.

4. 사용방법
   void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
   int munmap(void *start, size_t length);

   1. start
      메모리 영역에 파일을 대응시킬 시작 offset
   2. length
      start부터 length만큼 대응
   3. prot
      a. PROT_EXEC  : 페이지에 실행될 수 있다.
      b. PROT_READ  : 페이지는 읽혀질 수 있다.
      c. PROT_WIRTE : 페이지는 쓸 수 있다.
      d. PROT_NONE : 페이지를 접근할 수 없다.
   4. flags
     a. MAP_FIXED
     b. MAP_SHARED
        대응되는 객체를 다른 프로세스도 공유할 수 있게 만들어준다.
        프로세스들은 객체에 대해서 동등한 권한을 가지게 되고 이로 인해 동기화가 필요하며, 이를 위해
        msync(), munmap()가 사용
     c. MAP_PRIVATE
        --> MAP_SHARED와 MAP_PRIVATE 둘 중 하나는 반드시 사용해야 한다.
   5. fd
      file descriptor
   6. offset
      대응할 때 파일의 위치를 지정..
      파일의 처음이라면 0
    
5. 성능 비교
   http://developers.sun.com/solaris/articles/read_mmap.html
블로그 이미지

쩐의시대

나답게 살아가고 나답게 살아가자

,

1. 개념
   이 수열은 기본적으로 자연의 황금비율을 설명할 때 가장 많이
   쓰이는 수열입니다.
   수열이 커지면 커질수록 황금비율(1.618)에 근접하게 됩니다.

   한 쌍의 토끼가 매월 한 쌍의 토끼를 낳고, 태어난 한 쌍의 토끼가
   다음 달부터 한 쌍의 토끼를 매월 낳기 시작한다면, 처음 한 쌍의
   토끼로부터 1년간 합계 몇 쌍의 토끼가 태어날 것인가?

2. 특징
   "해당 수와 전의 수를 더한 수를 다음 수로 나타낸 것"
   ex) 데이지 꽃, 코스모스/나뭇가지의 성장

3. 복잡도
   a. 루프를 이용한 구현 : O(n)
   b. 재귀를 이용한 구현 : O(n^2)

4. 구현
   a. 루프를 이용한 구현
      int forfibo(int n)
      {
           int i, tmpe = 0;
           int firstnumber = 0;
           int secondnumber = 1;

           if( n <= 1)
               return n;
          else {
               for( i = 2; i <= n; i++) {
                    temp = firstnumber + secondnumber;
                    firstnumber = secondenumber;
                    secondnumber = temp;
              }
              return secondnumber;
         }
    
}

    b. 재귀를 이용한 구현
       int refibo(int n)
      {
          
if( n <= 1)
               return ;
           else
               return  refibo(n - 1) + refibo(n - 2);
     
}

'IT > 알고리즘' 카테고리의 다른 글

스트링에 대한 permutation을 구하는 알고리즘  (0) 2008.07.15
mmap (메모리맵)  (0) 2008.07.08
문자열을 뒤집는 프로그램  (0) 2008.07.08
call by value vs. call by reference  (0) 2008.07.08
C 프로그램에서 데이터형  (0) 2008.07.08
블로그 이미지

쩐의시대

나답게 살아가고 나답게 살아가자

,
문자열을 뒤집는 프로그램의 알고리즘은 많다.
그 중 대표적인 것을 살펴보자~~

1. 메모리를 사용한 변환
   int rev()
   {
       char string[20] = "this is test";
       char reverse[20];
       int i, j = 0;

       for( i = strlen(string); i >= 0; i--)
          reverse[j++] = string[i];

      reverse[j] = '\0'; // 이 부분을 잘 빠뜨리는데 주의할 것!!

   }

2. 별도의 메모리를 사용하지 않고 변환
   int rev()
   {
       char s[20] = "this is test";
       char temp;
       int len = strlen(s);
       int i;

       for( i = 0; i < len/2 ; i++) {
          temp = s[i];
          s[i] = s[len - 1 - i];
          s[len - 1 - i] = temp;
       }

   }

   문자가 2byte를 기본으로 하는 한국어일 경우는 힘들겠죠??

3. 재귀함수 이용
   char *s = "this is test";

   void rec(void)
  {
      if( *p == NULL) // 종결조건
         return;

      p++; // 전진방법
      rec();
      p--; // 수행방법
      putchar(*p);

      return;
   }

   * recursive(재귀함수) 코딩하는 방법에서 꼭 갖추어야 할 요건
     1. 전진방법
     2. 종결조건
     3. 수행방법

4. 스택 이용
    void rev(char string[])
    {
       int i;
       int top = 0;

       for( i = 0; s[i] != '\0'; i++)
          PUSH(&top, s[i]);

       for( i = 0; s[i] != '\0'; i++)
          s[i] = POP(&top);

    }

'IT > 알고리즘' 카테고리의 다른 글

mmap (메모리맵)  (0) 2008.07.08
피보나치 수열  (2) 2008.07.08
call by value vs. call by reference  (0) 2008.07.08
C 프로그램에서 데이터형  (0) 2008.07.08
프로세스의 메모리 구조  (4) 2008.07.08
블로그 이미지

쩐의시대

나답게 살아가고 나답게 살아가자

,