Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 미네르바 대학
- TOPCIT
- 토익
- 데이터관련자격증
- ai model collapse
- 토익문제
- pip
- 코드프렌즈
- toeic
- cv2
- arXiv
- 크롬 확장프로그램
- 데이터분석
- model collapse
- scaico
- ADsP
- 토익문법
- 프론트
- ai공모전
- minerva university
- 토익공부
- 환급형프론트챌린지
- 탑싯시험
- students@ai seoul hackathon
- 논문 pdf
- 탑싯
- 토익공부법
- ai consensus
- pdf 다운로드
- 논문 pdf 이름
Archives
- Today
- Total
토리의 데굴데굴 공부일기
[알고리즘] ch06. 정렬 본문
<이것이 코딩테스트다 with 파이썬> 책 내용을 바탕으로 작성된 글입니다.
정렬 알고리즘: 연속된 데이터를 기준에 따라서 정렬하기 위한 알고리즘
기준에 따라 데이터를 정렬
정렬 알고리즘 개요
- 정렬: 데이터를 특정한 기준에 따라서 순서대로 나열하는 것
- ex) 오름차순, 내림차순
선택 정렬
: 데이터가 무작위로 여러 개 있을 때, 이 중에서 가장 작은 데이터를 선택해 맨 앞에 있는 데이터와 바꾸고, 그다음 작은 데이터를 선택해 앞에서 두번째와 바꾸는 과정 반복
→ ‘매번 가장 작은 것을 선택’
데이터의 개수가 N개일 경우 가장 작은 데이터를 앞으로 보내는 과정을 N-1번 반복하면 정렬이 완료됨
#6-1.py 선택 정렬 소스코드
array = [7,5,9,0,3,1,6,2,4,8]
for i in range(len(array)):
min_index = i #가장 작은 원소의 인덱스
for j in range(i+1, len(array)):
if array[min_index] > array[j]:
min_index = array[j]
array[i], array[min_index] = array[min_index], array[i] #스와프
print(array)
**스와프: 특정한 리스트가 주어졌을 때 두 변수의 위치를 변경하는 작업
- 시간복잡도
- 연산 횟수는 $N + (N-1) + (N-2) + … + 2$
- 근사치로 N x (N+1) / 2번의 연산을 수행한다고 가정 ⇒ $N^2 + N / 2$
- 빅오 표기법 : $O(N^2)$
- 직관적으로 이해하자면, 소스코드 상으로 간단한 형태의 2중 반복문이 사용되었기 때문
- 알고리즘의 수행 시간 비교
→ 선택 정렬은 다른 알고리즘과 비교했을 때 매우 비효율적이지만, 특정한 리스트에서 가장 작은 데이터를 찾는 일이 코딩 테스트에서는 잦으므로 선택 정렬 소스코드 형태에 익숙해질 필요가 있음
삽입 정렬
- 데이터를 하나씩 확인하며, 각 데이터를 적절한 위치에 삽입
#6-3.py 삽입 정렬 소스코드
array = [7,5,9,0,3,1,6,2,4,8]
for i in range(1, len(array)):
for j in range(i, 0, -1): #인덱스 i부터 1까지 감소하며 반복하는 문법
if array[j] < array[j-1]: #한 칸씩 왼쪽으로 이동
array[j], array[j-1] = array[j-1], array[j]
else: #자기보다 작은 데이터를 만나면 그 위치에서 멈춤
break
print(array)
- 시간복잡도
- 빅오 표기법 : $O(N^2)$
- 반복문이 2번 중첩되어 사용됨
- 삽입 정렬은 현재 리스트의 데이터가 거의 정렬되어 있는 상태라면 매우 빠르게 동작함. 최선의 경우 $O(N)$의 시간 복잡도를 가짐
- 거의 정렬되어 있는 상황에서는 퀵 정렬보다 더 강력함!
퀵 정렬
- 지금까지 배운 정렬 알고리즘 중 가장 많이 사용되는 알고리즘
- 기준 데이터를 설정하고 그 기준보다 큰 데이터와 작은 데이터의 위치를 바꾸자!
- 피벗(Pivot) : 큰 숫자와 작은 숫자를 교환할 때, 그 교환의 ‘기준’
호어 분할(Hoare Partition) : 가장 대표적인 분할 방식
- 리스트에서 첫 번째 데이터를 피벗으로 정한다
- 왼쪽에서부터 피벗보다 큰 데이터를 찾고, 오른쪽에서부터 피벗보다 작은 데이터를 찾는다
- 큰 데이터와 작은 데이터의 위치를 서로 교환한다
- 단, 왼쪽에서부터 찾는 값과 오른쪽에서부터 찾는 값의 위치가 서로 엇갈린 경우에는 ‘작은 데이터’와 ‘피벗’의 위치를 서로 변경한다.
- 이를 반복
#6-4.py 퀵 정렬 소스코드
array = [5,7,9,0,3,1,6,2,4,8]
def quick_sort(array, start, end):
if start >= end: #원소가 1개인 경우 종료
return
pivot = start #피벗은 첫번째 원소
left = start + 1
right = end
while left <= right:
#피벗보다 큰 데이터를 찾을 때까지 반복
while left <= end and array[left] <= array[pivot]:
left += 1
#피벗보다 작은 데이터를 찾을 때까지 반복
while right > start and array[right] >= array[pivot]:
right -= 1
if left > right: #엇갈렸다면 작은 데이터와 피벗을 교체
array[right], array[pivot] = array[pivot], array[right]
else: #엇갈리지 않았다면 작은 데이터와 큰 데이터를 교체
array[left], array[right] = array[right], array[left]
#분할 이후 왼쪽 부분과 오른쪽 부분에서 각각 정렬 수행
quick_sort(array, start, right-1)
quick_sort(array, right+1, end)
quick_sort(array, 0, len(array)-1)
print(array)
#6-5.py 파이썬 장점을 살린 퀵 정렬 소스코드
array = [5,7,9,0,3,1,6,2,4,8]
def quick_sort(array):
#리스트가 하나 이하의 원소만을 담고 있다면 종료
if len(array) <= 1:
return array
pivot = array[0] #피벗은 첫 번째 원소
tail = array[1:] #피벗을 제외한 리스트
left_side = [x for x in tail if x <= pivot] #분할된 안쪽 부분
right_side = [x for x in tail if x >= pivot] #분할된 오른쪽 부분
#분할 이후 왼쪽 부분과 오른쪽 부분에서 각각 정렬을 수행하고, 전체 리스트를 반환
return quick_sort(left_side) + [pivot] + quick_sort(right_side)
print(quick_sort(array))
- 시간복잡도
- 평균적으로 $O(NlogN)$
- 최악의 경우 $O(N^2)$ - 이미 데이터가 정렬되어 있는 경우
계수 정렬
- 특정한 조건이 부합할 때만 사용할 수 있지만 매우 빠른 알고리즘
- 데이터의 크기 범위가 제한되어 정수 형태로 표현할 수 있을 때만 사용 가능
- ex) 0 이상 100 이하인 성적 데이터를 정렬할 때 계수 정렬이 효과적임.
- 모든 범위를 담을 수 있는 크기의 리스트(배열)를 선언해야 하기 때문에 가장 큰 데이터와 가장 작은 데이터의 차이가 너무 크면 계수 정렬을 사용할 수 없음
#6-6.py 계수 정렬 소스코드
#모든 원소의 값이 0보다 크거나 같다고 가정
array = [7,5,9,0,3,1,6,2,9,1,4,8,0,5,2]
#모든 범위를 포함하는 리스트 선언(모든 값은 0으로 초기화)
count = [0] * (max(array) + 1)
for i in range(len(array)):
count[array[i]] += 1 #각 데이터에 해당하는 인덱스의 값 증가
for i in range(len(count)):
for j in range(count[i]):
print(i, end=' ') #띄어쓰기를 구분으로 등장한 횟수만큼 인덱스 출력
- 시간 복잡도
- 모든 데이터가 양의 정수인 상황에서 데이터의 개수를 N, 데이터 중 최대값의 크기를 K라고 할때, 시간복잡도는 $O(N+K)$
- 데이터의 범위만 한정되어 있다면 효과적으로 사용할 수 있으며 항상 빠르게 동작함
- 공간 복잡도
- 계수 정렬의 공간복잡도는 $O(N+K)$
- 때에 따라 심각한 비효율성을 초래할 수 있음
- ex) 데이터가 0과 999,999 단 2가지만 존재할 때에도 리스트의 크기가 100만개가 되도록 선언해야함
- → 데이터의 특성을 파악하기 어렵다면 퀵 정렬을 이용하는 것이 유리함
파이썬의 정렬 라이브러리
sorted( )
- 파이썬의 기본 정렬 라이브러리
- 퀵 정렬과 동작 방식이 비슷한 병합 정렬을 기반으로 만들어짐
- 병합 정렬은 일반적으로 퀵 정렬보다는 느리지만 최악의 경우에도 시간복잡도 $O(NlogN)$을 보장한다는 특징이 있음
- sorted( ) 함수는 리스트, 딕셔너리 자료형 등을 입력받아서 정렬된 결과를 리스트로 출력함
- 기본은 오름차순
sorted( )
#6-7.py sorted 소스코드
array = [7,5,9,0,3,1,6,2,4,8]
result = sorted(array)
print(result)
sort( )
#6-8.py sort 소스코드
array = [7,5,9,0,3,1,6,2,4,8]
array.sort()
print(array)
#6-9.py 정렬 라이브러리에서 key를 활용한 소스코드
array = [('바나나',2), ('사과',5), ('당근',3)]
def setting(data):
return data[1]
result = sorted(array, key=setting)
print(result)
→ 또한 sorted( ), sort( )를 이용할 때에는 key 매개변수를 입력으로 받을 수 있음. key 값으로는 하나의 함수가 들어가야 하며 이는 정렬 기준이 됨
- 시간복잡도
- 최악의 경우 $O(NlogN)$
코딩 테스트에서 정렬 알고리즘이 사용되는 경우
- 정렬 라이브러리로 풀 수 있는 문제
- 단순히 정렬 기법을 알고 있는지 물어보는 문제로 기본 정렬 라이브러리의 사용 방법을 숙지하고 있으면 어렵지 않게 풀 수 있음
- 정렬 알고리즘의 원리에 대해서 물어보는 문제
- 선택 정렬, 삽입 정렬, 퀵 정렬 등의 원리를 알고 있어야 문제를 풀 수 있음
- 더 빠른 정렬이 필요한 문제
- 퀵 정렬 기반의 정렬 기법으로는 풀 수 없으며 계수 정렬 등의 다른 정렬 알고리즘을 이용하거나 문제에서 기존에 알려진 알고리즘의 구조적인 개선을 거쳐야 풀 수 있음
'Algorithm' 카테고리의 다른 글
[백준] 11866번 요세푸스 문제 0 Python (0) | 2024.03.30 |
---|---|
[알고리즘] ch05. DFS,BFS (0) | 2023.08.10 |
[백준] 10162 전자레인지 Python (0) | 2023.08.06 |
[백준] 11399 ATM Python (0) | 2023.08.05 |
[알고리즘] ch03. 그리디 알고리즘(Greedy Algorithm) (0) | 2023.08.04 |