트리 정렬은 이진 탐색 트리를 이용하여 정렬하는 방법이다.

정렬할 원소들을 이진 탐색 트리로 구성하고 중위 우선 순회 방법을

사용하여 이진 탐색 트리의 원소들을 순회하여 꺼내면 오름차순 정렬이 된다.

 트리 정렬에서 원소들을 비교하여 이진 탐색 트리를 구성하는 시간이

O(log₂n) 이므로 n개의 노드에 대해서 이진 탐색 트리를 구성하는

시간 복잡도는 O(nlog₂n) 가 된다.
Posted by 바람처럼..
|
히프 정렬은 히프 자료구조를 이용하여 정렬하는 방법이다. 히프에서는 항상 가장 큰 원소가 루트 노드가 되고, 삭제 연산을 수행하면 항상 루트 노드의 원소를 삭제하여 반환하는 성질이 있다. 그러므로 최대 히프에 대해서 원소의 개수만큼 삭제 연산을 수행하면 내림차순으로 정렬된 원소를 얻을 수 있다. 
 
 히프 정렬은 정렬할 원소들을 입력하여 최대 히프를 구성한다. 그리고 히프에 대해서 삭제 연산을 수행하여 얻은 원소를 마지막 자리에 배치하고 히프를 다시 최대 히프가 되도록 재구성하는 작업을 원소의 개수만큼 반복하면 오름차순의 정렬을 완성할 수 있다.

 히프에 삭제 연산을 반복 수행하여 오름차순의 정렬된 원소를 구하기는 쉽지만, 삭제 연산을 수행한 후에 히프를 재구성하는 연산 수행 시간이 필요하다. n개의 노드에 대해서 완전 이진 트리는 log₂(n+1)의 레벨을 가지므로 완전 이진 트리를 히프로 구성하는 평균 시간은 O(log₂n) 이 된다. 따라서 n개의 노드에 대해서 히프의 평균 재구성 시간은 O(nlog₂n)가 된다.  

아래의 동영상을 참고하면 이해가 편하다.


Posted by 바람처럼..
|
기수정렬 기수정렬 기수정렬 기수정렬 기수정렬 기수정렬 기수정렬

기수 정렬은 분배 방식의 정렬 방법으로 정렬할 원소의 키값에 해당하는 버킷에 원소를 분배하였다가 버킷의 순서대로 원소를 꺼내는 방법을 반복한다. 기수 정렬은 원소의 키를 표현하는 값의 기수만큼의 버킷이 필요하고, 키값의 자릿수만큼 기수 정렬을 반복한다. 10진수로 표현된 키값을 가진 원소들을 정렬할 때는 0부터 9까지 10개의 버킷을 사용한다. 

 먼저 키값의 일의 자리에 대해서 기수 정렬을 수행하고, 다음 단계에서는 키값의 십의 자리에 대해 그리고 그 다음 단계에서는 백의 자리에 대해서 기수 정렬을 수행한다. 한 단계가 끝날 때마다 버킷에 분배된 원소들을 버킷의 순서대로 꺼내서 다음 단계의 기수 정렬을 수행해야 하므로 큐를 사용하여 버킷을 만든다.

기수 정렬의 수행 시간은 정렬할 원소의 수 n과 키값의 자릿수 d와 버킷의 수를 결정하는 기수 r에 따라 달라진다. 정렬할 원소 n개를 r개의 버킷에 분배하는 작업이 n+r이고, 이 작업을 자릿수 d만큼 반복해야 하므로 수행할 작업은 d(n+r)이 된다. 따라서 시간복잡도는 O(d(n+r))이 된다.

기수정렬 기수정렬 기수정렬 기수정렬 기수정렬 기수정렬 기수정렬

Posted by 바람처럼..
|

[자료구조] 병합정렬(merge sort)[자료구조] 병합정렬(merge sort)
병합 정렬은 여러 개의 정렬된 자료의 집합을 결합하여 한 개의 정렬된 집합으로 만드는 방법이다. 병합 정렬은 전체 원소들에 대해서 수행하지 않고 부분집합으로 분할하고 각 부분집합에 대해서 정렬 작업을 완성한 후에 정렬된 부분집합을 다시 결합하는 분할 정복 기법을 사용한다. 
 
 2개의 정렬된 자료의 집합을 결합하여 하나의 집합으로 만드는 병합 방법을 2-way 병합이라 하고, n개의 정렬된 자료의 집합을 결합하여 하나의 집합으로 만드는 병합 방법을 n-way 병합이라고 한다. 2-way 병합 정렬은 다음과 같은 작업을 반복 수행하면서 완성한다.

(1) 분할 : 입력 자료를 같은 크기의 부분집합 2개로 분할한다.

(2) 정복 ; 부분집합의 원소들을 정렬한다. 부분집합의 크기가 충분히 작지 않으면 순환 호출을 이용하여 다시 분할 정복 기법을 적용한다.

(3) 결합 : 정렬된 부분집합들을 하나의 집합으로 통합한다.

병합 정렬은 각 단계에서 새로 병합하여 만든 부분집합을 저장할 공간이 추가로 필요하기 때문에 정렬할 원소 n개에 대해서 2xn개의 메모리 공간을 사용한다. n개의 원소를 분할하기 위해서 log₂n번의 단계를 수행하고, 부분집합의 원소를 비교하면서 병합하는 단계에서 최대 n번의 비교 연산을 수행하므로 전체 병합 정렬의 시간 복잡도는 O(nlog₂n )이 된다.

아래의 동영상을 보면 이해하기 쉬울 것이다.


[자료구조] 병합정렬(merge sort)[자료구조] 병합정렬(merge sort)

Posted by 바람처럼..
|
[자료구조] 셸 정렬(shell sort)...[자료구조] 셸 정렬(shell sort)...

셸 정렬은 일정한 간격으로 떨어져 있는 자료들끼리 부분집합을 구성하고 각 부분집합에 있는 원소들에 대해서 삽입 정렬을 수행하는 작업을 반복하면서 전체 원소들을 정렬하는 방법이다. 전체 원소에 대해서 삽입 정렬을 수행하는 것보다 부분 집합으로 나누어 정렬하게 되면 비교 연산과 교환 연산의 횟수를 줄일 수 있다.

셸 정렬에서 부분집합을 만드는 기준이 되는 간격을 매개변수 h에 저장하고 한 단계가 수행될 때마다 h의 값을 감소시키고 셸 정렬을 순환 호출하는데, 결국 h가 1이 될 때 까지 반복한다. 셸 정렬의 성능은 매개변수 h의 값에 따라 달라진다. 일반적으로 사용하는 h의 값은 원소 개수의 ½을 사용하고 한 단계 수행될 때마다 h의 값을 반으로 감소시키면서 반복 수행된다.

셸 정렬은 n개의 자료에 대한 메모리 공간과 매개변수를 저장할 공간을 사용한다. 비교횟수는 처음 원소의 상태에 상관없이 매개변수 h에 의해 영향을 받기 때문에 알고리즘의 성능을 분석하기가 쉽지 않은데, 일반적으로 셸 정렬의 시간복잡도를 O(n¹¼)로 측정한다.

아래의 동영상을 참고하면 이해가 훨씬 쉬울 것이다.



[자료구조] 셸정렬(shell sort)...[자료구조] 셸정렬(shell sort)...
Posted by 바람처럼..
|


삽입 정렬은 정렬되어 있는 부분집합에 정렬할 새로운 원소의 위치를 찾아 삽입하는 방법이다. 삽입 정렬에서는 정렬할 자료가 두 개의 부분집합 S와 U로 나뉘어 있다고 생각하면 이해가 쉽다. 
 앞부분 원소부터 정렬을 수행하는데, 정렬된 앞부분의 원소들은 부분집합 S가되고, 아직 정렬되지 않은 나머지 원소들은 부분집합 U가 된다. 정렬되지 않은 부분집합 U의 원소를 하나씩 꺼내서 이미 정렬되어 있는 부분집합 S의 마지막 원소부터 비교하면서 위치를 찾아 삽입하여 부분집합 S의 원소는 하나씩 늘리고 부분집합 U의 원소는 하나씩 줄인다.
 U의 원소를 모두 삽입하여 공집합이 되면 삽입 정렬이 완성된다.

n개의 자료를 정렬하는 삽입 정렬에서 사용하는 메모리 공간은 n개가 된다. 삽입 정렬에서 원소들을 비교하는 횟수가 최소인 경우는 이미 원소들이 정렬되어 있는 경우다. 이미 정렬되어 있는 경우에는 바로 앞자리 원소와 비교하는 연산이 한번만 필요하다.
 따라서 최소의 비교 횟수는 n-1이 되어 시간 복잡도가 O(n)이 된다. 그리고 모든 원소가 역순으로 되어 있는 경우에는 비교 횟수가 최대가 되어 시간 복잡도가 O(n²)가 된다.

 따라서 삽입 정렬의 평균 시간 복잡도는 O(n²)이 된다.

다음은 간단한 삽입정렬 알고리즘이다..

void insertSort(int a[], int n)
{
 int j, temp ;
 for(int i = 0 ; i < n ; ++i )
 {
  temp = a[i] ;
  j = i ;
  while ((j>0) && (a[j-1]>temp))
  {
   a[j] = a[j-1] ;
   j = j-1 ;
  }
  a[j] = temp ;
 }
}

그리고 다음은 삽입정렬에 대해서 쉽게 이해할 수 있는 동영상이다.


Posted by 바람처럼..
|


퀵 정렬(quick sort )은 정렬할 전체 원소에 대해서 정렬을 수행하지 않고 기준값을 중심으로 왼쪽 부분집합과 오른쪽 부분집합으로 분할한다. 왼쪽 부분 집합에는 기준값보다 작은 원소들을 이동시키고, 오른쪽 부분집합에는 기준값보다 큰 원소들을 이동시킨다.
 이 때 사용하는 기준값을 피봇(pivot)이라고 하는데, 일반적으로 전체 원소 중에서 가운데에 위치한 원소를 피봇으로 선택한다.
 퀵 정렬은 다음의 두 가지 기본 작업을 반복 수행하여 완성한다.

(1)분할(divide): 정렬할 자료들을 기준값을 중심으로 2개의 부분집합으로 분할 한다.

(2)정복(conquer) : 부분집합의 원소들 중에서 기준값보다 작은 원소들은 왼쪽 부분집합으로, 기준값보다 큰 원소들은 오른쪽 부분집합으로 정렬한다. 부분집합의 크기가 1 이하로 충분히 작지 않으면 순환 호출을 이용하여 다시 분할한다.

분할 작업을 순환적으로 반복하면서 피봇의 왼쪽 부분집합과 오른쪽 부분집합을 정렬하는 방법으로 전체 원소들을 정렬한다. 부분집합으로 분할하기 위해서는 L과 R을 사용한다. 왼쪽 끝에서 오른쪽으로 움직이면서 크기를 비교하여 피봇보다 크거나 같은 원소를 찾아 L로 표시하고, 오른쪽 끝에서 왼쪽으로 움직이면서 피봇보다 작은 원소를 찾아 R로 표시한 후에 두 원소를 서로 교환한다. L과 R이 만나면 피봇과 R의 원소를 서로 교환하여 피봇의 위치를 확정한다.
 피봇의 확정된 위치를 기준으로 만들어진 왼쪽 부분집합과 오른쪽 부분집합에 대해서 퀵 정렬을 순환적으로 반복 수행하는데, 부분집합의 크기가 1 이하가 되면 퀵정렬을 종료한다.

간단한 퀵정렬 소스 이다.

int partition(int a[], int begin, int end)
{
 int pivot, temp, L, R ;
 L = begin ;
 R = end ;
 pivot = (begin+end)/2 ;

 while(L<R)
 {
  while((a[L]<a[pivot]) && (L<R)) L++ ;
  while((a[R]>=a[pivot]) && (L<R)) R-- ;
  if(L<R)
  {
   temp = a[L] ;
   a[L] = a[R] ;
   a[R] = temp ;
  }
 }

 temp = a[pivot] ;
 a[pivot] = a[R] ;
 a[R] = temp ;

 return L ;  
}

void quickSort(int a[], int begin, int end)
{
 if(begin<end)
 {
  int p ;
  p = partition(a, begin, end) ;
  quickSort(a, begin, p-1) ;
  quickSort(a, p+1, end) ;
 }
}

그리고 아래의 동영상을 참고 하면 퀵정렬을 이해하기 좀더 쉬울것이다.

Posted by 바람처럼..
|
[자료구조] 버블 정렬 ( bubble sort )...

버블 정렬 (bubble sort )은 인접한 두개의 원소를 비교하여 자리를 교환하는

방식으로, 첫 번째 원소부터마지막 원소까지 반복하면 가장 큰 원소가 마지막

자리에 온다. 첫 번째 원소부터 인접한 원소끼리 계속 자리를 교환하면서 맨 마

지막 자리로 이동하는 모습이 물 속에서 물 위로 올라오는 물방울 모양과 같다

고 해서 버블 정렬이라고 한다.

버블 정렬을 함수로 구현하면 다음과 같다.

void bubbleSort(int a[], int n )
{
   int i, j, temp ;
   for ( i = n-1; i >=0; --i )
   {
      for ( j= 0 ; j <= i; ++j)
      {
         if ( a[j-1] >a [j] )
         {
            temp = a [j-1] ;
            a[j-1] = a [j] ;
            a[j] = temp ;
         }
     }
   }
}

버블 정렬에서 사용하는 메모리의 크기는 정렬할 원소의 개수 n이 된다. 버블

정렬에서 연산 시간이 가장 짧은 최선의 경우는 자료들이 이미 정렬되어있는

경우다. 이런 경우에는 i 번째 원소는 n - i 번 비교하게 되므로 비교 횟수는

n(n-1)/2가 된다. 그리고 이 경우에 원소의 자리 교환은 발생하지 않는다. 

버블 정렬에서 비교 횟수가 가장 많은 경우는 역순으로 정렬되어 있는 경우인

데 이 경우에도 i 번째 원소는 n-i번 비교하게 되므로 비교 횟수는 n(n-1)/2 이

되고, 자리 교환을 n-i번 하게 되므로 교환 횟수도 n(n-1)/2이 된다. 

버블 정렬에서 비교 횟수에 대한 시간 복잡도는 O(n²)이 된다.  

아래의 동영상을 보면.. 확실히 이해가 될 것이다..



Posted by 바람처럼..
|
[자료구조] 선택 정렬...
선택 정렬(selection sort)은 전체 원소들 중에서 기준 위치에 맞는 원소를 선택

하여 자리를 교환하는 방식으로 정렬한다. 전체 원소 중에서 가장 작은 원소를

찾아서 선택하고 첫번째 원소와 자리를 교환한다. 그 다음 두번 째로 작은 원소

를 찾아서 두번째 원소와 자리를 교환하고, 그 다음에는 세 번째로 작은 원소를
찾아서 세 번째 원소와 자리를 교환한다. 이 과정을 반복하면서 정렬을 완성한다.
 
정확한 자료의 값 이동은 맨 아래의 동영상을 참고하면 된다.

(유투브에서 검색하면 바로 나오는 거라서 일단 퍼왓는데 문제가 된다면 삭제

하겠다.)

간단한 선택정렬 함수이다.

void selectionSort(int a[], int n )
{
   int i, j, min, temp ;
   for( i =0 ; i < n-1; ++i )
   {
      min = i ;
      for ( j= i+1; j < n; ++j )
      {
         if ( a[j] < a[min] ) min = j ;
      }
      temp = a [i] ;
      a [i] = a [min] ;
      a[min] = temp ;
   }
}

n 개의 원소에 대한 선택 정렬은 n개의 메모리를 사용한다. 선택 정렬에서의 비

교 횟수를 구해보면, 1단계에서는 첫 번째 원소를 기준으로 n개의 원소를 비교

하고, 2단계에서는 두 번째 원소를 기준으로 마지막 원소까지 n-1개의 원소를

비교하고, 3단계에서는 세 번째 원소를 기준으로 마지막 원소까지 n-2개의 원

소를 비교한다. 이러한 방법으로 i단계에서는 i번째 원소를 기준으로 n-i개의 원

소를 비교하게 되므로 전체 비교 횟수는 n(n-1)/2 와 같다. 

선택정렬에서는 어떤 경우에서나 비교 횟수가 같으므로 시간 복잡도는 O(n²)이
된다. 




  
Posted by 바람처럼..
|

[자료구조] 정렬

정렬이란 순서 없이 배열되어 있는 자료들을 작은 것부터 큰 것 순서의 오름차

순이나 큰 것부터 작은 것 순서의 내림차순으로 재배열 하는 것이다. 할일을 순

서대로 정하는 것이나 가게에서 음식을 유통기한 순서대로 냉장고에 진열하는

것, 도서관에서 도서번호 순서대로 책들을 정리하는 것, 이름 순서로 기록되어

있는 전화번호부 등 일상 생활에서 정렬을 사용하고 있다.

자료를 정렬하는데 사용하는 기준이 되는 특정 값을 키라고 한다. 특정 이름이

라던지, 수치 등을 이용할 수 있는데 중복되는 값이 여서는 안된다.

정렬 방법은 실행하는 방법과 정렬이 수행되는 장소에 따라서 분류될 수 있다.


실행 방법에 따른 분류

정렬은 실행하는 방법에 따라 비교식 정렬과 분산식 정렬로 구분할 수가 있다.

비교식 정렬은 비교하고자 하는 각 키값들을 한번에 두개씩 비교하여 교환함으

로써 정렬을 실행하는 방식이다. 그리고 분산식 정렬은 키값을 기준으로 하여

자료를 여러 개의 부분집합으로 분해하고, 각 부분집합을 정렬함으로써 전체를
정렬하는 방식이다.


정렬 장소에 따른 분류

컴퓨터에서 수행되는 정렬은 컴퓨터 메모리 내부에서 정렬하는 내부정렬과 메

모리의 외부인 보조 기억 장치에서 정렬하는 외부 정렬로 분류할 수 있다.

Posted by 바람처럼..
|

트리를 통해 자료를 계층적인 구조로 저장하고 사용하기 위해서는 트리에 있는
모든 노드를 방문하는 방법이 필요하다. 이진 트리에있는 모든 노드를 한번씩

모두 방문하여 노드가 가지고 있는 데이터를 처리하는 것을 순회 라고 한다. 리

스트나 스택, 큐와 같은 선형 자료구조는 순회하는 방법이 한 가지 였지만, 트

리는 계층구조를 가지고 있기 때문에 여러가지 순회 방법이 있을 수 있다. 현재
노드를 방문하는 순서에 따라 전위 순회, 중위 순회, 후위 순회로 나눌 수 있다.
1. 전위 순회

(1) 현재 노드 n을 방문한다. ( 그 위치의 자료를 가져오거나 확인한다. )

(2) 현재 노드 n의 왼쪽 서브 트리로 이동한다.

(3) 현재 노드 n의 오른쪽 서브 트리로 이동한다.

이렇게 만 놓고 보면 왼쪽으로 갔다가 다시 바로 오른쪽으로 갈 것 같지만, 실

제 구현된 기능은 왼쪽으로 쭉 타고 내려간다. 그리고 더이상 왼쪽으로 갈 수

없다면 오른쪽, 오른쪽이 없다면 다시 부모 노드로, 그리고 다시 오른쪽을 확인
혹시 오른쪽에 노드가 있다면 거기로 갔다가 다시 왼쪽을 확인.. 이런식의 반복

이다..

코드로 구현하자면, 재귀호출을 이용하여 구현하면 간단하다.

2. 중위 순회

(1) 현재 노드 n의 왼쪽 서브 트리로 이동한다.

(2) 현재 노드 n을 방문한다.

(3) 현재 노드 n의 오른쪽 서브 트리로 이동한다.

중위 순회는 현재 노드를 방문하는 작업을 L과 R 노드 작업 중간에 수행하는 것

이다. 이 것은 일단 왼쪽 노드를 쭉 찾아내려간다.. 그리고 최종적인 왼쪽 노드

로 가면, 값을 읽고, 부모 노드로 돌아와서 값을 읽는다. 그 후에 다시 오른쪽노

드로 가서.. 또 왼쪽 노드부터 값을 읽는다.. 이 것의 반복인 것이다.

3. 후위 순회

(1) 현재 노드 n의 왼쪽 서브 트리로 이동한다.

(2) 현재 노드 n의 오른쪽 서브 트리로 이동한다.

(3) 현재 노드 n을 방문한다.

후위 순회도 일단 위의 순회들과 마찬가지로 왼쪽으로 쭉 노드를 찾아내려간

다. 그 후에 다시 부모로 돌아왔다가 오른쪽 노드를 확인을 하고, 그 오른쪽에

값이 있다면 또 왼쪽으로 가다가 최종적으로 돌아와서 자기의 값을 확인하고

또 돌아가고를 반복한다.

이렇게 글로만 보면 헷갈릴 수 있는데, 실제로 이진 트리를 하나 그려놓고, 전

위, 중위, 후위의 순서로 데이터를 읽어보고 방법이 맞는지 다시 검토해 보면

쉽게 이해가 될 것이라 생각된다.











'프로그래밍 > 자료구조' 카테고리의 다른 글

[자료구조] 선택 정렬 ( Selection Sort )...  (0) 2011.10.17
[자료구조] 정렬  (0) 2011.10.17
[자료구조] 이진 트리..  (0) 2011.10.12
[자료구조] 트리란...  (0) 2011.10.10
[자료구조] 큐의 응용..  (0) 2011.10.04
Posted by 바람처럼..
|
[자료구조] 이진 트리..[자료구조] 이진 트리..[자료구조] 이진 트리..
이진 트리란...


트리의 노드 구조를 일정하게 정의하면 트리의 연산이 쉬워진다. 그래서 모든

노드의 차수를 2 이하로 정하여 전체 트리의 차수가 2 이하가 되도록 만든 것이
이진 트리다.

이진 트리는 구별된 2개의 왼쪽, 오른쪽 자식 노드만을 가질 수 있으며, 공백 노

드도 이진 트리의 노드로 취급한다.

이진 트리에 있는 모든 노드는 왼쪽 자식 노드를 루트로 하는 왼쪽 서브 트리와
오른쪽 자식 노드를 루트로 하는 오른쪽 서브 트리를 갖는다. 그리고 이진 트리

의 서브 트리들 역시 모두 이진 트리여야 한다.



이진 트리의 특징...



1. n개의 노드를 가진 이진 트리는 항상 (n-1)개의 간선을 가진다.

2. 높이가 n인 이진 트리가 가질 수 있는 노드의 최소 개수는 (n+1)개가 되며,

최대 개수는 (2ⁿ+¹ - 1)  개 이다.


이진 트리의 분류....


포화 이진 트리 : 포화 이진 트리는 모든 레벨에 노드가 꽉 차서 포화 상태인 이

진 트리를 의미한다. 즉 포화 이진 트리는 최대 노드수를 갖는 이진 트리다.


완전 이진 트리: 완전 이진 트리는 높이가 h 노드의 갯수가 n 이라면, 높이가 h

인 포화 이진트리의 노드 순서와 n 개 까지가 동일한 이진 트리이다. 

쉽게 얘기하면, n 개 까지의 순서는 포화 이진 트리와 똑 같지만 그 이후의 나머

지 노드는 공백 노드인 이진 트리가 완전 이진 트리이다.  


편향 이진 트리 : 이진 트리 중에서 최소 개수의 노드를 가지면서 왼쪽이나 오

른쪽 서브 트리만 가지고 있는 트리를 편향 이진 트리라고 한다.

[자료구조] 이진 트리..[자료구조] 이진 트리..[자료구조] 이진 트리..
Posted by 바람처럼..
|
[자료구조] 트리란...[자료구조] 트리란...[자료구조] 트리란...

리스트와 스택, 큐는 자료들이 선의 형태로 나열되어 있는 구조를 가진 선형 자

료구조였다. 자료들이 나열된 구조가 선형이 아닌 자료구조를 비선형 자료구조

라고 하는데, 트리(tree)는 비선형 자료구조 중에서 자료들간에 계층관계를 가

진 계층형 자료구조다. 흔히 우리가 알고있는 가계도가 바로 계층형 자료구조

이다.

가계도에서 가족 구성원을 연결하는 선은 부보 자식 관계를 나타낸다. 가계도

의 시작을 루트로 보고, 그 아래 자식노드는 1세대(레벨1)그 아래의 자식노드들

은 2세대(레벨 2) 라고 볼 수 있다.

트리를 구성하는 원소는 노드라고 하고 노드를 연결하는 선을 간선(edge)이라

고 한다. 같은 부모 노드의 자식 노드들은 서로 형제 노드가 된다. 한 노드에서

간선을 따라 루트 노드까지 이르는 경로에 있는 노드들은 모두 그 노드의 조상

노드가 된다. 자식 노드들은 각각 독립하여 새로운 트리를 구성할 수 있으므로

각 노드의 자식 노드 수만큼의 서브 트리를 갖는다. 한 노드의 자손 노드들은

그 노드의 서브 트리에 있는 노드들이 된다.

한 노드가 가지는 서브 트리의 수, 즉 자식 노드의 수를 그 노드의 차수라 한다.
한 노드의 높이는 루트에서 그 노드에 이르는 경로에 있는 간선의 수가 되고,

노드의 높이 중에서 가장 큰 값, 즉 최대 레벨이 그 트리의 높이가 된다.

나무가 모이면 숲이 되듯이 여러 트리들의 집합을 포레스트 라고 한다. n개의

서브트리를 가진 루트 노드를 제거하면 n개의 분리된 트리가 생겨서 포레스트

를 이룬다.

[자료구조] 트리란...[자료구조] 트리란...[자료구조] 트리란...
Posted by 바람처럼..
|
[자료구조] 큐의 응용..
컴퓨터의 여러 분야에서 발생한 순서대로 문제를 해결하는 경우에 선입선출 운

영 구조의 큐를 사용한다. 컴퓨터 운영체제에서 실행을 요청한 작업들을 순서

대로 처리하기 위해서 버퍼 큐와 프로세스 스케줄링 큐를 사용하고, 산업공학

등의 분야에서는 최적의 시스템을 설계하기 위한 시뮬레이션에서 대기 행렬 큐

를 사용한다.

1. 운영체제의 작업 큐

큐는 각기 다른 속도로 실행되는 작업의 처리 시간을 조화시키기 위해 사용할

수 있다. 프린터를 사용하여 출력하는 경우를 생각해보자. cpu에서 데이터를

내보내는 출력속도는 프린터의 출력 속도보다 훨씬 빠르다. 만약 cpu가 프린터

의 출력이 끝날때까지 기다렸다가 다음 데이터를 내보낸다면 기다리는 시간만

큼 cpu도 다른 작업을 수행 할 수 없게 되어 작업 효율이 떨어지게 된다. 그래

서 운영체제에서 프린터 버퍼 큐 를 사용하게 한다. cpu는 출력으로 내보네는

데이터를 프린터 버퍼 큐에 삽입하고 프린터가 버퍼 큐에 있는 작업을 순서대

로 처리하면서 처리가 끝난 작업은 버퍼 큐에서 삭제한다. 이처럼 처리 속도가

다른 처리기 사이에서 처리 속도를 맞추기 위해 큐를 사용하는데, 이를 버퍼 큐

라고 한다.

컴퓨터 운영체제에서는 cpu를 사용하고자 하는 프로세스들에 대해 cpu 사용

스케줄을 관리하기 위해서 스케줄링 큐를 사용한다. 스케줄링 큐는 준비 큐

와 대기 큐로 구성한다. cpu를 사용하고자 하는 프로세슫르을 준비 큐에 삽이

하면순서대로 준비큐에서 꺼내서cpu를 사용하게 한다. cpu를 사용하던 프로세

스가다른 처리를 기다리는 대기 상태가 되면 대기 큐에 삽이하여 대기 상태의

프로세스들을 순서대로 관리한다.

2. 시뮬레이션에서의 큐잉 시스템

시뮬레이션이란 실제로 일을 실행하기 전에 미리 실험해보는 것이다. 시뮬레이

션을 하려면 먼저 시스템에 대한 수학적인 모델링 작업을 해야한다. 예를 들어,

공항을 새로 만들면서 입국심사 창구를 몇개 만들어야 할지를 시뮬레이션한다

고 하면 "비행기가 몇 분마다 도착하는가?", "한 명을 심사하는데 시간이 얼마나
소요되는가?", "어느 때에 가장 입국자가 많은가?"등을 모델링해야 하는데,

이러한 모델링에 사용되는 통계적인 이론이 큐잉 이론 이다. 큐잉 이론에서는

서비스를 받기 위해 기다리는 대기 행렬과 대기 시간을 실험하는데 큐를 사용

한다. 공항에 도착하는 입국자는 큐로 만든 대기 행렬에 순서대로 들어가고,

입국 심사관은 큐에 있는 입국자들을 순서대로 처리하게 된다. 모든 서비스가

끝나서 대기 행렬 큐가 공백이 되기까지의 평균 대기 시간을 여러 상황에 대해

시뮬레이션 하여 최적의 시스템을 설계 한다.

[자료구조] 큐의 응용..[자료구조] 큐의 응용..[자료구조] 큐의 응용..
Posted by 바람처럼..
|

[자료구조] 연결 자료구조를 이용한 큐의 구현..
순차 자료구조를 이용하여 구현한 큐에는 몇 가지 단점이 있다. 사용 크기가 제

한되어 큐의 길이를 마음대로 사용할 수 없고, 원소가 없을 때에도 항상 그 크

기를 유지하고 있어야 하므로 메모리도 낭비 된다는 점이다. 이와 같은 문제를

극복하기 위해 연결 자료구조를 이용하여 크기 제한이 없는 연결 큐(Linked

Queue)를 구현해 보자.

연결 큐에서 원소는 데이터 필드와 링크 필드를 가진 노드로 구성하며, 첫 번째
노드를 가리키는 front 포인터와 마지막 노드를 가리키는 rear 포인터를 사용한

다. 연결 큐의 초기 상태(공백 큐)는 front와 rear를 NULL 포인터로 설정하여 표

현한다.

다음은 간단히 구현한 소스 이다.

#include <stdio.h>
#include <malloc.h>

typedef struct QNode{
 char data ;
 struct QNode *link ;
} QNode;

typedef struct {
 QNode *front, *rear ;
} LQueueType ;

LQueueType* createLinkedQueue()
{
 LQueueType *LQ ;
 LQ = (LQueueType*)malloc(sizeof(LQueueType)) ;
 LQ->front = NULL ;
 LQ->rear = NULL ;

 return LQ ;
}

bool isEmpty(LQueueType* LQ)
{
 if(LQ->front == NULL)
 {
  printf("\n Linked Queue is empty! \n") ;
  return true ;
 }else
  return false ;
}

void enQueue(LQueueType* LQ, char item )
{
 QNode* newNode = (QNode*)malloc(sizeof(QNode)) ;
 newNode->data = item ;
 newNode->link = NULL ;

 if(LQ->front == NULL)
 {
  LQ->front = newNode ;
  LQ->rear = newNode ;
 }else
 {
  LQ->rear->link = newNode ;
  LQ->rear = newNode ;

 }
}

char deQueue(LQueueType* LQ)
{
 QNode* old = LQ->front ;
 char item ;

 if(isEmpty(LQ)) return 0 ;
 else
 {
  item = old->data ;
  LQ->front = LQ->front->link ;

  if(LQ->front == NULL )
   LQ->rear = NULL ;

  free(old) ;

  return item ;
 }
}

void printfLQ(LQueueType* LQ)
{
 QNode* temp = LQ->front ;
 printf("\n Linked Queue : [") ;

 while(temp){
  printf("%3c", temp->data) ;
  temp = temp->link ;
 }
 printf("]") ;
}

void main()
{
 LQueueType* LQ1 = createLinkedQueue() ;
 
 enQueue(LQ1, 'a') ;
 printfLQ(LQ1) ;

 enQueue(LQ1, 'b') ;
 printfLQ(LQ1) ;

 deQueue(LQ1) ;
 printfLQ(LQ1) ;

 enQueue(LQ1, 'c') ;
 printfLQ(LQ1) ;

 deQueue(LQ1) ;
 printfLQ(LQ1) ;

 deQueue(LQ1) ;
 printfLQ(LQ1) ;
}


이전의 선형구조로 소스를 짯을 때랑 화면상으로는 큰차이는 없다..

하지만 선형구조가 크기의 한계가 있다면, 연결 구조는 크기의 구애 받지 않고,
계속 늘었다, 줄었다 할 수 있기 때문에, 훨씬 여러곳에서 활용할 수 있다.

[자료구조] 연결 자료구조를 이용한 큐의 구현..
Posted by 바람처럼..
|

[자료구조] 순차 자료구조를 이용한 큐의 구현.

1. 선형 큐

1차원 배열을 사용하는 순차 자료구조로 큐를 구현해보자. 이때 배열의 크기는

큐의 크기, 즉 큐에 저장할 수 있는 최대 원소 갯수가 된다. 그리고 배열에 저장

된 원소 중에서, 첫 번째 원소의 인덱스를 저장할 front 변수와 저장된 마지막

원소의 인덱스를 저장할 rear 변수를 사용한다. 초기 공백 큐의 상태는 front 변

수와 rear 변수의 값을 -1로 설정한다.

정리된 소스는 아래와 같다... ( 정말 간단히 구현만 했다. )

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

#define Q_SIZE 100

char queue[Q_SIZE] ;

int front, rear ;

void enQueue( char item)
{
 if(rear == Q_SIZE -1 )
 {
  printf("\nQueue is full!\n" ) ;
  return ;
 }else
 {
  rear++ ;
  queue[rear] = item ;
 }
}

void deQueue()
{
 if( front == rear )
 {
  printf("\nQueue is empty!\n" ) ;
  return ;
 }else
 {
  front++ ;
 }
}

void printfQueue()
{
 printf("Queue : [") ;
 for(int i = front+1; i <= rear ; ++i )
  printf("%3c", queue[i]) ;

 printf(" ] \n") ;
}

void main()
{
 front = -1 ;
 rear = -1 ;

 enQueue('a') ;
 printfQueue() ;

 enQueue('b') ;
 printfQueue() ;

 deQueue() ;
 printfQueue() ;

 enQueue('c') ;
 printfQueue() ;

 deQueue() ;
 printfQueue() ;

 deQueue() ;
 printfQueue() ;
}

실행해 보면..

위와 같은 결과가 나온다...

하지만 위와 같은 소스는, 배열에 모든 정보가 꽉 찬 이후에는 동작을 하지 않

는다.. 바로 그 것을 해결하기 위해 나온 것이 원형 큐다..

2. 원형 큐

위의 선형 큐와 같은 경우는 rear가 배열의 마지막 위치에 있게 되면 큐에 빈자

리가 있는데도 포화 상태로 인식하고 삽입연산을 수행하지 않는다.

원형 큐는 선형 큐와 마찬가지로 1차원 배열을 사용하는데, 논리적으로 배열의

처음과 끝이 연결되어 있다고, 생각하는 것이다.

원형 큐에서는 초기 공백 상태일 때 front와 rear의 값이 0이 되고, 공백 상태와

포화 상태를 쉽게 구분하기 위해서 자리 하나를 항상 비워둔다. 원형 큐에서의

공백 상태는 front = rear 가 된다. rear가 앞으로 이동하면서 원소를 삽입하고,

front는 rear가 이동한 방향으로 따라가면서 원소를 삭제한다. 원형 큐에서는 배

열의 인덱스가 n-1 다음에 다시 0이 되어야 하므로 사용할 인덱스를 정하기 위

해서 나머지 연산자 mod를 사용한다.

원형 큐는 위의 소스를 바탕으로 직접 해보길 바란다.

[자료구조] 순차 자료구조를 이용한 큐의 구현.


Posted by 바람처럼..
|


큐는 스택과 마찬가지로 삽입과 삭제의 위치와 방법이 제한된 유한 순서 리스

트지만, 스택과 달리 리스트의 한쪽 끝에서는 삽입 작업이 이루어지고 반대쪽

끝에서는 삭제 작업이 이루어져서, 삽입된 순서대로 삭제되는 선입선출(FIFO,

First In Fiirst Out ) 의 구조로 이루어져있다.

예를 들면, 은행에 들어온 순서대로 번호표를 뽑게 하고 번호표 순서대로 일을

처리해주는 운영방식이 큐라고 볼 수 있다.

큐는 한쪽 끝을 프런트(front)로 정하여 삭제 연산만 수행하도록 하고, 다른쪽

끝을 리어(rear)로 정하여 삽입 연산만 수행하도록한다. 큐에서 프런트 원소는

가장 먼저 큐에 들어온 첫번재 원소고, 리어 원소는 큐에 가장 늦게 들어온 마

지막 원소가 된다. 가장 먼저 들어온 프런트 원소가 가장 먼저 삭제된다. 

큐의 리어에서 이루어지는 삽입 연산을 인큐(enQueue)라고 하고, 프런트에서

이루어지는 삭제 연산을 디큐(deQueue)라고 한다.  
Posted by 바람처럼..
|
[자료구조] 스택의 응용

스택은 알고 보면, 상당히 활용 할 곳이 많은 자료구조이다.

몇가지 스택을 응용하여 사용하는 것을 알아보자.

1. 역순 문자열 만들기.

스택의 LIFO 성질을 이용하여 문자열에 대한 역순 문자열을 간단히 만들 수 있

다. 문자열을 처음부터 순서대로 스택에 삽입한다. 
 
그 후 스택에 있는 원소들을 공백 스택이 될 때 까지 삭제하면서 반환된 문자를
나열하면 원래 문자열의 역순 문자열이 된다.

2. 시스템 스택

프로그램간의 호출과 복귀에 따른 수행 순서를 보면 호출한 순서와 복귀하는

순서는 반대기 때문에 가장 나중(last)에 호출된 함수가 가장 먼저(First) 실행

을 완료하고 복귀한다. 따라서 함수의 호출과 복귀 순서는 스택의 구조를 응용

하여 관리할 수 있다. 이 때 사용하는 스택을 시스템 스택이라 한다.

함수나 서브프로그램이 호출되면 일단 호출한 함수 수행에 필요한 지역변수,

매개변수 및 수행 후 복귀할 주소 등의 정보를 스택 프레임에 저장하여 시스템

스택에 삽입한다. 시스템 스택의 top은 항상 현재 실행중인 함수에 대한 스택

프레임이 된다. 함수의 실행이 끝나면 시스템 스택의 top원소를 삭제하면서 프

레임에 저장되어 있던 복귀 주소를 확인하고 복귀한다. 함수 호출과 복귀에 따

라 이 과정을 반복하여, 전체 프로그램 수행이 종료되면 시스템 스택은 공백 스

택이 된다.

3. 수식의 괄호 검사.

수식은 일반적으로 연산자와 피연산자로 구성되어 있으며, 왼쪽에서 오른쪽 순

서로 처리한다. 수식에 사용한 연산자의 우선순위가 다를경우에는 우선순위가

높은 연산자를 먼저 처리하는데, 괄호를 사용하여 우선순위를 표현하기도 한

다. 이 때 괄호는 일반괄호, 중괄호, 대괄호 를 사용한다. 괄호는 왼쪽 괄호와

오른쪽 괄호가 반드시 쌍을 이루어야 하며, 여러 개의 괄호가 중첩된 경우에는

가장 안쪽의 괄호를 먼저 처리한다.

괄호가 포함된 수식에서 괄호의 쌍이 제대로 사용되었는지를 검사하기 위해서

스택을 사용할 수 있다. 수식을 읽으면서 왼쪽 괄호를 만나면 스택에 push하고
오른쪽 괄호를 만나면 스택을 pop한다. 현재의 오른쪽 괄호와 pop한 왼쪽 괄호

가 같은 종류의 괄호면 괄호의 쌍이 맞는 것이고, 수식의 처리가 모두 끝났을

때 스택이공백 스택이 되면 왼쪽 괄호와 오른쪽 괄호의 갯수가 맞는 것이다.

4. 수식의 후위 표기법 변환.

연산자와 피연산자로 구성된 수식을 표기하는 방법은 연산자의 위치에 따라 다

음의 세가지로 나눌 수 있다.

◀ 전위 표기법
 
연산자를 앞에 표기하고 그 다음에 피 연산자를 표기하는 방법 예) +AB

◀ 중위 표기법

연산자를 피연산자의 가운데에 표기하는 방법 예) A+B

◀ 후위 표기법

연산자를 피연산자 뒤에 표기하는 방법 예) AB+

중위표기법의 수식을 후위표기법으로 변환하는 방법을 알아보자.

수식 A*B-C/D를 후위 표기법으로 변환하면,

1) 수식의 각 연산자에 대해서 우선순위에 따라 괄호를 사용하여 다시 표현한다.

((A*B)-(C/D)

2) 각 연산자를 그에 대응하는 오른쪽 괄호의 뒤로 이동시킨다.

((AB)*(CD)/)-

3) 괄호를 제거한다..

AB*CD/-

우리가 일반적으로 사용하는 표기법은 중위 표기법이지만, 컴퓨터 내부에서 수

식을 처리하기에 가장 효율적인 방법은 후위 표기법이다. 후위 표기법을 사용

하면 괄호나 연산자 우선순위를 따로 처리하지 않고 왼쪽에서 오른쪽으로 표기

된 순서대로 처리할 수 있다. 우리가 컴퓨터에 중위 표기법 형태의 수식을 입력

하면 컴퓨터 내부에서는 효율적인 처리를 위해 스택을 사용하여 입력된 수식을
후위 표기법으로 변환하게 된다.

5. 후위 표기 수식의 연산.

컴퓨터 내부에서 후위 표기법의 수식을 연산할 때도 스택을 사용할 수 있다. 스

택을 사용하여 수식을 계산하는 방법은,

1) 피연산자를 만나면 스택에 삽입한다.

(2) 연산자를 만나면 필요한 만큼의 피연산자를 스택에서 pop하여 연산하고,

연산 결과를 다시 스택에 삽입한다.

(3) 수식이 끝나면, 마지막으로 스택을 pop하여 출력한다.

Posted by 바람처럼..
|


이전 포스팅의 순차 자료구조를 이용한 스택은 구현하기는 쉽지만, 물리적으로
크기가 고정된 배열을 사용하기 때문에 스택의 크기를 변경하기가 어렵고 메모

리의 낭비가 생길 수 있다는 문제가 있다. 이러한 순차 자료구조의 문제는 연결
자료구조를 사용함으로써 해결할 수 있다.

연결 자료 구조 중에서 단순 연결 리스트를 이용하여 스택을 구현하면 스택의

원소는 연결리스트의 노드가 된다. 스택에 원소를 삽입할 때 마다 연결 리스트

에 노드를 하나씩 연결한다. 그리고 스택 원소의 순서는 연결 리스트 노드의 링

크를 사용하여 표현한다. 스택의 top을 표현하기 위해서 노드에 포인터 top을

사용한다. 스택의 초기 상태는 포인터 top을 NULL 포인터로 설정하여 표현한

다.

다음은 간단히 구현해본 연결 자료구조 스택 소스 이다...

#include <stdio.h>

typedef struct stackNode{

 int data ;
 struct stackNode *link ;

}stackNode ;

stackNode* top ;

void push(int Num)
{
 stackNode* temp = new stackNode ;
 temp->data = Num ;
 temp->link = top ;
 top = temp ;
}

int pop()
{
 int Num ;
 stackNode* temp = top ;

 if(top == NULL )
  return 0 ;
 else
 {
  Num = temp->data ;
  top = temp->link ;
  delete temp ;
  return Num ;
 }
}

void printStack()
{
 stackNode* p = top ;
 
 printf("STACK [") ;

 while(p)
 {
  printf(" %d ", p->data ) ;
  p = p->link ;
 }
 printf(" ] \n ") ;
}

void main()
{
 top = NULL ;
 
 printStack() ;

 push(1) ;

 printStack() ;

 push(2) ;

 printStack() ;

 push(3) ;

 printStack() ;

 pop() ;

 printStack() ;

 pop() ;

 printStack() ;

 pop() ;

 printStack() ;
}

순차 구조와 거의 비슷하지만, 각 함수별로 저장하는 방식만 조금 다를 뿐이

다... 그리고 할당된 메모리를 해제하는 부분이 값을 빼는 부분에만 있기 때문

에 값을 많이 넣고, 그냥 종료해버리면 메모리누수가 생길 것이다..

이 것은 조금만 고민하면 알 수 있는 것이니 소스를 조금씩 개인적으로 수정해

서 쓰길 바란다..

Posted by 바람처럼..
|


스택은 수차 자료구조인 1차원 배열을 이용하여 스택을 구현할 수 있다. 1차원

배열인 stack[n]을 사용할 때 n은 배열 크기로서 배열원소의 갯수를 나타내는

데, 이것이 스택의 크기다. 스택에 원소가 쌓이는 순서는 배열의 인덱스로 표현

한다. 따라서 스택의 첫 번째 원소는 stack[0]에 저장되고, 스택의 두 번째 원

소는 stack[1]에 저장되고, 스택의 i번째 원소는 stack[i-1]에 저장된다.

스택이 top을 표현하기 위해서 배열 stack의 마지막 원소의 인덱스를 변수 top

에 저장한다. 변수 top에 0부터 n-1까지의 인덱스를 저장하게 되므로 스택의 초

기 상태에는 -1을 저장한다..

아래는 간단히 구현한 스택의 소스 이다.

#include <stdio.h>

#define STACK_SIZE 100

int stack[STACK_SIZE] ;

int top = -1 ;

void push(int Num)
{
 if( top >= STACK_SIZE - 1)
  return ;
 else stack[++top] = Num ;
}

int pop()
{

 if( top == -1)
  return 0 ;
 else return stack[top--] ;
}

void printStack()
{
 printf("Stack [ ") ;
 
 for( int i = 0 ; i <= top ; ++i )
  printf("%d ", stack[i]) ;
 
 printf("] \n") ;

}


void main()
{
 printStack() ;

 push(1) ;

 printStack() ;

 push(2) ;

 printStack() ;

 push(3) ;

 printStack() ;

 pop() ;

 printStack() ;

 pop() ;

 printStack() ;

 pop() ;

 printStack() ;
}

정말 간단한 구조이기 때문에 그냥 스택이 이런거구나 하는데만 쓰면 된다..

Posted by 바람처럼..
|


스택(stack)이란 쌓아 올린다는 의미다. 따라서 스택 자료구조라는 것은 접시

를 쌓듯이 자료를 차곡차곡 쌓아 올린 형태의 구조를 말한다.

스택 자료구조의 스택은 같은 구조와 크기의 자료를 정해진 방향으로만 쌓을

수 있고, top 이라고 정한 한 곳으로만 접근하도록 제한되어 있다.

스택에서는 top을 통해서 들어온 자료가 일정한 방향으로 차곡차곡 쌓이게 된

다. 스택에서의 top은 현재 스택의 가장 위에 있는 마지막 자료를 가리키고 있

고, 삽입되는 새 자료는 top이 가리키는 자료의 위에 쌓이게 된다. 그러면 삽입

된 자료는 스택의 마지막 자료가 되고, 이 때 top은 삽입된 자료를 마지막 자료

로 가리킨다. 스택에서 자료를 삭제할 때 에도 역시 top을 통해서만 가능하다.

따라서 스택은 시간 순서에 따라 자료가 쌓여서 가장 마지막에 삽입된 자료가

가장 먼저 삭제된다는 구조적 특징을 갖는다. 이러한 스택의 구조를 후입선출

(LIFO, Last-In-First-Out) 이라고 표현한다.

스택에서 top을 통한 삽입 연산을 push, top을 통한 삭제 연산을 pop이라고 한

다.
Posted by 바람처럼..
|

순차 선형 리스트는 논리적인 순서와 물리적인 순서가 같기 때문에 원소의 위

치를 찾아 엑세스 하기 쉽다는 장점이 있지만, 삽입 연산이나 삭제 연산 후에

연속적인 물리 주소를 유지하기 위해서 원소들을 이동시키는 추가적인 작업과

시간이 소요된다. 원소들의 이동 작업으로 인한 오버헤드는 원소의 개수가 많

고, 삽입,삭제 연산이 많이 발생하는 경우에 더 많이 발생한다. 또한 순차 자료

구조는 배열을 이용하여 구현하기 때문에 배열이 갖고 있는 메모리 사용의 비

효율성 문제를 그대로 갖는다.

순차 자료구조에서의 연산 시간에 대한 문제와 저장 공간에 대한 문제를 개선

한 자료 표현 방법으로 연결 자료구조 또는 비순차 자료구조가 있다. 연결 자료

구조에서는 순차 자료구조에서처럼 원소의 논리적인 순서와 물리적인 순서가

일치할 필요가 없다. 연속한 물리주소에 의해 원소의 순서를 표현하는 것이 아

니라 각 원소에 저장되어 있는 다음 원소의 주소에 의해 순서가 연결되는 방식

이기 때문에 물리적인 순서를 맞추기 위한 오버헤드가 발생하지 않는다. 하나

의 순차 자료구조를 위해서는 하나의 고정크기 메모리 공간을 사용하지만, 연

결 자료구조에서는 여러개의 작은 공간을 연결하여 전체를 표현하므로 크기 변

경이 유연하고 좀더 효율적으로 메모리를 사용할 수 있다.

연결 리스트는 리스트를 연결 자료구조로 표현한 구조로서 연결하는 방식에 따

라 단순 연결 리스트와 원형 연결 리스트, 이중 연결 리스트, 이중 원형 연결 리

스트로 나눌수 있다.

연결 자료 구조에서 원소는 연결될 다음 원소에 대한 주소를 저장해야 하기 때

문에 <원소, 주소> 의 단위로 저장해야한다. 이러한 단위 구조를 노드 라고 한

다. 노드는 원소의 값을 저장하는 데이터 필드와 다음 노드의 주소를 저장하는

링크 필드로 구성된다. 데이터 필드는 저장할 원소의 형태에 따라서 하나 이상

의 필드로 구성되기도 한다. 링크 필드는 포인터 변수를 사용하여 주소값을 저

장하며, 포인터 또는 링크, 참조 라고도 한다.

그리고 연결 자료 구조에서는 헤드 라고 불리는 시작 주소가 있다. 그 헤드는

시작 주소이면서, 첫 데이터를 가르키는 역할도 한다. 연결 리스트의 마지막 노

드는 다음에 연결되는 노드가 없으므로 링크 필드에 저장할 주소값이 없는데,

이럴때는 노드의 끝을 표시하기 위해 마지막 노드의 링크 필드에 NULL을 저장

한다. 만약 아무런 노드도 없다면 헤드에 NULL을 저장하여 널 포인터로 만들면
된다.
Posted by 바람처럼..
|


선형 리스트를 순차구조로 구현하기 위해서 배열을 사용한다. 배열은 <인덱스,
원소 > 의 쌍으로 구성되어 메모리에 연속적으로 할당되는데, 이때 인덱스는

배열 원소의 순서를 타나낸다. 배열은 순서를 가진 배열 원소들을 메모리에 연

속하여 구성하므로 프로그램 언어에서 제공하는 배열을 그대로 사용하여 구현

할 수가 있다.

그럼 가장 간단한 1차원 배열의 순차 표현을 알아보자.

1차원 배열은 인덱스를 하나만 사용하는 배열이다. 원소의 순서를 하나의 값으

로 구별할 수 있는 간단한 선형 리스트는 1차원 배열을 사용하여 표현할 수 있

다.

예를 들어 학교의 학생수에 대해서 생각해보자.

1학년에 4반이 있는데, 각 반에는 35명, 36명, 39명, 37명 이렇게 있다고 치자.

이 것을 배열을 통해 나타내면,

int student[4] =  { 35, 36, 39, 37 } ;

이렇게 표현할 수 있다.

1차원 배열 student는 int로 선언 되었으므로, 각 원소의 길이는 4바이트가 된

다. C 프로그래밍에서 배열의 인덱스는 0부터 시작하므로 배열 sale의 각 원소

의 위치는 시작 주소가 a일 때 a + ( 인덱스 * 4바이트 ) 가 된다.

이번엔 2차원 배열의 순차 표현을 알아보자. 

위의 1차원 배열과 마찬가지로 2차원배열은 학교의 학생수 분류에서 학년을 추

가해 보자.

1학년에 4반, 2학년에 4반, 3학년에 4반이 있다.

그렇게 되면, 

int student[3][4] = { { 35, 36, 39, 37 }, 
                             { 37, 36, 38, 39 }, 
                             { 34, 37 ,38 ,37 } } ;
이렇게 표현 할 수 있다.

2차원 배열의 구조를 논리적으로 표현할 때는 행과 열의 구조로 나타내지만, 실

제로 메모리에 저장될 때에는 1차원의 순서로 저장된다. 2차원의 논리적 순서

가 1차원의 물리적 순서로 변환되는 방법은 첫 번째 인덱스를 기준으로 하는 행
우선 순서와 마지막 인덱스를 기준으로 하는 열 우선 순서 방법이 있다.

간단히 말해서 행 우선 방법은 student[0][0] -> student[0][1]->student[0]

[2] 이런식으로 우선 0 의 0~3까지 그다음은 1의 0~3까지. 이런식으로 앞의 행

의 값을 쭉 넣고, 다음 행의 값을 넣는 방식이다.

그리고 열 우선 방법은 student[0][0] ->student[1][0]->student[2][0] 이렇

게 먼저 앞의 0의 0 값, 1의 0값, 2의 0값을 넣고 다음에 0의 1값, 1의 1값, 2의 1

값 이렇게 차례대로 넣는 방식이다. 

행 우선 방법과 열 우선 순서 방법에 따라 물리적 저장 순서가 다르기 때문에

배열의 원소 위치를 계산하는 방법도 달라져야 한다. 행의 갯수가 n1 이고, 열

의 갯수가 n2인 2차원 배열 A[n1][n2]의 시작 주소가 a고, 원소의 길이가 L 일

때, i행 j 열의 원소, 즉 A[i][j]의 위치는 행 우선 구조에서는 a + ( i * n2 + j ) *
L 이 되고, 열 우선 순서 구조에서는 a + (j * n1 + i) * L 이 된다.

논리 순서를 물리 순서로 변환하는 방법은 프로그래밍 언어의 컴파일러에 의해

서 결정되는데, C 컴파일러는 행 우성 순서 방법을 사용한다.

3차 배열도 이와 유사한 방법을 사용한다. 단지 배열의 크기가 지금은 행과 열

이였다면, 거기에 면이 하나 추가되는 개념이며, 논리적 구조를 물리적 구조로

바꾸는 공식은 조금만 생각하면 유추할 수 있으므로, 자세한 설명은 생략한다.
Posted by 바람처럼..
|


문제를 해결하기 위해 추상적으로 정의한 자료와 연산자는 구체적으로 구현되

어 실행되어야 하는데, 연산자의 구현은 처리할 자료를 표현하는 방법에 의해..

결정된다.

따라서 처리할 문제에 대한 자료를 가장 효율적인 방법으로 표현하는 것이 중

요하다.

자료구조에서 기본적인 자료 표현 방법으로는 순차 자료구조와 연결 자료구조

가 있다..

여기서는 순차 자료 구조를 알아보자..

가장 기초적인 자료를 표현하는 방법은 나열 하는 것이다. 동물이면 동물,

식물, 음식, 지리 등등을 나열한 목록을 list 라고 한다..

list 나열한 원소들 간에 순서를 가지고 있는 list를 선형 list 또는 순서 list라고

한다.

리스트를 표현하는 일반적인 방법은 다음과 같다..

list 이름 = ( 항목1, 항목2, 항목 3 )

선형 list는 원소들 같의 논리적인 수서와 메모리에 저장하는 물리적인 순서가

같은 구조로 되어잇는데, 이러한 구조를 순차 자료구조라고 한다..

따라서 순차 자료구조에서는 원소의 논리적인 순서대로 메모리에 연속적으로

저장된다..

즉, 순차 자료구조에서는 원소들이 순서대로 연속 저장되기 때문에 시작 위치

와 원소의 길이를 알면, 특정 원소의 위치를 알 수 있다.

시작 위치가 a고, 원소의 길이가 L 인 list에서 두번째 원소의 길이는

a + L 이고, 세번째 원소의 위치는 a + 2L 이다. 따라서 i 번째 원소의 위치는

a + (i -1 ) * L 이 된다..

 그렇다면, 선형 list 에서의 원소 삽입에 대해 알아보자...

 밥을 먹으로 갔는데, 밥집에 줄이 서있다....

일단 기다린다.. 그런데 누군가 새치기를 했다... (물론 이런경우는 없을 것이지

만 예를 들면이다..)

그렇다면 순번은 한칸씩 뒤로 밀린다...

이 것이 바로 선형 list 에서의 원소 삽입이다...

무언가 list에 순서대로 자료가 저장되어 있지만, 가운데 원소를 하나 집어 넣으

려면, 먼저 삽입할 자리를 만든 후에 그 자리에 원소를 삽입해야 된다..

그렇게 하려면, 메모리에 연속해서 저장되어 있는 자료들을, 한칸씩 뒤로 밀어

야 하겠다..

(n+1) 개의 원소로 이루어진 선형 list 에서 k번 자리에 원소를 삽입하려면,

k번 원소부터 마지막 원소인 n번째  원소까지 n - k + 1 개의 원소를 모두 한자

리씩 뒤로 밀어야 하므로 빈자리를 만들기 위한 이동 횟수는 n - k + 1 이 된다.

그렇다면 반대로 선형 list에서의 원소 삭제를 알아보자.

이번에는 반대로 차례대로 밥집의 줄을 서있는데, 갑자기 앞에 사람이 어디로

가버렸다.. 그렇다면 순서는 한칸씩 앞으로 당겨지게 된다...

이번에는 코드 상으로는 빠진 부분의 값은 뒤의 값이 덮어 버리면 되기 때문에

바로 값이 빠진 곳 부터 한칸씩 앞으로 값을 땡기기만 하면 된다...

이 것을 식으로 쓰면, (n+1)개의 원소로 이루어진 선형 list 에서 k번 자리의

원소를 삭제했다면 k+1번 원소부터 마지막 n번 원소까지 n - ( k + 1 ) + 1개의

원소를 모두 한자리씩 앞으로 옮겨야 하므로 필요한 이동 횟수는 n - ( k + 1 ) +
1 이 되어 정리하면, n - k 가 된다...

Posted by 바람처럼..
|


컴퓨터 알고리즘을 공부하다 보면 자료구조 라는 이야기가.. 항상 같이 나온다...

그렇다면 자료구조는 무엇인가??

쉽게 예를 들어보면, 문구점에 가서 볼펜을 사려 할 때, 빨강, 파랑, 검정, 모두 어지럽게

한 공간에 꽂혀 있다면, 우리는 한 눈에 바로 내가 원하는 물건인지 확인을 하기 위해 하나씩 꺼내 봐야 할 지도 모른다...

하지만, 각 칸을 나눠서 여기는 빨강, 파랑, 검정, 그 중에서도 굵은 펜, 가는 펜 이렇게 분류가 되어있다면,

우리가 의사결정을 하는데 더 쉽고 수월할 것이다...

이것이 바로 자료구조인 셈이다...

이 것을 프로그램의 입장에서 바꿔보면, 데이터들이 중구난방으로 펼쳐져 있다면, 쉽게 접근하기도 어려울 뿐더러..

혹시 사용하게 되더라도 검증을 하거나, 처음부터 하나씩 빼서 확인하는 수 밖에 없다..

하지만 구역 별로 데이터들이 나눠져 있다면, 필요한 조건이 아닌 부분은 제외하고 검색을 하거나 할 수 있다..

이것은 바로 프로그램의 속도의 향상을 가져온다...

이렇게 같은 동작을 하는 프로그램이더라도, 얼마나 설계를 잘 했느냐, 꼭 필요한 자료구조를 사용하였나에 따라서

내부적으로 (혹은 외부적으로도) 더 빠르고, 버그가 없는 프로그램이 될 수 있을 것이다.

자료구조는 이론적인 부분과 효율적인 부분을 모두 다뤄야한다.

이론적으로 그래프 이론, 집합이론 등의 이론을 바탕으로 알고리즘을 분석하여 검색, 정렬 방법을 결정하고

효율적인 부분에서는 최상의 상태를 분석하여 결정해야한다..

이렇게 다양한 자료구조에 대해 이해하고, 논리적이고 수학적인 분석을 통해 최적의 알고리즘을 개발할 수 있는

이론적인 능력을 갖춰야지만 실제 시스템을 개발할 수 있는 고급 개발자가 될 수 있을 것이다..

자료구조는 크게 3가지로 나눌 수 있다..

선형구조, 비선형 구조, 파일 구조 이다...

선형 구조는 자료들간의 앞뒤 관계가 일대일로 고정되어 있는 구조로서, 리스트, 연결리스트, 스택, 큐, 덱 등이 있다.

비 선형 구조는 자료들간의 계층 구조나 망 구조를 갖는 자료구조로 트리와 그래프가 있다..

파일 구조는 서로 관련 있는 필드들로 구성된 레코드의 집합인 파일에 대한 자료구조로서, 보조기억장치에 데이터가

실제로 기록되는 자료구조다. 파일의 구성 방식에 따라 순차 파일과 색인 파일, 직접 파일 등이 있다...
Posted by 바람처럼..
|