1.05k likes | 1.55k Views
11. 검색 / 탐색 (Search). 학습목표 자료에 대한 검색의 개념을 이해한다 . 순차 검색의 개념과 알고리즘을 알아본다 . 이진 검색의 개념과 알고리즘을 알아본다 . 해싱에 대해 알아보고 다른 검색 알고리즘과의 차이를 이해한다 . 내용 검색 순차 검색 이진 검색 이진 트리 검색 해싱. 1. 검색 / 탐색. 검색 / 탐색 (search) 컴퓨터에 저장한 자료 중에서 원하는 항목을 찾는 작업 검색 성공 - 원하는 항목을 찾은 경우 검색 실패 – 원하는 항목을 찾지 못한 경우
E N D
11 검색/탐색(Search)
학습목표 • 자료에 대한 검색의 개념을 이해한다. • 순차 검색의 개념과 알고리즘을 알아본다. • 이진 검색의 개념과 알고리즘을 알아본다. • 해싱에 대해 알아보고 다른 검색 알고리즘과의 차이를 이해한다. • 내용 • 검색 • 순차 검색 • 이진 검색 • 이진 트리 검색 • 해싱
1. 검색/탐색 • 검색/탐색(search) • 컴퓨터에 저장한 자료 중에서 원하는 항목을 찾는 작업 • 검색 성공 - 원하는 항목을 찾은 경우 • 검색 실패 –원하는 항목을 찾지 못한 경우 • 탐색 키를 가진 항목을 찾는 것 • 탐색 키(search key) - 자료를 구별하여 인식할 수 있는 키 • 삽입/삭제 작업에서의 검색 • 원소를 삽입하거나 삭제할 위치를 찾기 위해서 검색 연산 수행
1. 검색 • 검색 방법 • 수행 위치에 따른 분류 • 내부 검색 - 메모리 내의 자료에 대해서 검색 수행 • 외부 검색 - 보조 기억 장치에 있는 자료에 대해서 검색 수행 • 검색 방식에 따른 분류 • 비교 검색 방식(comparison search method) • 검색 대상의 키를 비교하여 검색하는 방법 • 순차 검색, 이진 검색, 트리 검색 • 계산 검색 방식(non-comparison method) • 계수적인 성질을 이용한 계산으로 검색 하는 방법 • 해싱 • 검색 방법의 선택 • 자료 구조의 형태와 자료의 배열 상태에 따라 최적의 검색 방법 선택
2. 순차 검색 • 순차 검색(sequential search, 선형 검색(linear search)) • 일렬로 된 자료를 처음부터 마지막까지 순서대로 검색하는 방법 • 가장 간단하고 직접적인 검색 방법 • 배열이나 연결 리스트로 구현된 순차 자료 구조에서 원하는 항목을 찾는 방법 • 검색 대상 자료가 많은 경우에 비효율적이지만 알고리즘이 단순하여 구현이 용이함
2. 순차 검색 • 순차 검색 –정렬되지 않은 순차자료구조에서의 순차 검색 • 검색 방법 • 첫 번째 원소부터 시작하여 마지막 원소까지 순서대로 키 값이 일치하는 원소가 있는지를 비교하여 찾는다. • 키 값이 일치하는 원소를 찾으면 그 원소가 몇 번째 원소인지를 반환 • 마지막 원소까지 비교하여 키 값이 일치하는 원소가 없으면 찾은 원소가 없는 것이므로 검색 실패 • 순차 검색 예) 검색 성공의 경우
2. 순차 검색 • 순차 검색 예) 검색 실패의 경우
2. 순차 검색 • 정렬되어있지 않은 자료에 대한 순차검색 알고리즘 • 비교횟수 - 찾고자 하는 원소의 위치에 따라 결정 • 찾는 원소가 첫 번째 원소라면 비교횟수는 1번, 두 번째 원소라면 비교횟수는 2번, 세 번째 원소라면 비교횟수는 3번, 찾는 원소가 i번째 원소이면 i번, … • 정렬되지 않은 원소에서의 순차 검색의 평균 비교 횟수 = 1/n(1+2+3+ … + n) = (n+1)/2 • 평균 시간 복잡도 : O(n)
2. 순차 검색 : [예제 11-1] • 정렬되어있지 않은 자료에 대한 순차검색 C 프로그램 • 검색 대상 자료 : {8, 30, 1, 9, 11, 19, 2} - 7개 • 검색 1 : 탐색키 9 검색 ☞ 검색 성공! • 검색 2 : 탐색키 6 검색 ☞ 검색 실패! • 실행 결과 >
2. 순차 검색 : [예제 11-1] 01 #include <stdio.h> 02 03 void sequentialSearch1(int a[], int n, int key) 04 { 05 int i=0; 06 printf("\n %d를 검색하여라! ->> ", key); 07 while (i<n && a[i]!=key) i++; 08 if(i<n) printf("%d번째에 검색 성공! \n\n", i+1); 09 else printf("%d번째에 검색 실패! \n\n", i+1); 10 } 11 12 void main() 13 { 14 int a[]={8, 30, 1, 9, 11, 19, 2}; 15 int n=7; 16 17 sequentialSearch1(a, n, 9); 18 sequentialSearch1(a, n, 6); 19 20 getchar(); 21 }
2. 순차 검색 • 순차 검색 –정렬되어 있는 순차자료구조에서의 순차 검색 • 검색 방법 • 순서대로 검색하면서 키 값을 비교하여, 원소의 키 값이 찾는 키 값보다 크면 찾는 원소가 없는 것이므로 더 이상 검색을 수행하지 않고 검색종료 • 정렬되어있는 자료에 대한 순차 검색 예)
2. 순차 검색 • 정렬되어있는 자료에 대한 순차검색 알고리즘 • 비교횟수 - 찾고자 하는 원소의 위치에 따라 결정 • 검색 실패의 경우에 평균 비교 횟수가 반으로 줄어든다. • 정렬되어있는 원소에서의 순차 검색의 평균 비교 횟수 = 1/n(1+2+3+ … + n) x 1/2 = (n+1)/4 • 평균 시간 복잡도 : O(n)
2. 순차 검색 : [예제 11-2] • 정렬되어있는 자료에 대한 순차검색 C 프로그램 • 검색 대상 자료 : {1, 2, 8, 9, 11, 19, 29} – 7개 • 검색 1 : 탐색키 9 검색 ☞ 검색 성공! • 검색 2 : 탐색키 6 검색 ☞ 검색 실패! • 실행 결과 >
2. 순차 검색 : [예제 11-2] 01 #include <stdio.h> 02 03 void sequentialSearch2(int a[], int n, int key) 04 { 05 int i=0; 06 printf("\n %d를 검색하여라! ->> ", key); 07 while (a[i] < key) i++; 08 if(a[i]==key) printf("%d번째에 검색 성공!\n\n", i+1); 09 else printf("%d번째에 검색 실패! \n\n", i+1); 10 } 11 12 void main() 13 { 14 int a[]={1, 2, 8, 9, 11, 19, 29}; 15 int n=7; 16 17 sequentialSearch2(a, n, 9); 18 sequentialSearch2(a, n, 6); 19 20 getchar(); 21 }
2. 순차 검색 • 색인 순차 검색(index sequential search) • 정렬되어있는 자료에 대한 인덱스 테이블(index table)을 추가로 사용하여 탐색 효율을 높인 검색 방법 • 인덱스 테이블 • 배열에 정렬되어있는 자료 중에서 일정한 간격으로 떨어져있는 원소들을 저장한 테이블 • 자료가 저장되어있는 배열의 크기가 n이고 인덱스 테이블의 크기가 m일 때, 배열에서 n/m간격으로 떨어져있는 원소와 그의 인덱스를 인덱스 테이블에 저장 • 검색 방법 • indexTable[i].key ≤ key < indexTable[i+1].key를 만족하는 i를 찾아서 배열의 어느 범위에 있는지를 먼저 알아낸 후에 해당 범위에 대해서만 순차 검색 수행
2. 순차 검색 • 색인 순차 검색 예 • 검색 대상 자료 : {1, 2, 8, 9, 11, 19, 29} • 크기가 3인 인덱스 테이블 작성 • 인덱스 테이블에서 먼저 탐색키를 검색하여 검색 범위를 확인하고, 해당 범위에 대해서만 순차 검색 실행
2. 순차 검색 : [예제 11-3] • 색인 순차 검색 C 프로그램 • 검색 대상 자료 : {1, 2, 8, 9, 11, 19, 29} – 7개 • 인덱스 테이블 크기 : 3 • 검색 1 : 탐색키 9 검색 ☞ 검색 성공! • 검색 2 : 탐색키 6 검색 ☞ 검색 실패! • 실행 결과 >
2. 순차 검색 : [예제 11-3] 01 #include <stdio.h> 02 03 #define INDEX_SIZE 3 04 05 typedef struct{ 06 int index; 07 int key; 08 } itable; 09 itable indexTable[INDEX_SIZE]; 10 11 void sequentialSearch2(int a[], int begin, int end, int key) 12 { 13 int i=begin; 14 15 printf("\n %d를 검색하여라! ->> ", key); 16 while(i<end && a[i]<key) i++; 17 18 if(a[i]==key) printf("%d번째에 검색 성공!\n\n", (i-begin)+1); 19 else printf("%d번째에 검색 실패! \n\n", (i-begin)+1); 20 } 21
2. 순차 검색 : [예제 11-3] 22 void makeIndexTable(int a[], int size) 23 { 24 int i, n; 25 n=size/INDEX_SIZE; 26 if(size%INDEX_SIZE > 0) n=n+1; 27 for(i=0; i<INDEX_SIZE; i++){ 28 indexTable[i].index = i*n; 29 indexTable[i].key = a[i*n]; 30 } 31 } 32 33 void indexSearch(int a[], int n, int key) 34 { 35 int i, begin, end; 36 if (key < a[0] || key > a[n-1]) 37 printf("\n찾는 키가 없습니다. 검색 실패! \n"); 38 for (i=0; i<INDEX_SIZE; i++) 39 if ((indexTable[i].key <= key) && (indexTable[i+1].key > key)){ 40 begin = indexTable[i].index; 41 end = indexTable[i+1].index; 42 break; 43 }
2. 순차 검색 : [예제 11-3] 44 if (i == INDEX_SIZE) { 45 begin = indexTable[i-1].index; 46 end = n; 47 } 48 49 sequentialSearch2(a, begin, end, key); 50 } 51 52 void main() 53 { 54 int a[]={1, 2, 8, 9, 11, 19, 29}; 55 int n=7; 56 printf("\n\t<< 색인 순차 검색 >>\n"); 57 makeIndexTable(a, n); 58 indexSearch(a, n, 9); 59 indexSearch(a, n, 6); 60 61 getchar(); 60 }
2. 순차 검색 • 색인 순차 검색의 성능 • 인덱스 테이블의 크기에 따라 결정 • 인덱스 테이블의 크기가 줄어들면 배열의 인덱스를 저장하는 간격이 커지므로 배열에서 검색해야하는 범위도 커진다. • 인덱스 테이블의 크기를 늘리면 배열의 인덱스를 저장하는 간격이 작아지므로 배열에서 검색해야하는 범위는 작아지겠지만 인덱스 테이블을 검색하는 시간이 늘어나게 된다. • 색인 순차 검색의 시간 복잡도 : O(m + n/m) • 배열의 크기 : n, 인덱스 테이블의 크기 : m
3. 이진 검색 • 이진 검색(binary search, 이분 검색, 보간 검색(interpolation search)) • 자료의 가운데에 있는 항목을 키 값과 비교하여 다음 검색 위치를 결정하여 검색을 계속하는 방법 • 찾는 키 값 >원소의 키 값 : 오른쪽 부분에 대해서 검색 실행 • 찾는 키 값 <원소의 키 값 : 왼쪽 부분에 대해서 검색 실행 • 키를 찾을 때까지 이진 검색을 순환적으로 반복 수행함으로써 검색 범위를 반으로 줄여가면서 더 빠르게 검색 • 정복 기법을 이용한 검색 방법 • 검색 범위를 반으로 분할하는 작업과 검색 작업을 반복 수행 • 정렬되어있는 자료에 대해서 수행하는 검색 방법
3. 이진 검색 • 이진 검색의 예
3. 이진 검색 • 이진 검색 알고리즘 • 삽입이나 삭제가 발생했을 경우에 항상 배열의 상태를 정렬 상태로 유지하는 추가적인 작업 필요 • 시간 복잡도 : O(log2n)
4. 이진 트리 검색 • 이진 트리 검색(binary tree search) • 8장에서 설명한 이진 탐색 트리를 사용한 검색 방법 • 원소의 삽입이나 삭제 연산에 대해서 항상 이진 탐색 트리를 재구성하는 작업 필요
4. 이진 트리 검색 : [예제 11-4] 영어사전 검색 프로그램 • 이진 트리 검색을 이용한 영어사전 검색 프로그램 • 영어 단어와 뜻을 입력하면 알파벳순서대로 이진 탐색 트리에 삽입 • 검색할 단어를 입력하면 이진 트리 검색으로 검색하여 단어의 뜻을 출력 • 사전 출력을 선택하면 트리를 중위 순회하면서 트리에 있는 모든 단어를 출력
4. 이진 트리 검색 : [예제 11-4] 영어사전 검색 프로그램 • 실행 결과 > (1)
4. 이진 트리 검색 : [예제 11-4] 영어사전 검색 프로그램 • 실행 결과 > (2)
4. 이진 트리 검색 : [예제 11-4] 영어사전 검색 프로그램 • 실행 결과 > (3)
4. 이진 트리 검색 : [예제 11-4] 영어사전 검색 프로그램 • 실행 결과 > (4)
%균형 트리(Balanced Tree) • 이진 트리 알고리즘 • 최악의 경우 성능이 나쁨 : 정렬된 화일 또는 역순으로 정렬된 화일, 큰 키와 작은 키가 교대로 나오는 파일 • 성능 개선 • 퀵 정렬의 경우 성능 개선 방법은 확률에 의해 임의의 분할 원소를 선택하는 수 밖에 없음 • 이진 트리 탐색의 경우에는 트리를 균형 있게 유지하면 최악의 상황을 피할 수 있음 • 일반적인 개념은 쉽게 기술할 수 있지만, 실제 구현에서는 특수한 상황을 고려해야 함 알고리즘 분석
% 2-3-4 트리(2-3-4 Tree) • 이진 탐색 트리(1개의 키, 2개의 링크)에서 발생하는 최악의 상황을 방지하기 위해 다음과 같이 자료구조를 변경 • 트리에 있는 하나의 노드가 하나 이상의 키를 가질 수 있음 • 3-노드(2개의 키, 3개의 링크)와 4-노드(3개의 키, 4개의 링크)를 허용 • 트리의 균형에 대해 신경 쓰지 않아도 완벽한 균형 트리가 만들어 짐 알고리즘 분석
% 분할 과정(1) • 루트가 4-노드인 경우 • 세 개의 2-노드로 변환 • 루트의 분할은 트리의 높이를 하나 증가시킴 • 부모가 2-노드인 4-노드의 경우 • 중간 키를 부모로 보내여 3-노드에 연결된 두 개의 2-노드로 변환 • 부모가 3-노드인 4-노드의 경우 • 4-노드에 연결된 두 개의 2-노드로 변환 알고리즘 분석
% 분할 과정 (2) • 4-노드가 2-3-4 트리의 루트인 경우 • 노드가 루트인 경우의 분할(Ti는 서브트리) 알고리즘 분석
7 7 5 T5 T5 5 6 4 4 6 T1 T2 T3 T4 T1 T2 T3 T4 % 분할 과정(3) • 4-노드가 2-노드의 자식인 경우의 분할 • 4-노드가 2-노드의 왼쪽 자식인 경우 • 4-노드가 2-노드의 왼쪽 중간 자식인 경우 알고리즘 분석
% 분할 과정(4) • 4-노드가 3-노드의 자식인 경우 • 4-노드가 3-노드의 왼쪽 자식인 경우 • 4-노드가 3-노드의 왼쪽 중간 자식인 경우 알고리즘 분석
% 분할 과정(5) • 4-노드가 3-노드의 오른쪽 자식인 경우 알고리즘 분석
% 2-3-4 트리의 구축 과정 (1) 키 삽입 ( 2, 1, 8, 9, 7, 3, 6, 4, 5 ) 알고리즘 분석
% 2-3-4 트리의 구축 과정 (2) 키 삽입 ( 2, 1, 8, 9, 7, 3, 6, 4, 5 ) 알고리즘 분석
% 성능 특성 • N-노드로 이루어진 2-3-4 트리의 탐색은 logN + 1개 이상 노드를 방문하지 않음 • N-노드로 이루어진 2-3-4 트리의 삽입은 최악의 경우 logN + 1개의 노드를 분할하며, 평균적으로는 하나 이하의 노드를 분할함 • 2-3-4 트리에서 4-노드의 개수가 적음 • 일반적으로 높이가 h인 2-3-4 트리는 2h+1-1과 4h+1-1 사이의 키를 포함 • N개의 키를 포함하는 2-3-4 트리의 높이는 ⌈log4(N+1)⌉-1과 ⌈log2(N+1)⌉-1 사이 알고리즘 분석
or % 레드-블랙 트리(red-black tree) • 2-3-4 트리를 표준 이진 트리로 표현하는 방법 • 노드당 추가로 1 비트가 더 필요함 • 블랙 링크는 보통의 노드이고, 3-노드와 4-노드는 레드 링크로 연결된 이진 트리로 표현 알고리즘 분석
% 성질 • 루트나 외부 노드는 모두 블랙 • 루트에서 외부 노드까지의 경로 상에는 2개의 연속된 레드 노드가 포함되지 않음 • 루트에서 각 외부 노드까지의 경로에 있는 블랙 노드의 수는 모두 같음 • 동일한 키를 가지는 레코드는 노드의 왼쪽과 오른쪽에 모두 올 수 있음 • 동일한 키가 여러 개 있을 때 주어진 키를 갖는 모든 노드를 찾는 것이 어려움 알고리즘 분석
% 4-노드의 분할 알고리즘 분석
단일 회전 이중 회전 % 4-노드의 분할(회전 필요) 알고리즘 분석
g p p g T4 x x T3 T1 T2 T3 T4 T1 T2 g p p x T1 g x T2 T1 T2 T3 T4 T3 T4 % 단일 회전 알고리즘 분석
g g x x p T4 T4 g p p x T3 T1 T1 T2 T3 T4 T1 T2 T2 T3 g g x x p T1 T1 g p p x T4 T2 T1 T2 T3 T4 T2 T3 T3 T4 % 이중 회전 알고리즘 분석
% 레드 블랙 트리의 구축과정 (1) • 키 ( 2, 1, 8, 9, 7, 3, 6, 4, 5 ) 2 삽입1 삽입8 삽입 9 삽입7 삽입 알고리즘 분석
% 레드 블랙 트리의 구축과정 (2) • 키 ( 2, 1, 8, 9, 7, 3, 6, 4, 5 ) 3 삽입 6 삽입 이중 회전4 삽입 알고리즘 분석
% 레드 블랙 트리의 구축과정 (3) • 키 ( 2, 1, 8, 9, 7, 3, 6, 4, 5 ) 이중 회전5 삽입 단일 회전 알고리즘 분석
% 회전(rotate) struct node *rotate(int v, struct node *y) { struct node *gc, *c; c = (y->key > v) ? y->l : y->r; if (c->key > v) { gc = c->l; c->l = gc->r; gc->r = c; } else { gc = c->r; c->r = gc->l; gc->l = c; } if (y->key > v) y->l = gc; else y->r = gc; return gc; } 알고리즘 분석