[3장. 검색]
1. 검색 알고리즘
- 검색과 키
- 검색 : 데이터 집합에서 원하는 값을 가진 요소를 찾아내는 것이다.
- 키 : 주목하는 항목. 키는 데이터의 일부에 해당한다.
- 조건은 하나만 지정하기도 하지만 논리곱이나 논리합을 사용하여 복합해서 지정하기도 한다.
- 배열에서 검색하기
- 체인법 : 같은 해시 값의 데이터를 선형 리스트로 연결하는 방법이다.
- 오픈주소법 : 데이터를 위한 해시값이 충돌할 때 재해시하는 방법이다.
- 선형 검색 : 무작위로 늘어놓은 데이터 모임에서 검색을 수행한다.
- 이진 검색 : 일정한 규칙으로 늘어놓은 데이터 모임에서 아주 빠른 검색을 수행한다.
- 해시법 : 추가, 삭제가 자주 일어나는 데이터 모임에서 아주 빠른 검색을 수행한다.
- 데이터의 검색, 추가, 삭제에 소요되는 비용을 종합적으로 계산해서 알고리즘을 선택해야 한다.
2. 선형 검색
- 선형 검색(순차 검색)배열에서 원하는 키 값을 갖는 요소를 만날 때까지 맨 앞부터 순서대로 요소를 검색한다.
- 무한루프의 구현 : while, for문은 첫번째 행만 봐도 무한루프인지 구분이 된다. do ~ while의 경우 끝까지 읽지 않으면 무한루프인지 알 수 없으므로 권장하지 않는다.
- 보초법
- 선형 검색은판단한다. 이 비용을 줄이기 위해 보초법이 등장했다.
- 검색하기 전에검색하고자 하는 키 값을 맨 끝요소에 저장하는데, 이 저장값을 보초(sentinel)라고 한다.
package com.doit.ds.chapter03;
import java.util.Scanner;
//선형검색(보초법)
public class SeqSearchSen {
static int seqSearchSenWhile(int[] a, int n, int key) {
int i = 0;
a[n] = key;//보초를 추가
while (true) {
if (a[i] == key)
break;
i++;
}
return i == n ? -1 : i; //검색결과 인덱스가 길이와 일치할 경우 키 존재.
}
/**
* 연습문제 Q1 While문이 아닌 For문으로 메소드를 수정하기
*/
static int seqSearchSenFor(int[] a, int n, int key) {
int i;
a[n] = key;//보초를 추가
for(i=0; i<a.length; i++) {
if (a[i] == key)
break;
}
return i == n ? -1 : i; //검색결과 인덱스가 길이와 일치할 경우 키 존재.
}
/**
* 연습문제 Q2 선형검색의 스캐닝 과정을 상세히 출력하는 메소드
*/
static int seqSearchSenPrint(int[] a, int n, int key) {
int i = 0;
a[n] = key;//보초를 추가
System.out.print(" |");
for(int j=0; j<n; j++) {
System.out.printf("%2d",j);//인덱스 출력
}
System.out.print("\n--+------------------------\n");
for(i=0; i<n; i++) {
System.out.print(" | ");
for(int k=0; k<i; k++) {
System.out.print(" ");//공백 출력
}
System.out.printf("*\n%2d|",i);
for(int j=0; j<n; j++) {
System.out.printf("%2d",a[j]);//값 출력
}
System.out.println();
if (a[i] == key)
break;
}
return i == n ? -1 : i; //검색결과 인덱스가 길이와 일치할 경우 키 존재.
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("요솟수 : ");
int num = sc.nextInt();
int x[] = new int[num+1]; //전체요솟수+1
for (int i=0; i<num; i++) {
System.out.printf("x[%d]:", i);
x[i] = sc.nextInt();
}
System.out.print("검색 키 : ");
int key = sc.nextInt();
//int idx = seqSearchSenWhile(x, num, key);
//int idx = seqSearchSenFor(x, num, key);
int idx = seqSearchSenPrint(x, num, key);
if (idx == -1) {
System.out.println("검색 결과 일치하는 요소가 없습니다.");
} else {
System.out.printf("%d는 x[%d]에 있습니다.\n", key, idx);
}
}
}
package com.doit.ds.chapter03;
public class Test {
/**
* 연습문제 Q3 key와 일치한 요솟수의 인덱스를 반환하는 메소드
*/
static int searchIdx(int[] a, int n, int key, int[] idx) {
int seq = 0;
for (int i=0; i<n; i++) {
if (key == a[i]) {
idx[seq++] = i;
}
}
return seq;
}
/**
* 연습문제 Q4 이진검색의 과정을 자세히 출력하는 메소드
*/
static int binSearchPrint(int[] arr, int n, int key) {
int pl = 0; // 검색범위 첫번째 인덱스
int pr = n-1; // 검색범위 마지막 인덱스
System.out.print(" |");
for(int j=0; j<n; j++) {
System.out.printf("%2d",j);//인덱스 출력
}
System.out.print("\n--+------------------------\n");
do {
int pc = (pl+pr)/2; // 중앙 인덱스
System.out.print(" | <-");
for(int k=0; k<pc-1; k++) {
System.out.print(" ");//공백 출력
}
System.out.print("+");
for(int k=0; k<pc-1; k++) {
System.out.print(" ");//공백 출력
}
System.out.print("->\n");
System.out.printf("%2d|",pc);//현재 인덱스 출력
for(int j=0; j<n; j++) {
System.out.printf("%2d",arr[j]);//값 출력
}
System.out.println();
if (arr[pc] == key) {
return pc; // 검색 성공
} else {
pr = pc -1;//검색범위를 첫번째인덱스~중앙값으로 좁힘
}
} while (pl <= pr);
return -1;//검색실패
}
/**
* 연습문제 Q5 이진검색중 같은값을 같는 요소가 있을 경우 가장 앞에 요소를 반환.
*/
static int binSearchX(int[] arr, int n, int key) {
int pl = 0; // 검색범위 첫번째 인덱스
int pr = n-1; // 검색범위 마지막 인덱스
do {
int pc = (pl+pr)/2; // 중앙 인덱스
if (arr[pc] == key) {
for(int i=pc; i>pl; i--) {
if (arr[i] == key) {
pc = i;
}
}
return pc; // 검색 성공
} else {
pr = pc -1;//검색범위를 첫번째인덱스~중앙값으로 좁힘
}
} while (pl <= pr);
return -1;//검색실패
}
public static void main(String[] args) {
// 연습문제3
int[] arr = {1,9,2,9,4,6,7,9};
int key = 9;
int num = 8;
int[] idx = new int[num];
int result = searchIdx(arr, num, key, idx);
StringBuffer sb = new StringBuffer("key와 일치한 요소의 인덱스 :{");
for (int i=0; i<result; i++) {
sb.append(idx[i]);
if(i == result-1) {
sb.append("}\n");
} else {
sb.append(", ");
}
}
System.out.println(sb.toString());
// 연습문제4
int[] arr2 = {1,2,3,5,6,8,9};
int key2 = 2;
int num2 = 7;
int result2 = binSearchPrint(arr2, num2, key2);
if (result2 == -1) {
System.out.println("검색 결과 일치하는 요소가 없습니다.\n");
} else {
System.out.printf("%d는 x[%d]에 있습니다.\n\n", key2, result2);
}
// 연습문제5
int[] arr3 = {1,3,5,7,7,7,7,8,8,9,9};
int key3 = 7;
int num3 = 11;
int result3 = binSearchX(arr3, num3, key3);
if (result3 == -1) {
System.out.println("검색 결과 일치하는 요소가 없습니다.\n");
} else {
System.out.printf("첫번째 %d는 x[%d]에 있습니다.\n\n", key3, result3);
}
}
}
3. 이진 검색
- 이진 검색
- 이진 검색알고리즘의 전제조건은 이미 데이터가 키 값으로 정렬되어 있다는 것이다.
- 이진 검색요소가 오름차순 또는 내림차순으로 정렬된 배열에서 검색.
- 검색을 반복할 때마다 검색범위가 절반이 되므로 검색에 필요한 비교 횟수의 평균값은 log n이다.
- 복잡도 : 알고리즘의 성능을 객관적으로 평가하는 기준에 해당한다.
- 시간 복잡도 : 실행에 필요한 시간을 평가한 것이다.
- 공간 복잡도 : 기억 영역과 파일 공간이 얼마나 필요한가를 평가한 것이다.
- 시공간 복잡도에 관련된 이전 포스팅 >> 참조
- 2개 이상의 복잡도로 구성된 알고리즘의 전체 복잡도는 차원이 더 높은 쪽 복잡도를 우선시 한다.
- Arrays.binarySearch에 의한 이진검색
- 자바는 배열에서 이진 검색 메소드를 표준 라이브러리로 제공한다.
- java.util.Arrays클래스의 binarySearch 메소드
package com.doit.ds.chapter03;
import java.util.Arrays;
import java.util.Scanner;
//Arrays.binarySearch 메소드로 검색하기
public class BinarySearchTester {
/**
* 연습문제 Q6 : 검색에 실패하면 삽입 포인트의 값을 출력하는 프로그램
*/
static int binSearch(int[] arr, int n, int key) {
int pl = 0; // 검색범위 첫번째 인덱스
int pr = n-1; // 검색범위 마지막 인덱스
int pc;
do {
pc = (pl+pr)/2; // 중앙 인덱스
if (arr[pc] == key) {
return pc; // 검색 성공
} else if(arr[pc]<key) {
pl = pc+1;//검색범위를 중앙~뒤로 좁힘
} else {
pr = pc-1;//검색범위를 앞~중앙로 좁힘
}
} while (pl <= pr);
if(key > arr[pc])
++pc;
return ++pc * -1;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("요솟수 :");
int num = sc.nextInt();
int x[] = new int[num];
System.out.println("오름차순으로 입력하세요.");
System.out.printf("x[0]:");
x[0] = sc.nextInt();
for (int i=1; i<num; i++) {
do {
System.out.printf("x[%d]:", i);
x[i] = sc.nextInt();
} while (x[i] < x[i-1]); //직전요소보다 커야만 함
}
System.out.print("검색 키 : ");
int key = sc.nextInt();
//int idx = Arrays.binarySearch(x, key);
int idx = binSearch(x, num, key);
if (idx < 0) {
System.out.printf("검색에 실패했습니다. %d의 삽입포인트는 x[%d]에 있습니다.\n", key, idx);
} else {
System.out.printf("%d는 x[%d]에 있습니다.\n", key, idx);
}
}
}
public class PhysExamSearch {
static class PhyscData{
private String name; //이름
private int heigth; //키
private double vision; //시력
//생성자
public PhyscData(String name, int height, double vision) {
this.name = name;
this.heigth = height;
this.vision = vision;
}
public String toString() {
return name + " " + heigth + " " + vision;
}
public static final Comparator<PhyscData> HEIGHT_ORDER = new HeightOrderComparator();
public static final Comparator<PhyscData> VISION_ORDER = new VisionOrderComparator();
private static class HeightOrderComparator implements Comparator<PhyscData> {
@Override
public int compare(PhyscData o1, PhyscData o2) {
return (o1.heigth > o2.heigth) ? 1 : (o1.heigth < o2.heigth) ? -1 : 0;
}
}
private static class VisionOrderComparator implements Comparator<PhyscData> {
@Override
public int compare(PhyscData o1, PhyscData o2) {
return (o1.vision > o2.vision) ? -1 : (o1.vision < o2.vision) ? 1 : 0;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
PhyscData[] x = {
new PhyscData("가가가", 162, 1.9d),
new PhyscData("나나나", 163, 1.8d),
new PhyscData("다다다", 175, 1.5d),
new PhyscData("라라라", 179, 1.5d),
new PhyscData("마마마", 188, 1.0d),
new PhyscData("바바바", 194, 0.9d),
new PhyscData("사사사", 199, 0.8d)
};
int idx = 0;
while(true) {
System.out.print("검색조건? 키(1)/시력(2):");
int search = sc.nextInt();
switch (search) {
case 1:
System.out.print("키가 몇cm인 사람을 찾고 있나요? :");
int height = sc.nextInt();
idx = Arrays.binarySearch(
x, //배열 x에서 탐색
new PhyscData("", height, 0.0), //키가 height인 요소
PhyscData.HEIGHT_ORDER //PhyscData.HEIGHT_ORDER에 의해 검색
);
break;
case 2:
System.out.print("특정 시력을 가진 사람을 찾고 있나요? :");
double vision = sc.nextDouble();
idx = Arrays.binarySearch(
x, //배열 x에서 탐색
new PhyscData("", 0, vision),//시력이 vision인 요소
PhyscData.VISION_ORDER //PhyscData.HEIGHT_ORDER에 의해 검색
);
break;
}
if (idx < 0) {
System.out.println("검색 결과 일치하는 요소가 없습니다.");
} else {
System.out.println("x["+idx+"]에 있습니다.\n");
System.out.println("찾은데이터 : "+x[idx].toString());
}
System.out.print("계속하나요? [Y:1/N:0] >>>>>>>>>>>>>");
if (sc.nextInt()>0) {
continue;
} else {
break;
}
}
sc.close();
}
}
}
문제 출처는 Do it! 자료구조와 함께 배우는 알고리즘 입문 자바 편입니다.
연습문제 풀이는 직접 작성하였습니다. 잘못된 부분이 있다면 댓글 부탁드립니다.
'CS > Data Structure' 카테고리의 다른 글
[자료구조] 재귀 알고리즘 (0) | 2020.02.10 |
---|---|
[자료구조] 스택과 큐 (0) | 2020.02.02 |
[자료구조] 기본 자료구조(배열, 클래스) (0) | 2019.11.26 |
[자료구조] 알고리즘이란 (0) | 2019.11.09 |
[자료구조] Heap (0) | 2019.02.12 |