카테고리 없음
[Udemy] JavaScript 알고리즘 & 자료구조 마스터클래스_Section 15
호랑구야
2023. 7. 3. 09:00
* 수업은 JS 기반이지만 Python으로 구현
Section 15
더 빠른 정렬들
- 시간복잡도를 O(N^2)에서 O(N*logN)으로 향상시킨다.
- 효율성과 간단함은 상충관계로, 알고리즘이 효율적일수록 이해하는데 시간이 걸린다.
합병 정렬: 소개
분할, 정렬, 합병 세 가지 조합으로 이루어져 있다. 0개 요소 혹은 1개 요소 배열이 이미 정렬되어 있는 상태라는 것을 활용한다. 배열을 더 작은 배열로 나누어 0개 혹은 1개가 되면, 옆의 요소와 비교하여 정렬 후 합병을 한다. 이것을 모든 요소가 수행하여 배열을 완성한다.
배열 정렬: 소개
- 두 정렬된 배열을 합병하는 헬퍼 함수를 먼저 구현하는 것이 유용하다.
- 정렬된 두 배열이 주어지면 헬퍼 함수가 정렬된 새 배열을 만든다.
- 헬퍼 함수는 O(n+m)의 시간과 O(n+m)의 공간으로 실행된다.
- n은 첫 번째 배열의 크기
- m은 두 번째 배열의 크기
배열 정렬: 구현
- 빈 배열을 만든다.
- 각 배열마다 가장 작은 값을 확인한다.
- 카운터 2개를 이용한 while 루프를 만든다.
- 두 카운터는 0부터 시작한다.
- 두 카운터가 각각 배열 끝에 도달하지 않았다면, 첫 번째 배열의 첫 번째 항목과 두 번째 배열의 첫 번째 항목을 비교한다.
- 만약 첫 번째 배열의 첫 번째 항목이 더 작았다면, 첫 번째 배열의 두 번째 항목과 두 번째 배열의 첫 번째 항목을 비교한다.
- 이런 방식을 반복하다가 한 배열이 완료된다면, 나머지 배열의 남은 값은 전부 결과 배열에 넣는다.
# Merge Sort Helper
def Merge(arr1, arr2):
# 결과 배열 선언
result = []
# 카운터 i: arr1용 j: arr2용
i = 0
j = 0
# i가 배열1의 길이보다 작고, j가 배열2의 길이보다 작은 동안 루프 진행
while((i < len(arr1)) and (j < len(arr2))):
# 함수 진행 확인 용도
print(i, j)
# 각 배열의 원소를 비교하여 더 작은 것이 결과 배열로 들어감
# 두 값이 같을 때에는 일단 arr2의 요소를 넣고
# 나머지를 arr2의 그 다음값과 비교하여
# 더 작은 값인 arr1의 요소가 result안으로 들어가
# 결과에 문제가 없으므로 그대로 진행
if arr1[i] < arr2[j]:
result.append(arr1[i])
i += 1
else:
result.append(arr2[j])
j += 1
# 함수 진행 확인 용도
print(result)
# 만약 루프가 끝난 후, 배열1 혹은 배열2에 남은 요소가 있다면 결과 배열에 넣기
while(i < len(arr1)):
result.append(arr1[i])
i += 1
while(j < len(arr2)):
result.append(arr2[j])
j+= 1
# 결과 배열 return
return result
Merge([1, 3, 6], [2, 4, 5, 7, 8, 9])
Merge([1, 3, 4, 6], [2, 4, 5, 7, 8, 9])
합병 정렬: 구현
- 배열을 반으로 나누고, 하위 배열이 0 혹은 1개의 요소만 남을 때까지 반복한다.
- 작은 하위 배열이 준비되면, 합병 함수를 이용하여 배열을 다시 합치고 원래의 길이가 될 때까지 반복한다.
- 배열을 다시 합친 이후 가장 마지막에 합병된 배열을 반환한다.
# Merge Sort
def MergeSort(arr):
# 만약 배열의 수가 1보다 작거나 같다면 배열을 반환하라
# base case, 함수의 재귀가 멈추는 시점
if len(arr) <= 1:
return arr
# 배열의 중간 지점 변수
mid = int(len(arr) / 2)
# 매번 다른 데이터를 이용해 함수를 호출하는 부분
left = MergeSort(arr[:mid])
right = MergeSort(arr[mid:])
# 함수 진행 확인 용도
print('L: ', left, 'R: ', right)
return Merge(left, right)
# Merge Sort
def MergeSort(arr):
# 만약 배열의 수가 1보다 작거나 같다면 배열을 반환하라
# base case, 함수의 재귀가 멈추는 시점
if len(arr) <= 1:
return arr
# Merge Sort Helper
def Merge(arr1, arr2):
# 결과 배열 선언
result = []
# 카운터 i: arr1용 j: arr2용
i = 0
j = 0
# i가 배열1의 길이보다 작고, j가 배열2의 길이보다 작은 동안 루프 진행
while((i < len(arr1)) and (j < len(arr2))):
# 함수 진행 확인 용도
print('i: ', i, 'j: ', j)
# 각 배열의 원소를 비교하여 더 작은 것이 결과 배열로 들어감
# 두 값이 같을 때에는 일단 arr2의 요소를 넣고
# 나머지를 arr2의 그 다음값과 비교하여
# 더 작은 값인 arr1의 요소가 result안으로 들어가
# 결과에 문제가 없으므로 그대로 진행
if arr1[i] < arr2[j]:
result.append(arr1[i])
i += 1
else:
result.append(arr2[j])
j += 1
# 함수 진행 확인 용도
print('Rst: ', result)
# 만약 루프가 끝난 후, 배열1 혹은 배열2에 남은 요소가 있다면 결과 배열에 넣기
while(i < len(arr1)):
result.append(arr1[i])
i += 1
while(j < len(arr2)):
result.append(arr2[j])
j+= 1
# 결과 배열 return
return result
# 배열의 중간 지점 변수
mid = int(len(arr) / 2)
# 매번 다른 데이터를 이용해 함수를 호출하는 부분
left = MergeSort(arr[:mid])
right = MergeSort(arr[mid:])
# 함수 진행 확인 용도
print('M: ', mid, 'L: ', left, 'R: ', right)
return Merge(left, right)
MergeSort([3, 2, 5, 13, 8, 23, 0, 9])
합병 정렬: 빅오 복잡도
배열 n개를 반으로 나누는 것을 반복하여 1개가 될 때까지 수행한다면 반으로 나누는 횟수는 log n번이 된다. 8개의 배열은 반으로 나누는 행위를 3번 수행한다. 그리고 나눈 것끼리 매번 n번의 비교가 있기 때문에 n log n이 된다. 데이터에 구애받지 않는 정렬 알고리즘에서 최선은 O(n log n)을 활용할 수 있다.
ex) 8개 배열 나누기
8
4 4
2 2 2 2
1 1 1 1 1 1 1 1
- 시간복잡도
- Best: O(n log n)
- Average: O(n log n)
- Worst: O(n log n)
- 공간복잡도
- O(n)
반응형