CS/Data Structure

[자료구조] 검색

창문닦이 2019. 12. 5. 00:20

[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);
		}
	}
}

연습문제 1,2 풀이 실행결과

 

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, 4, 5 풀이 실행결과


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);
		}
	
	}
}

연습문제 6 풀이 실행결과

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();
		}
	}
}

연습문제 7 풀이 실행결과

문제 출처는 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