# -*- coding: utf-8 -*-
import unittest
class Exam08(unittest.TestCase):
@classmethod
def setUp(cls):
pass
def test_selection_sort(self):
"""선택 정렬을 이용한 정렬 방법"""
data = [2, 11, 3, 91, 7, 50, 33]
length = len(data)
for i in range(length):
min_idx = i
for j in range(i+1, length):
# 더 작은 최솟값이 있다면, 교환(swap)한다.
if data[min_idx] > data[j]:
tmp = data[min_idx]
data[min_idx] = data[j]
data[j] = tmp
print("{} 회차: {}".format(i+1, data))
self.assertEqual([2, 3, 7, 11, 33, 50, 91], data)
if __name__ == "__main__":
unittest.main()
import unittest
def selectionSort(input):
for i in range(len(input) - 1):
# assume the min is the first element
idx_min = i
j = i + 1
# test against elements after i to find the smallest
while j < len(input):
if(input[j] < input[idx_min]):
# found new minimum; remember its index
idx_min = j
j = j + 1
if idx_min is not i:
# swap
input[idx_min], input[i] = input[i], input[idx_min]
return input
class unit_test(unittest.TestCase):
def test(self):
self.assertEqual([1, 2, 3, 4, 5, 6], selectionSort([4, 6, 1, 3, 5, 2]))
self.assertEqual([1, 2, 3, 4, 5, 6], selectionSort([6, 4, 3, 1, 2, 5]))
self.assertEqual([1, 2, 3, 4, 5, 6], selectionSort([6, 5, 4, 3, 2, 1]))
삽입 정렬(Insertion Sort)은 첫번째 원소부터 하나하나 차례대로 비교해가면서, 자신보다 큰 값이 나타나면 그 큰 값 앞에 놓는 방식이다.
일반적인 삽입정렬
# -*- coding: utf-8 -*-
import unittest
class Exam09(unittest.TestCase):
@classmethod
def setUp(cls):
pass
def test_insertion_sort(self):
data = [2, 4, 5, 1, 3]
n = len(data)
for i in range(1, n):
key = data[i]
j = i - 1
while j >= 0 and data[j] > key:
data[j + 1] = data[j] # 오른쪽으로 밀어준다. (공간 확보)
j -= 1
data[j + 1] = key
# print(key, data)
self.assertEqual([1, 2, 3, 4, 5], data)
if __name__ == "__main__":
unittest.main()
출처: https://memostack.tistory.com/26 [MemoStack:티스토리]
# -*- coding: utf-8 -*-
import unittest
class Exam09(unittest.TestCase):
@classmethod
def setUp(cls):
pass
def test_insertion_sort_with_python(self):
data = [2, 4, 5, 1, 3]
sorted_data = list()
while data:
value = data.pop(0)
n = len(sorted_data) # value 보다 큰 원소가 없는 경우 맨 뒤로 배치 (default)
insert_idx = n
# value 보다 큰 원소를 찾는다.
for i in range(n):
if value < sorted_data[i]:
insert_idx = i
break
sorted_data.insert(insert_idx, value)
# print(value, sorted_data)
self.assertEqual([1, 2, 3, 4, 5], sorted_data)
if __name__ == "__main__":
unittest.main()
삽입정렬의 시간복잡도는 O(N^2)로 상당히 오래 걸리는 편이다.
# -*- coding: utf-8 -*-
import unittest
class Exam12(unittest.TestCase):
@classmethod
def setUp(cls):
pass
def test_bubble_sort(self):
data = [2, 6, 3, 4, 8, 9, 1, 10]
n = len(data)
for _ in range(n-1):
for i in range(n-1):
if data[i] > data[i+1]:
data[i], data[i+1] = data[i+1], data[i]
self.assertEqual([1, 2, 3, 4, 6, 8, 9, 10], data)
if __name__ == "__main__":
unittest.main()
시간복잡도는 O(N^2)로 상당히 오래 걸리는 편이다.
병합 정렬은 그룹을 반으로 나눈다음 합칠때 크기에 맞게 고친다. 이 과정을 재귀적으로 수행한다.
일반적인 병합 정렬
# -*- coding: utf-8 -*-
import unittest
class Exam10(unittest.TestCase):
@classmethod
def setUp(cls):
pass
def test_merge_sort(self):
"""일반적인 병합 정렬"""
def _merge_sort(group):
# 재귀 종료 조건: 그룹이 없을때
n = len(group)
if n <= 1:
return
# 반으로 그룹을 나눈다.
mid_idx = n // 2
group1 = group[:mid_idx]
group2 = group[mid_idx:]
# print(group, group1, group2)
# 재귀적으로 병합 정렬을 수행한다.
_merge_sort(group1)
_merge_sort(group2)
# 정렬한다.
idx, idx1, idx2 = 0, 0, 0 # 전체 그룹 인덱스, 그룹1 인덱, 그룹2 인덱스
while idx1 < len(group1) and idx2 < len(group2):
if group1[idx1] < group2[idx2]:
group[idx] = group1[idx1]
idx1 += 1
else:
group[idx] = group2[idx2]
idx2 += 1
idx += 1
# 그룹에 남아있는 원소를 넣는다.
while idx1 < len(group1):
group[idx] = group1[idx1]
idx1 += 1
idx += 1
while idx2 < len(group2):
group[idx] = group2[idx2]
idx2 += 1
idx += 1
data = [38, 27, 43, 3, 9, 82, 10]
_merge_sort(data)
self.assertEqual([3, 9, 10, 27, 38, 43, 82], data)
if __name__ == "__main__":
unittest.main()
# -*- coding: utf-8 -*-
import unittest
class Exam10(unittest.TestCase):
@classmethod
def setUp(cls):
pass
def test_merge_sort_with_python(self):
"""파이썬을 이용한 병합 정렬"""
def _merge_sort(group):
# 종료 조건 설정
n = len(group)
if n <= 1:
return group
# 그룹 나누기
mid_idx = n // 2
group1 = _merge_sort(group[:mid_idx])
group2 = _merge_sort(group[mid_idx:])
# 정렬
result = list()
while group1 and group2:
result.append(group1.pop(0) if group1[0] < group2[0] else group2.pop(0))
result.extend(group1)
result.extend(group2)
# print(result)
return result
data = [38, 27, 43, 3, 9, 82, 10]
sorted_data = _merge_sort(data)
self.assertEqual([3, 9, 10, 27, 38, 43, 82], sorted_data)
if __name__ == "__main__":
unittest.main()
반으로 나누어서 문제를 푸는 방식을 분할 정복(Divide and Conquer)이라고 한다.
분할 정복은 시간복잡도가 O(N logN)이다. 선택 정렬이나 삽입 정렬의 시간복잡도(ON^2)보다 낮아, 비교적 빠르다.
즉, 개수가 많아질수록 병합정렬(Merge Sort)을 이용하는것이 효율적이다.
퀵 정렬(Quick Sort)은 병합 정렬(Merge Sort)과 비슷하게 재귀 호출을 하여 정렬을 한다. 다만 퀵 정렬은 기준점과 미리 비교를 한다는 차이점이 있다.
일반적인 퀵 정렬
# -*- coding: utf-8 -*-
import unittest
class Exam11(unittest.TestCase):
@classmethod
def setUp(cls):
pass
def test_quick_sort(self):
"""일반적인 퀵 정렬"""
def _quick_sort(data, start, end):
# 종료 조건: 정렬을 할 원소가 1개 이하인 경우
if start >= end:
return
# 피봇 설정 (편의상 맨 마지막 원소를 피봇으로 지정)
pivot = data[end]
left = start
# 왼쪽은 피봇보다 작은 수가 위치한다.
# 오른쪽은 피봇보다 큰 수가 위치한다.
for right in range(start, end):
if data[right] <= pivot:
# data[right], data[left] = data[left], data[right] 와 동일
tmp = data[right]
data[right] = data[left]
data[left] = tmp
left += 1
# 맨 마지막에 위치한 피봇과 위치를 바꿔준다.
# [피봇보다 작은 수들] + [피봇] + [피봇보다 큰 수]
data[end], data[left] = data[left], data[end]
_quick_sort(data, start, left - 1)
_quick_sort(data, left + 1, end)
data = [6, 8, 3, 9, 10, 1, 2, 4, 7, 5]
_quick_sort(data, 0, len(data) - 1)
self.assertEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], data)
if __name__ == "__main__":
unittest.main()
# -*- coding: utf-8 -*-
import unittest
class Exam11(unittest.TestCase):
@classmethod
def setUp(cls):
pass
def test_quick_sort_with_python(self):
def _quick_sort(data):
# 종료 조건
n = len(data)
if n <= 1:
return data
# 피봇 값은 어떤값으로 하든 상관없음. 편의상 맨 마지막을 피봇으로 지정
pivot = data[-1]
group1 = list() # 피봇보다 작은 값
group2 = list() # 피봇보다 큰 값
for num in data[:-1]:
if num < pivot:
group1.append(num)
else:
group2.append(num)
return _quick_sort(group1) + [pivot] + _quick_sort(group2)
data = [4, 2, 6, 5, 3, 9]
self.assertEqual([2, 3, 4, 5, 6, 9], _quick_sort(data))
if __name__ == "__main__":
unittest.main()
시간 복잡도는 병합 정렬과 똑같이 분할 정복을 수행하려 들기 때문에 O(N * logN)이다.
다만, 대부분은 O(NlogN)이지만 최악의 경우 O(N^2)까지 갈 수 있다.