
💡 카카오페이 코테를 준비하기 위해서 알고리즘 정리를 해보고자 한다.
=> Tip:
1. 하나의 함수에 여러 개의 재귀함수를 선언하는 것은 좋지 않다.
2. 재귀는 메모리 초과나 시간초과를 쉽게 발생시킬 수 있으니, 시도해보고 초과가 발생하면 복잡해도 반복문으로 구현해야한다.

=> Tip:
1. 모든 경우의 수를 계산해야 할 때 고려할 필요 있음
2. 입력 크기가 작은 경우 많이 사용
3. 중복없이 선택하는 경우
4. 사전순 (작은수 -> 큰수) 같은 경우 주로 사용
=> 풀이 Top:
1. 조건을 통해 특정 조건에 만족하면 return하는 식으로 재귀를 빠져 나간다
2. 재귀 호출 전 상태 변경 + 재귀 호출 후 상태 복원

from bisect import bisect_left, bisect_right
array = [1, 2, 3, 4, 4, 6, 8, 9]
x = 4
print(bisect_left(array, x)) # 3
print(bisect_right(array, x)) # 5
# 특정 값 존재 여부
def count_by_array(a, val):
right_index = bisect_right(a, val)
left_index = bisect_left(a, val)
return right_index - left_index
# 특정 범위 원소 찾기
def count_by_range(a, left, right):
right_index = bisect_right(a, right)
left_index = bisect_left(a, left)
return right_index - left_index
# 특정 갑의 존재 여부
print(count_by_array(array,3)) # 0보다 크면 존재, 0이면 존재하지 않음
# 특정 범주 내의 존재하는 총 갯수
print(count_by_range(array, -1, 3))
# 특정 값의 갯수
print(count_by_range(array, 4, 4))
# 특정 갯수가 포함되어 있는지
def binary_search(target):
st = 0
en = N-1
while(st <= en):
mid = (st+en)//2
if target == card[mid]:
return True
elif target < card[mid]:
en = mid - 1
else:
st = mid + 1
## 특정 원소의 갯수 구하기
# 하위 값
def lower_bound(target):
st = 0
en = N
while(st < en):
mid = (st+en)//2
if target <= card[mid]:
en = mid
else:
st = mid + 1
return st
# 상위 값
def upper_bound(target):
st = 0
en = N
while(st < en):
mid = (st+en)//2
if target < card[mid]:
en = mid
else:
st = mid + 1
return st
조건을 만족하는 최소/최댓값을 구하는 문제(최적화 문제) -> 결정문제로 변환해 이분탐색 수행
ex) N개를 만들 수 있는 랜선의 최대 길이 (최적화) / 랜선의 길이가 X일때 랜선이 N개 이상인지 여부 (결정)
조건은 위와 같은 함수 형태로 나타냈을 때, 증가함수 or 감소함수로 일정해야한다. (뒤죽박죽이면 불가)
접근 Tip:
=> 최소 or 최대값에 대한 얘기 있을 경우에 범위가 무지막지하게 큰경우
=> 시간 복잡도에게 N하나를 logN으로 떨어트려야 할 경우 고민해봐야 한다. ex) O(N^2) -> O(NlogN)

코딩테스트에서 소수를 구할 경우 많이 사용되는 방식이다.
기존에는 이중 for문을 이용한 O(N^2)의 시간복잡도를 가지고 있지만, 이 경우는 O(N√n )을 가진다.
# 0과 1은 먼저 False 처리
prime = [False,False] + [True] * (N-1)
for i in range(2, int(n**0.5)+1):
if prime[i]:
# i의 배수에 해당하는 수는 전부 False 처리 ( 2,3 등을 제외하기 위해 *2 )
for j in range(2*i, N+1, i):
prime[j] = False
위 방식을 통해 범주를 줄이고, 크기를 키워가면서, 해당하는 수의 배수를 전부 제거해주면 소수인 것 들만 True를 가지고 있다.
파이썬에서는 heapq라는 우선순위 큐 라이브러리를 제공한다. (heapq는 Min Heap)
heap은 최소 큐라고도 불리고, 가장 작은 값을 반환하기에 수월하다.(반대로 -를 붙여서 가장 큰 값을 반환하기도 수월하다.)

우선순위 큐 (Priority Queue)는 힙(Heap)의 키(Key)를 우선순위로 사용하므로, 우선순위 큐의 구현체가 된다.
Heapq Method
heappush ( 해당 node의 index와 우선순위 같이 넣어줌) - O(logN)
아이템 삽입heappop - O(lognN)
우선 순위가 가장 높은 or 낮은 노드 삭제heap[0] - O(1)
우선 순위가 가장 높은 or 낮은 노드 조회
+) 배열을 통해 이진 트리를 구성하고, 부모노드, 자식노드를 찾는법

BFS와 DFS는 기존 방식과 동일하게, Deque와 Stack or 재귀를 사용해서 푼다
Tip은 2차원 배열을 통해 1 - 2가 연결되어 있으면 graph[1][2] = graph[2][1] = 1을 통해 저정하거나, graph[1].append(2)를 통해서 각 인덱스와 연결된 노드를 저장해서 풀도록 하자 ( 해당 노드의 부모 노드를 저장할 경우에는 따로 부모배열을 선언하자)
이제, DFS와 BFS를 제외한, 순회에 대해서 알아보도록 하자
레벨순회 = 높이순으로 방문하는 방식 (정점 노드부터 시작)
레벨 순회는 1-2-3-4-5-6-7-8 순으로 순회하며 BFS를 사용하면 자식노드를 넣어주면 해결가능

전위순회 = 정점 - 왼쪽 - 오른쪽 순으로 순회한다. (재귀를 사용)
전위 순회는 1-2-4-5-6-3-6-7 순으로 순회하며, DFS를 통해서 해결 가능하다.

중위순회 = 왼쪽 - 정점 -오른쪽으로 순회한다. (가장 왼쪽부터 순회)
중위순회는 4-2-5-8-1-6-3-7 순으로 순회한다.

후위순회 = 왼쪽 - 오른쪽 -정점으로 순회한다. (정점보다 오른쪽을 먼저 거침)
후위순회는 4-8-5-2-6-7-3-1 순으로 순회한다.
