개발하는 삶

[알고리즘] 이진탐색 알고리즘 본문

CS/알고리즘

[알고리즘] 이진탐색 알고리즘

삶_ 2022. 7. 2. 14:13

순차 탐색

  • 리스트 안에 특정 데이터를 찾기위해 앞에서부터 데이터를 하나씩 확인하는 방법

이진탐색

  • 정렬되어 있는 리스트에서 탐색범위를 절반씩 좁혀가며 데이터를 탐색하는 방법
  • 시작점, 끝점, 중간점을 이용해 탐색범위 설정
  • 재귀함수로 소스코드 구현
import sys

def binary_search(array, target, start, end):
    if start > end: #만약 값을 못찾을경우 None 반환
        return None
    mid = (start+end)//2 #중간값
    if array[mid] == target: #값을 찾았을때
        return mid
    # 중간값이 찾는 값보다 큰 경우. 왼쪽으로 중간값을 옮김
    elif array[mid] > target:
        return binary_search(array, target, start, mid-1)
    # 중간값이 찾는 값보다 작으면, 오른쪽으로 중간값을 옮김
    else:
        return binary_search(array, target, mid+1, end)

n, target = list(map(int, sys.stdin.readline()))
array = list(map(int, sys.stdin.readline()))

result = binary_search(array, target, 0, n-1)
if result == None:
    print("원소가 존재하지 않습니다.")
else:
    print(result+1) #인덱스 위치 +1
#위에꺼를 반복문으로 풀어보기

import sys

def binary_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

n, target = list(map(int, sys.stdin.readline()))
array = list(map(int, sys.stdin.readline()))

result = binary_search(array, target, 0, n-1)
if result == None:
    print("원소가 존재하지 않습니다.")
else:
    print(result+1) #인덱스 위치 +1

  • 첫째 줄에 떡의 개수 n개와 떡의길이 m이 주어짐. (m은 0부터 10억)
    둘째 줄엔 떡의 개별 높이가 주어짐.
  • 떡 높이의 총합은 항상 m이상이고,
    손님은 필요한 양만큼 떡을 사갈 수 있다.
    최소 m만큼 떡을 집에 가져가기 위해 절단기에 설정할수있는 높이의 최댓값을 출력
  • 문제 풀이
    • 적절한 높이를 찾을때까지 조건의 만족여부 (예/아니오) 로 탐색범위를 좁힌다
    • m이 0부터 10억까지중 하나이니, 큰 탐색범위일 때는 이진탐색을 써야함
    • 중간값은 시간이 지날수록 최적화된 값이 됨. 중간값을 결과로 도출하면 됨.
# 떡의 개수, 요청한 떡의 최소 길이 입력
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: # 자른값보다 떡의 양이 많을때만 자른다. 자른 값들을 다 더한다
        	total += x - mid
    if total < m: # 떡의 총량이 요청한 떡의 최소길이보다 작다면 (다른방법이 필요! => 중간값을 줄인다)
    	end = mid - 1
    else: # 딱 잘 맞게 나왔다면 (자른길이를 결과값에 저장)(mid가 클수록 좋기때문에 한번더 늘려 시도해본다)
    	result = mid
        start = mid +1
        
# 정답 출력
print(result)



파이썬 이진 탐색 라이브러리

  • bisect_left(a,x) : 정렬된 순서를 유지하면서 배열 a에 x를 삽입할 가장 왼쪽 인덱스를 반환
  • bisect_right(a,x) : 정렬된 순서를 유지하면서 배열 a에 x를 삽입할 가장 오른쪽 인덱스를 반환
  • ex. [1,2,4,4,8] 이런 파일이 있을때
    x가 4면 4에 근접한 위치에 놓는것이 좋음
    왼쪽이라면 4 왼쪽인 a[2]
    오른쪽이라면 4 오른쪽인 a[4]
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,9]

print(count_by_range(a, 4, 4))
print(count_by_range(a, -1, 3))

  • n개의 원소를 포함한 수열이 오름차순으로 정렬됨.
    이 수열에서 x가 등장하는 횟수를 계산함.
  • 첫째줄에 n과 x가 공백으로 구분되어 입력됨
    둘째줄에 n개의 원소가 정수형태로 공백
  • 수열의 원소 중에서 값이 x인 원소의 갯수를 출력.
    값이 x인 원소가 하나도 없다면 -1을 출력.
  • 마지막위치 - 첫번째 위치 = 결과값
#정석적으로 푼다면 def함수를 이용해 푸는걸 추천

import sys
from bisect import bisect_left, bisect_right

n,x  = map(int, sys.stdin.readline().split())
l = list(map(int, sys.stdin.readline().split()))

if x not in l:
    print(-1)
else:
    print(bisect_right(l, x) - bisect_left(l, x))



파라메트릭 서치

  • 최적화 문제를 결정문제(예/아니오)로 바꾸어 해결하는 기법
  • 특정한 조건을 만족하는 가장 알맞는 값을 빠르게 찾는 최적화 문제

'CS > 알고리즘' 카테고리의 다른 글

[알고리즘] 최단경로 알고리즘  (0) 2022.07.11
[알고리즘] 다이나믹 프로그래밍  (0) 2022.07.05
[알고리즘] 정렬 알고리즘  (0) 2022.06.30
[알고리즘] DFS/BFS  (0) 2022.06.26
[알고리즘] 구현 알고리즘  (0) 2022.06.25