데이터를 특정한 기준에 따라 순서대로 나열하는 것
처리되지 않은 데이터 중에서 가장 작은 데이터를 선택해
맨 앞에 있는 데이터와 바꾸는 것을 반복하는 정렬
array = list(map(int, input().split()))
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 = j # 그 원소를 가장 작은 원소로 입력
# 가장 작은 원소와 맨 앞의 원소를 교환
array[i], array[min_index] = array[min_index], array[i]
print(array)
9 6 7 3 5
[3, 5, 6, 7, 9]
선택 정렬은 N번 만큼 가장 작은 수를 찾아서 맨 앞으로 보내야 하므로
N + (N - 1) + (N - 2) + ... + 2 = (N^2 + N -2) / 2
즉 시간 복잡도는 O(N^2)이다.
처리되지 않은 데이터를 하나씩 골라 적절한 위치에 삽입한다.
array = list(map(int, input().split()))
for i in range(len(array)):
# i부터 0까지 감소하며 반복된다.
for j in range(i, 0, -1):
# 왼 쪽의 값이 오른 쪽의 값보다 크면
if array[j] < array[j - 1]:
# 두 값을 교체한다.
array[j], array[j - 1] = array[j - 1], array[j]
# 왼쪽의 값이 오른 쪽의 값과 작거나 같으면 = 올바른 위치이므로 탈출
else:
break
print(array)
9 6 7 3 5
[3, 5, 6, 7, 9]
선택 정렬과 마찬가지로 반복문이 두 번 중첩되어 사용되므로 O(N^2)이다.
하지만 거의 정렬되어 있는 상태에서는 매우 빠르게 동작한다.
즉 최선의 경우, O(N)이 될 수 있다.
기준 데이터(Pivot)를 설정하고 그 기준보다 큰 데이터와 작은 데이터의 위치를 바꾸는 방법으로 첫 번째 데이터를 기준 데이터(Pivot)로 설정한다.
array = list(map(int, input().split()))
def quick_sort(array, start, end):
if start >= end: # 원소가 1개인 경우 종료
return
pivot = start # 피벗은 첫 번째 원소
left = start + 1 # 피벗을 제외한 가장 왼쪽
right = end # 가장 오른쪽
# left가 right보다 커질 경우 종료 => 엇갈릴 때까지
while(left <= right):
# left가 끝까지 가면서 피벗보다 큰 데이터를 찾을 때까지 반복
while(left <= end and array[left] <= array[pivot]):
left += 1
# right가 맨 앞으로 가면서 피벗보다 작은 데이터를 찾을 때까지 반복
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)
9 6 7 3 5
[3, 5, 6, 7, 9]
평균 연산 횟수는 O(NlogN)의 시간 복잡도를 가질 수 있지만
최악의 경우, O(N^2)의 시간 복잡도를 가질 수 있다.
첫 번째 원소를 피벗으로 삼을 때, 이미 정렬된 배열에 대해서 수행한다면
분할이 한 쪽으로만 이루어지므로 O(N^2)이다.
리스트 슬라이싱과 리스트 컴프레싱을 통해서 간결하게 작성할 수 있다.
array = list(map(int, input().split()))
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))
데이터의 크기 범위가 제한되어 정수 형태로 표현할 수 있을 때 사용 가능하며
데이터 개수 N, 최댓값 K일 때, 최악의 경우에도 O(N + K)를 보장한다.
array = list(map(int, input().split()))
# 모든 원소의 범위를 포함하는 리스트 선언
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 = ' ')
7 5 9 0 3 1 6 2 9 1 4 8 0 5 2
0 0 1 1 2 2 3 4 5 5 6 7 8 9 9
계수 정렬의 시간 복잡도와 공간 복잡도는 모두 O(N + K)이다.
만약 0과 999,999로 2개만 존재하는 경우, 심각한 비효율성을 초래할 수 있다.
따라서 계수 정렬은 데이터의 분포를 미리 파악해야 하고
특히 동일한 값을 가지는 데이터가 여러 개 등장할 때 효과적으로 사용할 수 있다.
하지만 표준 정렬 라이브러리는 최악의 경우에도 O(NlogN)을 보장하므로
문제에서 정렬 구현을 요구하지 않는다면 sort()를 사용하자!
두 배열 A, B는 N개의 원소로 구성되어 있으며 배열의 원소는 모두 자연수이다.
최대 K번의 바꿔치기 연산을 수행할 수 있는데 바꿔치기 연산이란 배열 A의 원소 하나와 배열 B의 원소 하나를 골라서 서로 바꾸는 것을 말한다.
배열 A의 모든 원소의 합이 최대가 되도록 프로그램을 작성하라.
(1 <= N <= 100,000 / 0 <= K <= N)
풀이)
N = 100,000이므로 표준 정렬 라이브러리[O(NlogN)]을 사용해도 된다.
a는 오름차 정렬, b는 내림차 정렬을 하고 비교하면 된다.
n, k = map(int, input().split())
# 배열 입력
array_a = list(map(int, input().split()))
array_b = list(map(int, input().split()))
array_a.sort() # 오름차 순 정렬
array_b.sort(reverse = True) # 내림차 순 정렬
for i in range(k):
# a의 원소보다 b의 원소가 클 때
if (array_a[i] < array_b[i]):
# 두 원소를 교체 (바꿔치기 연산)
array_a[i], array_b[i] = array_b[i], array_a[i]
# a의 원소가 b의 원소보다 크거나 같을 때
else:
# a는 오름차 정렬, b는 내림차 정렬으로 더 비교할 필요가 없다.
break
print(sum(array_a))
5 3
1 2 5 4 3
5 5 6 6 5
26
이코테 2021 강의 4편
이것이 코딩 테스트다 교재