개발하는 삶
[알고리즘] 이진탐색 알고리즘 본문
순차 탐색
- 리스트 안에 특정 데이터를 찾기위해 앞에서부터 데이터를 하나씩 확인하는 방법
이진탐색
- 정렬되어 있는 리스트에서 탐색범위를 절반씩 좁혀가며 데이터를 탐색하는 방법
- 시작점, 끝점, 중간점을 이용해 탐색범위 설정
- 재귀함수로 소스코드 구현
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 |