일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 완전탐색
- python __init__
- 영어
- 이미지 생성
- #영어 명언
- python list
- convexhull
- #일상영어
- #영어
- #실생활영어
- word embedding
- Convolution Neural Network
- keras
- #opencv
- #English
- #1일1영어
- findContours
- c언어
- 딥러닝
- tokenizing
- #프로젝트
- text2img
- TensorFlow
- 영어명언
- object detection
- #Android
- #실생활 영어
- tensorflow update
- opencv SURF
- python 알고리즘
- Today
- Total
When will you grow up?
Sort Algorithm 본문
정렬 알고리즘
정렬(Sorting)이란 데이터를 특정한 기준에 따라 순서대로 나열하는 것을 말합니다.
일반적으로 문제 상황에 따라서 적절한 정렬 알고리즘이 공식처럼 사용된다.
아래 리스트를 어떻게 정렬할까 ?
[7, 5, 9, 0, 3, 1, 6, 2, 4, 8]
1. 선택 정렬(selection sort)
- 처리되지 않은 데이터 중 가장 작은 데이터를 선택해 맨 앞에 있는 데이터와 바꾸는 것을 반복
: [7, 5, 9, 0, 3, 1, 6, 2, 4, 8] 가장 작은 0을 선택해 7와 바꾼다.
: [0, 5, 9, 7, 3, 1, 6, 2, 4, 8] 다음 작은 1를 선택해 5와 바꾼다.
: [0, 1, 9, 7, 3, 5, 6, 2, 4, 8] 다음 작은 2을 선택해 9와 바꾼다.
: [0, 1, 2, 7, 3, 5, 6, 9, 4, 8] 위 작업을 계속 반복한다.
: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 마지막 경우에는 처리하지 않아도 된다.
탐색할때마다 범위가 줄어드는데 매번 작은 값을 찾기위해 탐색 범위만큼 확인해서 찾아야되기 때문에
매번 선형탐색을 수행하는 것과 동일
N + (N-1) + (N-2) + ... + 2 이므로 (N^2+N-2)/2 로 표현 가능하지만 빅오 표기법으로 O(N^2) 의 시간 복잡도를 가진다.
# 선택 정렬
arr = [7, 5, 9, 0, 3, 1, 6, 2, 4, 8]
for i in range(len(arr)):
min_index = i # 가장 작은 원소 인덱스
for j in range(i+1, len(arr)): # 탐색해야 할 인덱스
if arr[min_index] > arr[j]:
min_index = j
arr[i], arr[min_index] = arr[min_index], arr[i] # 작은 인덱스와 바꾼다
print(arr)
2. 삽입 정렬(insertion sort)
- 처리되지 않은 데이터를 하나씩 골라 적절한 위치에 삽입
- 선택 정렬에 비해 구현 난이도가 높지만, 일반적으로 더 효율적으로 동작
: [7, 5, 9, 0, 3, 1, 6, 2, 4, 8] 맨 앞 7은 정렬되어 있다고 가정하고 그 다음 원소인 5가 어떤 위치로 들어갈지 판단하여 7이랑 비교해서 앞으로 갈지 뒤로 갈지 판단
: [5, 7, 9, 0, 3, 1, 6, 2, 4, 8] 그 다음 앞 두개는 정렬되어 있다고 가정하고 그다음 원소인 9가 어떤 위치로 들어갈지 판단해 정렬
: [5, 7, 9, 0, 3, 1, 6, 2, 4, 8] 그 다음 앞 세개는 정렬되어 있다고 가정하고 그다음 원소인 0가 어떤 위치로 들어갈지 판단해 정렬(정렬된 원소랑만 비교하므로 가장 앞으로 간다)
: [0, 5, 7, 9, 3, 1, 6, 2, 4, 8] 그 다음 앞 네개는 정렬되어 있다고 가정하고 그다음 원소인 3가 어떤 위치로 들어갈지 판단해 정렬
: [0, 3, 5, 7, 9, 1, 6, 2, 4, 8] 그 다음 앞 네개는 정렬되어 있다고 가정하고 그다음 원소인 3가 어떤 위치로 들어갈지 판단해 정렬
: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 위 작업을 계속 반복해 모든 원소가 정렬될때까지 반복
삽입 정렬의 시간 복잡도는 O(N^2)이며, 현재 리스트의 데이터가 거의 정렬되어 있는 상태라면 매우 빠르게 동작하는 장점이 있다. 최선의 경우는 O(N)
# 삽입 정렬
arr = [7, 5, 9, 0, 3, 1, 6, 2, 4, 8]
for i in range(1, len(arr)):
for j in range(i, 0, -1): # 인덱스 i부터 1까지 1씩 감소하며 반복하는 문법
if arr[j] < arr[j-1]: # 한 칸씩 왼쪽으로 이동
arr[j], arr[j-1] = arr[j-1], arr[j]
else: # 자기보다 작은 데이터 만나면 그 위치에서 멈춤
break
print(arr)
3. 퀵 정렬(quick sort)
- 기준 데이터를 설정하고 그 기준보다 큰 데이터와 작은 데이터의 위치를 바꾸는 방법
- 병합 정렬과 더불어 대부분의 프로그래밍 언어의 정렬 라이브러리의 근간이 되는 알고리즘
- 가장 기본적인 퀵 정렬은 첫 번째 데이터를 기준 데이터(pivot)로 설정
: [5, 7, 9, 0, 3, 1, 6, 2, 4, 8] 현재 pivot 값은 5이라고 가정하고, 왼쪽부터 5보다 큰 데이터를 선택하므로 7이 선택되고 오른쪽부터 5보다 작은 데이터를 선택하므로 4가 선택되고 위치를 바꿈
: [5, 4, 9, 0, 3, 1, 6, 2, 7, 8] 마찬가지로 pivot이 5이므로 왼쪽에서 부터 검색하고해서 큰값을 검색하고 오른쪽에서 부터 큰 값을 찾아 위치를 바꾼다.
: [5, 4, 2, 0, 3, 1, 6, 9, 7, 8] 현재 pivot 값이 5이므로 왼쪽부터 큰 값인 6이 선택되고 오른쪽에서는 작은값이 1이 선택되는데, 이처럼 위치가 엇갈리는 경우 pivot과 작은 데이터의 위치를 서로 변경
: [1, 4, 2, 0, 3, 5, 6, 9, 7, 8] 이제 5의 왼쪽에 있는 데이터는 모두 5보다 작고, 왼쪽에 있는 데이터는 5보다 크다는 특징이 생기는데, 데이터 묶음을 나누는 작업을 분할(divide)라고 한다.
: [1, 4, 2, 0, 3, 5, 6, 9, 7, 8] 5를 기준으로 나눠진 부분을 정렬을 마찬가지로 계속 수행해준다.
: [1, 0, 2, 4, 3, 5, 6, 9, 7, 8] 계속 재귀적으로 정렬해주면 정렬이 완료 될것이다.
이상적인 경우 분할이 절반씩 일어난다면 전체연산 횟수로 O(NlogN)을 기대 할 수 있다. 최악은 O(N^2)
너비 x 높이 = N * logN = NlogN
삽입 정렬의 시간 복잡도는 O(N^2)이며, 현재 리스트의 데이터가 거의 정렬되어 있는 상태라면 매우 빠르게 동작하는 장점이 있다. 최선의 경우는 O(N)
arr = [5, 7, 9, 0, 3, 1, 6, 2, 4, 8]
def quick_sort(arr, start, end):
if start >= end: # 원소가 1개인 경우 종료
return
pivot = start # pivot은 첫 번재 원소
left = start + 1
right = end
while(left <= right):
# pivot보다 큰 데이터를 찾을 때까지 반복
while(left <= end and arr[left] <= arr[pivot]):
left += 1
# pivot보다 작은 데이터를 찾을 때까지 반복
while(right > start and arr[right] >= arr[pivot]):
right -= 1
if (left > right): # 왼쪽에서 작은 데이터랑 오른쪽에서 큰 데이터가 엇갈렸다면 pivot 교체
arr[right], arr[pivot] = arr[pivot], arr[right]
else: # 엇갈리지 않았다면 작은 데이터와 큰 데이터 교체
arr[left], arr[right] = arr[right], arr[left]
# 분할 이후 왼쪽 부분과 오른쪽 부분에서 각각 정렬 수행
quick_sort(arr, start, right-1)
quick_sort(arr, right + 1, end)
quick_sort(arr, 0, len(arr)-1)
print(arr)
# 파이썬 장점 살린 버전
arr = [5, 7, 9, 0, 3, 1, 6, 2, 4, 8]
def quick_sort_1(arr):
# list 하나 이하의 원소만을 담고 있다면 종료
if len(arr) <= 1:
return arr
pivot = arr[0] # pivot은 첫 번재 원소
tail = arr[1:] # 피벗을 제외한 리스트
left_side = [x for x in tail if x <= pivot] # 분할된 왼쪽 부분
right_side = [x for x in tail if x > pivot] # 분할된 오른쪽 부분
# 분할 이후 왼쪽 부분과 오른쪽 부분에서 각각 정렬 수행 후 전체 리스트 반환
return quick_sort_1(left_side) + [pivot] + quick_sort_1(right_side)
print(quick_sort_1(arr))
4. 계수 정렬(counting sort)
- 특정한 조건이 부합할 때만 사용할 수 있지만 매우 빠르게 동작하는 정렬 알고리즘
- 계수 정렬은 데이터의 크기 범위가 제한되어 정수 형태로 표현할 수 있을 때 사용 가능
- 데이터의 개수가 N, 데이터(양수) 중 최대값이 K 일 때 최악의 경우에도 수행 시간 O(N+K)를 보장
: [7, 5, 9, 0, 3, 1, 6, 2, 9, 1, 4, 8, 5, 2] 0 ~ 9 까지 테이블 표로 만들어놓고 각각 count table를 계산한다
: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [2, 2, 2, 1, 1 ,2, 1, 1, 1, 2] 인덱스/ 개수 이렇게 만들 수 있다. index 접근하는건 O(N) 이라고 가정할때
: [0, 0, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9] # 결과를 확인할 때는 리스트의 첫 번재 데이터부터 하나씩 그 값을 반복하여 인덱스 출력
계수 정렬의 시간 복잡도와 공간 복잡도는 모두 O(N+K)이다
때에 따라서 비효율성을 초래할 수 있다. ex) 0, 999999 2개만 존재하는 경우 데이터 범위가 너무 크다..
동일한 값을 가지는 데이터가 여러 개 등장할 때 효율적으로 사용
# 모든 원소의 값이 0보다 크거나 같다고 가정
arr = [7, 5, 9, 0, 3, 1, 6, 2, 9, 1, 4, 8, 5, 2]
# 모든 범위를 포함하는 리스트 선언
count = [0] * (max(arr) + 1)
for i in range(len(arr)):
count[arr[i]] += 1 # 각 데이터에 해당하는 인덱스의 값 증가
for i in range(len(count)): # 리스트에 기록된 정렬 정보 확인
for j in range(count[i]):
print(i, end = '')
위 내용들은 이코테 youtube 및 책을 참고하였습니다
이코테 링크
책 링크
'02. Study > Algorithm' 카테고리의 다른 글
dynamic programming (0) | 2020.11.03 |
---|---|
binary search (0) | 2020.10.11 |
DFS / BFS (0) | 2020.10.09 |
Greedy Algorithm & Implementation (0) | 2020.10.07 |
python_그래프 기초 (0) | 2019.09.02 |