-
[이코테 # 정렬1] 정렬 소스코드, 선택/삽입/퀵/계수 정렬, sorted()와 sort()의 차이Programming 기초/Coding Test 2023. 6. 9. 02:21
* 선택 정렬
- 시간복잡도 : O(n^2)
- 이전 포스팅 선택/삽입 정렬 참고 : https://operationcoding.tistory.com/45
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 = j array[i], array[min_index] = array[min_index], array[i] # 스와프 print(array)
* 삽입 정렬
- 시간 복잡도: 최악의 경우는O(n^2)이지만 보통 어느정도 정렬된 상태에서 삽입 정렬을 쓰기 때문에 최선의 경우에는 O(n)으로 퀵 정렬 알고리즘보다 더 강력하다.
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까지 1씩 감소하며 반복하는 문법 if array[j] < array[j - 1]: # 한 칸씩 왼쪽으로 이동 array[j], array[j - 1] = array[j - 1], array[j] else: # 자기보다 작은 데이터를 만나면 그 위치에서 멈춤 break print(array)
* 퀵 정렬
- 시간 복잡도 : O(NlogN) (밑이 2다.) 최악의 경우 O(n^2)이다.
- 이전 포스팅에서와 아래 퀵 정렬(Hoare Partition 방식)의 정렬 방식이 약간 다르다(https://operationcoding.tistory.com/60)
# 퀵 정렬 정석 버전 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)
# 퀵 정렬 짧은 버전 - 정석버전보다 시간효율이 약간 떨어진다. 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] # 분할된 오른쪽 부분 # 리스트 컴프리헨션 -> tail의 원소 중의 피벗 보다 작거나큰(혹은 큰) 요소를 차례대로 x에 대입 # 분할 이후 왼쪽 부분과 오른쪽 부분에서 각각 정렬을 수행하고, 전체 리스트를 반환 return quick_sort(left_side) + [pivot] + quick_sort(right_side) print(quick_sort(array))
* 계수 정렬(count sort)
- 특정한 조건이 부합할 때만 사용할 수 있음 : '데이터의 크기 범위가 제한(일반적으로 ~1,000,000)되어 정수 형태로 표현할 수 있을 때'
- 시간 복잡도 : O(N+K)
- 이전 포스팅 참고 : https://operationcoding.tistory.com/61
# 모든 원소의 값이 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=' ') # 띄어쓰기를 구분으로 등장한 횟수만큼 인덱스 출력
* sorted() 와 sort()
- sorted()는 파이썬 기본 정렬 라이브러리이다. ex) sorted(arr), 리스트나 딕셔너리 자료형을 매개변수로 입력받는다.(리턴값이 리스트다.)
- sort()는 리스트 객체의 내장 함수이다. ex) arr.sort(). 별도의 정렬된 리스트가 반환되지 않고 내부 원소가 바로 정렬됨.(리턴값이 없다. None)
- sorted()와 sort() 둘 다, key 매개변수를 입력으로 받을 수 있다. key 값으로 하나의 함수(lambda함수를 많이 사용한다.)가 들어가야 하며, 이는 정렬기준이된다.
- 시간 복잡도 O(nlogn)
- key옵션을 주면 기준값에 대해서만 정렬되고 나머지 값들은 본래 순서대로 보장된다. (정렬 안정성 참고)
# 정렬 라이브러리에서 key를 활용한 소스코드 array = [('바나나', 2), ('사과', 5), ('당근', 3)] def setting(data): return data[1] result = sorted(array, key=setting) print(result) # 람다함수를 활용 array = [("바나나", 2), ("사과", 5), ("당근", 3)] result = sorted(array, key=lambda data: data[1]) print(result)
기준이 여러개 일 때 key에 lambda함수 작성 예시
a = [(1, 99, 10000), (2, 98, 10002), (3, 97, 105), (4, 96, 105), (5, 95, 100)] a.sort(key=lambda a: (a[2], a[0])) # 기준이 여러 개일 때 print(a) -------------------------------------- [(5, 95, 100), (3, 97, 105), (4, 96, 105), (1, 99, 10000), (2, 98, 10002)]
< 정렬에서 key 매개변수에 대한 고찰 >
의문.
lambda 함수는 data[1]을 출력한다. data[1]은 array[1]을 뜻한다고 생각했고, 그래서 ("바나나",2)가 반환되어야 한다고 생각했다. 하지만 위 코드의 결과는 각 리스트 요소의 두 번째 요소를 반환한다.
의문 해결.
sort 함수의 설명서를 찾아봤다.
"list.sort()와 sorted()는 모두 비교하기 전에 각 리스트 요소에 대해 호출할 함수(또는 다른 콜러블)를 지정하는 key 매개 변수를 가지고 있습니다."
즉, data[1]은 array[1]을 의미하는 것이 아닌 data변수에 리스트의 각 요소인 array[0] ~ array[2]이 입력되는 것으로, data[1]은 array[0][1], array[1][1], array[2][1]를 의미한다.* 소스코드 출처
'이것이 코딩테스트다 ' 깃허브
https://github.com/ndb796/python-for-coding-test
'Programming 기초 > Coding Test' 카테고리의 다른 글
[Python Tip#1] input()보다 sys.stdin.readline()의 처리속도가 더 빠르다. (0) 2023.06.11 [이코테 # 정렬2] 기본 정렬 라이브러리를 활용한 문제들 (0) 2023.06.09 [이코테 # DFS/BFS 2] 음료수 얼려 먹기(DFS), 미로탈출(BFS) (0) 2023.06.07 [이코테 # DFS/BFS 1] DFS, BFS 예제 (0) 2023.06.07 [이코테 # 구현3] 시뮬레이션 문제 : 게임 개발 (0) 2023.06.07