[알고리즘] 탐색 - 리스트
탐색 개요
- 여러 개의 원소로 구성된 데이터에서 원하는 값을 가진 원소를 찾는 것
- 데이터의 형태 - 리스트, 트리, 그래프 등
- 내부 탐색 vs 외부 탐색
-
관련 연산 -> 탐색 + (초기화, 삽입, 삭제)
- 탐색 방법
- 리스트 형태 : 순차 탐색, 이진 탐색
- 트리 형태 : 이진 탐색 트리, 2-3-4 트리, 레드-블랙 트리, B-트리
- 해시 테이블 : 해시 함수, 충돌 해결 방법
- 리스트 형태 : 순차 탐색, 이진 탐색
순차탐색
- 리스트 형태로 주어진 원소들을 처음부터 하나씩 차례로 (“순차”) 비교하면서 원하는 값을 가진 원소를 찾는 방법
탐색 연산
SequentialSearch(A[], n, x){
i=0;
while(i<n && A[i]!=x) i = i+1;
return(i)
}
삽입 연산
SequentialSearch_Insert(A[], n, x){
A[n] = x;
return(A, n+1);
}
삭제 연산
SequentialSearch_Delete(A[], n, x){
Index = SequentialSearch(A, n, x);
if(Index == -1) return(A, n);
A[Index] = A[n-1];
return(A, n-1);
}
성능 및 특징
- 탐색 성공 시
- 1번~n번 비교
- 평균 (n+1)/2회
- 1번~n번 비교
- 탐색 실패 시
- 항상 n번 비교
- O(n)
- 항상 n번 비교
- 삭제
- 삭제할 원소의 순차 탐색 O(n) 후, 마지막 원소의 이동
- O(1)
- 삭제할 원소의 순차 탐색 O(n) 후, 마지막 원소의 이동
- 삽입 연산의 시간 복잡도
- 리스트의 마지막에 추가하는데 상수 시간만 필요
- O(1)
- 리스트의 마지막에 추가하는데 상수 시간만 필요
- 정렬되지 않고 크기가 작은 데이터에 적합
- 모든 리스트 형태의 입력에 적용 가능
- 비정렬 데이터 탐색에 적합
- 비정렬 데이터 탐색에 적합
- 탐색과 삭제에 O(n) 시간이 필요
- 데이터가 큰 경우에는 부적합
- 데이터가 큰 경우에는 부적합
이진 탐색
이진탐색 개요
- 정렬된 리스트 형태로 주어진 원소들을 절반씩 줄여 가면서 원하는 값을 가진 원소를 찾는 방법
- 분할정복 방법이 적용됨
-
위에서 작성한 ‘리스트’는 오직 배열만 가능
- 탐색 방법
- 배열의 가운데 원소 A[min]를 탐색 키 key를 비교
- A[mid] = key : 탐색 성공
- key < A[mid] : 이진탐색(원래 크기의 1/2인 왼쪽 부분배열) 순환 호출
- A[mid] < key : 이진탐색(원래 크기의 1/2인 오른쪽 부분배열) 순환 호출
- 배열의 가운데 원소 A[min]를 탐색 키 key를 비교
- 탐색을 반복 할 때마다 대상 원소의 개수가 절반으로 감소
탐색 연산
BinarySearch(A[], key, Left, Right){
if(Left>Right) return(-1);
mid = (Left+Right)/2;
if(A[mid] == key) return(mid);
else if(key<A[mid]) BinarySearch(A, key, Left, mid-1)
else BinarySearch(A, key, mid+1, Right)
}
- T(n) = O(logn)
초기화 연산 (정렬이 되어있지 않을 때)
BinarySearch_Initialize(A[], n){ // O(n)
for(i=0; i<n-1; i++){
if(A[i]>A[i+1]){
A=Sort(A,n); // 오름차순으로 정렬되는 메서드라고 가정. O(nlogn)
break;
}
}
return(A)
}
- T(n) = O(nlogn)
삽입 연산
BinarySearch_Insert(A[], n, x){
left = 0; right = n-1;
while(left <= right) {
mid = ((right-left+1)/2)+left;
if(x==A[mid]) return(A,n); // 삽입할 원소가 이미 존재
else if(x<A[mid]) right = mid-1; // 왼쪽 부분배열 탐색
else left = mid+1; // 오른쪽 부분배열 탐색
}
for(i=n;i>left;i--) A[i]=A[i-1]; //A[left]부터 오른쪽으로 한 칸씩 이동
A[Left] = x; // 원소 삽입
return (A,n+1);
}
- T(n) = O(n)
삭제 연산
BinarySearch_Delete(A[], n, x){
index = BinarySearch(A[], x, 0, n-1);
if(index==-1) return (A,n); // 삭제할 원소가 존재하지 않음
for(i==index; i<n-1; i++) { // 삭제할 위치의 오른쪽 모든 원소를
A[i] = A[i+1]; // 왼쪽으로 한칸씩 이동(원소 삭제)
}
return (A,n-1);
}
- T(n) = O(n)
성능과 특징
- 연결리스트 에서 삽입과 삭제가 용이하다고 생각할 수 있지만 구조상 이진 탐색 자체가 불가능
- 직접적으로 중앙값을 찾는 단계 자체가 안됨
- 직접적으로 중앙값을 찾는 단계 자체가 안됨
- 정렬된 리스트에 대해서만 적용 가능
- 삽입과 삭제가 빈번한 경우에는 부적합
- 연산 후 리스트의 정렬 상태를 유지하기 위해 O(n)의 데이터 이동이 필요
- 데이터가 작은 경우에 적합
- 연산 후 리스트의 정렬 상태를 유지하기 위해 O(n)의 데이터 이동이 필요
- 탐색 연산 : O(logn)
- 초기화 연산 : O(nlogn)
- 삽입 연산 : O(n)
- 삭제 연산 : O(n)