정렬 알고리즘의 핵심은 교환, 선택, 삽입!
내부정렬과 외부정렬
버블 정렬
이웃한 두 원소의 대소관계를 비교(단순 교환 정렬)
# [Do it! 실습 6-1] 버블 정렬 알고리즘
from typing import MutableSequence
def bubble_sort(a: MutableSequence) -> None:
"""버블 정렬"""
n = len(a)
# a의 길이 n , 0에서 n-1까지 i 반복
# n-1에서 i까지, -1씩 j 반복 // 마지막 인덱스에서 처음 인덱스까지
# j-1 > j 이면 j-1에 j값을, j에 j-1값을 넣어줌(**교환**)
# j 한 바퀴 도는게 패스 하나
# i가 반복하는 횟수 = 패스 횟수
for i in range(n - 1):
for j in range(n - 1, i, -1):
if a[j - 1] > a[j]:
a[j - 1], a[j] = a[j], a[j - 1]
if __name__ == '__main__':
print('버블 정렬을 수행합니다.')
num = int(input('원소 수를 입력하세요.: '))
**x = [None] * num # 원소 수가 num인 배열을 미리 생성**
for i in range(num):
x[i] = int(input(f'x[{i}] : ')) // input(f'x[{i}] : ') 이게 f가 뭘까
bubble_sort(x) # 배열 x를 버블 정렬
print('오름차순으로 정렬했습니다.')
for i in range(num):
print(f'x[{i}] = {x[i]}')
개선1 : 교환 횟수에 따른 패스 중단
개선2 : 마지막 교환 인덱스에 따른 패스 스킵
셰이커 정렬 : 버블정렬을 개선한 정렬방법으로, 앞뒤방향으로 번갈아가면서 패스를 진행
[ ] 예제 코드 넣기
단순 선택정렬
가장 작은 원소부터 선택해 알맞은 위치로 옮겨서 정렬
배열 안에서 가장 작은 원소를 찾음
→ 맨 앞 원소와 교환
→ 그럼 맨 앞은 정렬된거
이어서 정렬되지 않은 원소들 중 가장 작은 값을 찾고
→ 정렬되지 않은 가장 앞쪽 원소와 교환
→ 을 반복
단순선택정렬에서 교환과정은 다음과 같습니다.
이걸 n-1번 반복하면 정렬이 완료됨
# [Do it! 실습 6-6] 단순 선택 정렬 알고리즘 구현
from typing import MutableSequence
def selection_sort(a: MutableSequence) -> None:
"""단순 선택 정렬"""
n = len(a)
for i in range(n - 1):
min = i # 정렬 할 부분에서 가장 작은 원소의 인덱스
for j in range(i + 1, n):
if a[j] < a[min]:
min = j
a[i], a[min] = a[min], a[i] # 정렬 할 부분에서 맨 앞의 원소와 가장 작은 원소를 교환
if __name__ == '__main__':
print('단순 선택 정렬을 수행합니다.')
num = int(input('원소 수를 입력하세요.: '))
x = [None] * num # 원소 수가 num인 배열을 생성
for i in range(num):
x[i] = int(input(f'x[{i}] : '))
selection_sort(x) # 배열 x를 단순 선택 정렬
print('오름차순으로 정렬했습니다.')
for i in range(num):
print(f'x[{i}] = {x[i]}')
단순 선택 정렬 알고리즘에서 원솟값을 비교하는 횟수는 (n^2 - n)/2
서로 이웃하지 않는 원소를 교환하므로 안정적이지 않음
→ 중복된 숫자를 교환
단순 삽입 정렬
셀 정렬
퀵 정렬
분할 정복 알고리즘, 재귀 호출
# [Do it! 실습 6-10] 퀵 정렬 알고리즘 구현
from typing import MutableSequence
def qsort(a: MutableSequence, left: int, right: int) -> None:
"""a[left] ~ a[right]를 퀵 정렬"""
pl = left # 왼쪽 커서
pr = right # 오른쪽 커서
x = a[(left + right) // 2] # 피벗(가운데 요소)
while pl <= pr: # 실습 6-10과 같은 while 문
while a[pl] < x: pl += 1
while a[pr] > x: pr -= 1
if pl <= pr:
a[pl], a[pr] = a[pr], a[pl]
pl += 1
pr -= 1
if left < pr: qsort(a, left, pr)
if pl < right: qsort(a, pl, right)
def quick_sort(a: MutableSequence) -> None:
"""퀵 정렬"""
qsort(a, 0, len(a) - 1)
if __name__ == '__main__':
print('퀵 정렬을 수행합니다.')
num = int(input('원소 수를 입력하세요.: '))
x = [None] * num # 원소 수가 num인 배열을 생성
for i in range(num):
x[i] = int(input(f'x[{i}]: '))
quick_sort(x) # 배열 x를 퀵 정렬
print('오름차순으로 정렬했습니다.')
for i in range(num):
print(f'x[{i}] = {x[i]}')
# [Do it! 실습 6-12] 퀵 정렬 알고리즘 구현(비재귀적인 퀵 정렬)
from stack import Stack # 실습 4C-1의 파일 import
from typing import MutableSequence
def qsort(a: MutableSequence, left: int, right: int) -> None:
"""a[left] ~ a [right]를 퀵 정렬(비재귀 버전)"""
**range = Stack(right - left + 1) # 스택 생성**
**range.push((left, right))**
while not range.is_empty():
**pl, pr = left, right = range.pop()** # 왼쪽, 오른쪽 커서를 꺼냄
x = a[(left + right) // 2] # 피벗(중앙 요소)
while pl <= pr:
while a[pl] < x: pl += 1
while a[pr] > x: pr -= 1
if pl <= pr: # 실습 6-10, 실습 6-11과 같음
a[pl], a[pr] = a[pr], a[pl]
pl += 1
pr -= 1
**if left < pr: range.push((left, pr))** # 왼쪽 그룹의 커서를 저장
**if pl < right: range.push((pl, right))** # 오른쪽 그룹의 커서를 저장
def quick_sort(a: MutableSequence) -> None:
"""퀵 정렬"""
qsort(a, 0, len(a) - 1)
if __name__ == '__main__':
print('비재귀적인 퀵 정렬')
num = int(input('원소 수를 입력하세요.: '))
x = [None] * num # 원소 수가 num인 배열을 생성
for i in range(num):
x[i] = int(input(f'x[{i}]: '))
quick_sort(x) # 배열 x를 퀵 정렬
print('오름차순으로 정렬했습니다.')
for i in range(num):
print(f'x[{i}] = {x[i]}')
병합 정렬
힙 정렬
선택 정렬을 응용한 알고리즘
힙의 특성을 이용하여 정렬하는 알고리즘
부모와 자식간의 대소관계는 일정하지만 형제사이의 대소관계는 일정하지 않음
(부분 순서 트리)
힙을 배열에 저장하기
가장 위쪽에 있는 루트를 a[0]에 저장
한단계 아래에서 왼쪽에서 오른쪽원소로 따라감
배열의 인덱스값을 1씩 증가시키면서 각 원소를 저장
0
1 2
3 4 5 6
7 8 9
뭐 대충 이런 순서
이런 순서로 힙을 배열에 저장하면 규칙이 생김
원소 a[i]에서
힙 정렬의 특징 :: 힙에서 최댓값은 루트에 위치한다.
루트를 삭제한 힙의 재구성
힙 정렬 알고리즘
배열을 힙으로 만들기
시간복잡도
heapq 모듈도 있음
도수 정렬