(파이썬) 이진 탐색 알고리즘

0Kim_jae·2023년 3월 13일
0

알고리즘

목록 보기
4/10

이진 탐색 알고리즘

  • 순차 탐색: 리스트 안에 있는 특정한 데이터를 착기 위해 앞에서부터 데이터를 하니씩 확인하는 방법

  • 이진 탐색: 정렬된 리스트에서 탐색 범위를 절반씩 좁혀가며 데이터를 탐색. (시작점, 끝점, 중간점 설정 필요)

이진 탐색의 시간 복잡도

단계마다 탐색 범위를 2로 나누는 것과 동일하므로 연산 횟수는 Log2(N)에 비례한다.
다시말해 이진탐색의 시간 복잡도는 O(logN)을 보장한다.

https://www.youtube.com/watch?v=94RC-DsGMLo

# 재귀 함수를 활용한 이진 탐색 소스코드 구현

def binary_search(array, target, start, end):
	if start > end:
    	return None
    mid = ( start + end ) // 2
    # 찬은 경우 중간점 인덱스 반환
    if array[mid] == target:
    	retrun mid
    # 중간점의 값보다 찾고자 하는 값이 작은 경우 왼쪽 환인
    elif array[mid] > target:
    	return binary_search(array, target, start, mid - 1)
    # 중간점의 값보다 찾고자 하는 값이 큰 경우 오른쪽 확인
  	else:
    	return binary_search(array, target, min + 1, end)
        

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

# 이진 탐색 수행 결과 출력
result = binary_search(array, target, 0, n-1)
if result == None:
	print("원소가 존재하지 않습니다.")
else:
	print(result + 1)
파이썬 이진 탐색 라이브러리
  • bisect_left(a, x): 정렬된 순서를 유지하면서 배열 a에 x를 삽입할 가장 왼쪽 인덱스를 반환

  • bisect_right(a, x): 정렬된 순서를 하지하면서 배열 a에 x를 삽입할 가장 오른쪽 인덱스 반환

from bisect import bisect_left, bisect_right

a = [1,2,3,4]
x = 4

print(bisect_left(a,x)) # 실행결과 2
print(bisect_left(a,x)) # 실행결과 4

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


from bisect import bisect_left, bisect_right

# 값이 [left_value, right_value]인 데이터의 개수를 반환하는 함수
def count_by_range(a, left_value, right_value):
	right_index = bisect_right(a, right_value)
	left_index = bisect_left(a, left_value)
	return right index - left index
    
# 배열 선언
a= [1, 2, 3, 3,3,3,4,4,8,91

# 값이 4인 데이터 개수 출력
print(count_by_range(a, 4, 4))

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

파라메트릭 서치란 최적화문제를 결정 문제('예' 또는 '아니오')로 바꾸어 해결하는 기법이다.
ex) 특정한 조건을 만족하는 가장 알맞은 값을 빠르게 찾는 최적화 문제
일반적으로 코딩 테스트에서 파라메트릭 서치 문제는 이진 탐색을 이용하여 해결할 수 있다.

이진탐색 예제

문제 해결 아이디어

  • 적절한 높이를 찾을 때까지 이진 탐색을 수행하여 높이 H를 반복해서 조정.

  • '현재 지정한 높이로 자르면 조건을 만족할 수 있는가?'를 확인한 뒤에 조건의 만족 여부('예' 또는 '아니오')에 따라서 탐색 범위를 좁혀서 해결할 수 있다.

  • 절단기 높이는 0부터 10억까지의 정수중 하나이다. (큰 탐색 범위를 보면 이진 탐색을 떠올려야 한다.)


# 떡의 개수(N)와 요청한 떡의 길이(M)을 입력
n, m = list(map(int, input().split(' ')))
# 각 떡의 개별 높이 정보를 입력
array = list(map(int, input().split()))


# 이진 탐색을 위한 시작점과 끝점 설정
start = 0
end = max(array)

# 이진탐색 수행
result = 0
while (start <= end):
	total = 0
    mid = (start + end) // 2
    for x in array:
    	# 잘랐을 때의 떡의 양 계산
        if x > mid:
        	toral += x - mid
    # 떡의 양이 부족한 경우 더 많이 자르기 (왼쪽 부분 탐색)
    if total < m:
    	end = mid - 1
    # 떡의 양이 충분한 경우 덜 자르기 (오른쪽 부분 탐색)
    else:
    	result = mid
        start = mid + 1

문재 해결 아이디어

  • 일반적 선형탐색으로는 시간 초과 판정을 받는다.

  • 하지만 데이터가 정렬되어 있기 때문에 이진 탐색을 수행할 수 있다.

  • bisect를 활용하여 특정값이 등장하는 첫 번째 위치와 마지막 위치를 찾아 위치 차이를 계산해 문제를 해결 할 수 있다.


from bisect import bisect_left, bisect_right

# 값이 [left_value, right_value]인 데이터의 개수를 반환하는 함수
def count_by_range(array, left_value, right_value):
	right_index = bisect_right(array, right_value)
	left_index = bisect_left(array, left_value)
	return right_index - left_index
    
n, x = map(int, input().spl1t()) # 데이터의 개수 N, 찾고자 하는 값 x 입력받기 
array = list(map(int, input().split())) # 전체 데이터 입력받기 

# 값이 [x, x] 범위에 있는 데이터의 개수 계산
count = count_by_range(array, x, x)

# 값이 x인 원소가 존재하지 않는다면
if count == 0:
	print(-1)
# 값이 x인 원소가 존재한다면
else:
	print (count)

0개의 댓글