세리프 따라잡기

WEEK02 - TIL 이분 탐색(이진 탐색) / 이진 탐색 트리 본문

SW사관학교 정글

WEEK02 - TIL 이분 탐색(이진 탐색) / 이진 탐색 트리

맑은 고딕 2022. 4. 7. 21:59

순차 탐색: 리스트 안에 있는 특정한 데이터 찾기 위해 앞에서부터 데이터를 하나씩 확인하는 방법 [별다른 말이 없다면 기본적으로 이 방법으로 탐색]

이진 탐색: 정렬되어 있는 리스트에서 (만족한다면) 탐색 범위를 절반씩 좁혀가며 데이터를 탐색하는 방법

-> 탐색 범위를 정해줘야함. 시작점, 끝점, 중간점.

 

ex.

step1

중간점 선정은 중간점index값에서 소수점 이하는 제거다.

우리가 찾는 값이 4일 때, 중간점 보다 왼쪽에 있으므로, 중간점~끝점까지는 볼 필요가 X

따라서 끝점을 중간점보다 한 칸 왼쪽으로 다시 재지정해준다.

 

step2

끝점의 위치 조정으로 인한 중간점 재지정에 들어감.

이번에도 찾고자 한 값이 중간점보다 오른쪽에 있으므로 시작점~중간점은 볼 필요X

따라서 시작점을 중간점보다 한 칸 오른쪽으로 재지정해준다.

 

step3

이제 중간점이 우리가 찾고자 한 4값과 일치하므로 탐색을 멈춘다!

 

- 이진 탐색의 시간 복잡도는 탐색 범위를 2로 나눈 것과 동일, 즉 연산 횟수는 log₂N에 비례

(초기 개수: 32 > 1단계: 16 > 2단계: 8 > 3단계: 4 …)       ∴ 시간복잡도 O(logN)을 보장

 

 파이썬 소스코드 [재귀적 구현]

import sys

input = sys.stdin.readline

def search(array,target,start,end):
    # start>end일 시 탐색하고자 한 범위에 데이터가 X니까 none값 반환
    if start>end:
        return None

    # 중간점 지정
    mid = (start+end)//2

    # 찾은 경우 중간점 index 반환   
    if array[mid] == target:
        return mid
    # 중간점의 값보다 찾고자 하는 값이 작은 경우 왼쪽 확인
    elif array[mid]>target:
        return search(array,target,start,mid-1)
    # 중간점의 값보다 찾고자 하는 값이 큰 경우 오른쪽 확인
    else:
        return search(array,target,mid+1,end)

# 원소의 개수와 찾고자 하는 값 입력 받기
n, target = list(map(int, input().split()))
# 전체 원소 입력 받기
array = list(map(int, input().split()))

# 이진 탐색 수행 결과 출력
result = search(array,target,0,n-1)
if result == None:
    print('원소X')
# 있다면 몇 번째 원소인지 출력
else:
    print(result+1)

[반복문 구현]

import sys

input = sys.stdin.readline

def search(array,target,start,end):
    while start<=end:
        mid = (start+end)//2

        if array[mid] == target:
            return mid
        elif array[mid]>target:
            end=mid-1
        else:
            start=mid+1
    return None

# 원소의 개수와 찾고자 하는 값 입력 받기
n, target = list(map(int, input().split()))
# 전체 원소 입력 받기
array = list(map(int, input().split()))

# 이진 탐색 수행 결과 출력
result = search(array,target,0,n-1)
if result == None:
    print('원소X')
# 있다면 몇 번째 원소인지 출력
else:
    print(result+1)

 

* 이진 탐색 라이브러리 [파이썬 편]

from bisect import bisect_left,bisect_right

bisect_left(a,x) # 정렬된 순서 유지하면서 배열 a에 x를 삽입할 가장 왼쪽 index 반환
bisect_right(a,x) # 정렬된 순서 유지하면서 배열 a에 x를 삽입할 가장 오른쪽 index 반환

+ 값이 특정 범위에 속하는 데이터 개수 구하기

import sys
from bisect import bisect_left,bisect_right

input = sys.stdin.readline

# 값이 [left, right]인 데이터의 개수를 반환하는 함수
def countRange(a,left,right):
    rightIndex = bisect_right(a,right)
    leftIndex = bisect_left(a,left)
    return rightIndex - leftIndex

a = [1,2,3,3,3,3,4,4,8,9]

#값이 4인 데이터 개수 출력
print(countRange(a,4,4)) # 2
#값이 [-1,3] 범위에 있는 데이터 개수 출력
print(countRange(a,-1,3)) # 6

- 파라메트릭 서치(Parametric Search)

= 최적화 문제를 결정 문제(예 or 아니오)로 바꾸어 해결하는 기법 [ex. 어떤 함수의 값을 가능한 낮추거나, 높이는 문제]

 


- 기본 트리 구조

트리는 부모-자식 구조

최상단: 루트 노드

최하단: 단말 노드

트리를 떼어내도: 서브 트리

 

+ basic. 트리 크기가 N이라고 했을 때, 트리의 간선(줄기)의 개수는 N-1

 

- 이진 탐색 트리

= 가장 간단한 형태 : 이진 탐색이 동작할 수 있게 고안한 자료구조

 

* 이진 탐색 트리의 특징

1. 부모(루트) 노드보다 왼쪽 자식 노드가 작고

2. 부몬 노드보다 오른쪽 자식 노드가 크다

∴ 즉, 왼쪽 < 부모 < 오른쪽 노드 순으로 크다! 이게 성립되면 == '이진 트리'

ex. 37을 찾는 과정

→ 루트부터 방문하고 → 30<37이네? 왼쪽 노드 볼 가치가 없다~ 하면서 부모랑 함께 안 봄! → 오른쪽 노드로 gogo

→ 이때 48이 루트가 되어 왼쪽과 오른쪽 노드를 비교함. → 왼쪽 찾고(37) 탐색 종료~🕊

 

if. 자식 노드가 없을 때까지 찾지 못하면 == 원소가 없는 것

 

 

+++ 이진 탐색 보강

- 구현 준비

  • target : 찾고자 하는 값
  • data : 오름차순으로 정렬된 list
  • start : data 의 처음 값 인덱스
  • end : data 의 마지막 값 인덱스
  • mid : start, end 의 중간 인덱스

- 구현 개요

  • 자료의 중간 값이 (mid) 찾고자 하는 값인지 검사
  • 아니라면 대소관계를 비교하여 start, end 값 이동
  • 동일 연산 반복 (재귀로 구현 가능)

'SW사관학교 정글' 카테고리의 다른 글

WEEK02 - TIL 우선순위 큐(Priority Queue)  (0) 2022.04.11
WEEK02 - TIL 스택/큐  (0) 2022.04.10
WEEK02- TIL 분할정복  (0) 2022.04.09
WEEK01 - 특별한 과제  (7) 2022.04.02
입소까지 D-1  (2) 2022.03.28
Comments