728x90

정렬(Sorting)

데이터를 특정한 기준에 따라서 순서대로 나열하는 것

 

선택 정렬(Selection Sort)

가장 작은 데이터를 선택 -> 맨 앞에 있는 데이터와 교환 -> 그 다음 작은 데이터를 선택 -> 앞에서 두 번째 데이터와 교환

 

예시)

 

7 5 9 0 3 1 6 2 4 8

 

가장 작은 데이터인 '0'와 맨 앞 데이터인 '7'을 교환한다.

 

0 5 9 7 3 1 6 2 4 8

 

그 다음 작은 데이터인 '1'과 두 번째 데이터인 '5'를 교환한다.

 


 

0 1 2 3 4 5 6 7 9 8

 

위의 과정을 9번 반복하면 다음과 같이 정렬이 완료된다.

 

0 1 2 3 4 5 6 7 8 9

 

선택 정렬은 데이터 교환 과정을 N-1번 반복하면 완료된다.

 

 

 

선택 정렬 소스코드)

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)으로 느린 편이다.

하지만 코테에서는 가장 작은 인덱스를 찾는 과정이 있으므로 위의 과정은 익숙해지는 것이 좋다.

 

 

 

삽입 정렬(Insertion Sort)

특정한 데이터를 적절한 위치에 삽입하는 정렬. 이때 삽입되기 이전, 그 앞까지의 데이터는 이미 정렬되어 있다고 가정한다.

 

예시)

 

7 5 9 0 3 1 6 2 4 8

 

첫 번째 데이터인 '7'은 그 자체로 정렬되어 있다고 판단하고, 두 번째 데이터인 '5'가 어떤 위치로 들어갈지 판단한다.

'7'의 왼쪽 또는 오른쪽 중에서 결정하는데 왼쪽에 삽입한다.

(본 위치에서 왼쪽으로 차례차례 확인하는 것)

 

5 7 9 0 3 1 6 2 4 8

 

다음 '9'가 어느 위치에 들어갈지 판단한다. 들어갈 수 있는 위치는 총 3가지이다.

'5'의 왼쪽, 오른쪽, 그리고 '7'의 오른쪽이다.

적절한 위치는 '7'의 오른쪽이므로 그대로 둔다.

 

5 7 9 0 3 1 6 2 4 8

 

'0'은 총 4가지의 위치 중 '5'의 왼쪽에 해당하므로 해당 위치에 삽입한다.

 

0 5 7 9 3 1 6 2 4 8

 

'3'은 '0'과 '5'사이에 삽입한다.

 


 

0 1 2 3 4 5 6 7 9 8

 

위의 과정을 N-1번 반복하면 다음과 같이 정렬이 완료된다.

 

0 1 2 3 4 5 6 7 8 9

 

삽입 정렬의 특징으로는 정렬이 이루어진 원소는 항상 오름차순이다.(내림차순으로 정렬했다면 내림차순!)

예시의 파란색 숫자를 보면 이해하기 편하다.

따라서 삽입 위치를 선정할 때, 삽입될 데이터보다 작은 데이터를 만나면, 그 위치에 멈추면 된다.

 

 

삽입 정렬 소스코드)

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):
        if array[j] < array[j-1]:	# 한 칸씩 왼쪽으로 이동
            array[j], array[j-1] = array[j-1], array[j]
        else:	# 자기보다 작은 데이터를 만나면 그 위치에서 멈추기
            break
    
print(array)

시간 복잡도: O(N^2)

단, 최선의 경우(현재 리스트의 데이터가 거의 정렬되어 있는 상태) 시간 복잡도: O(N)

 

 

 

퀵 정렬(Quick Sort)

기준 데이터(pivot)를 설정하고 그 기준보다 큰 데이터와 작은 데이터의 위치를 바꾸는 정렬

가장 많이 사용하는 정렬!

 

예시)

 

5 7 9 0 3 1 6 2 4 8

 

처음 pivot을 설정할 땐 맨 처음 데이터로 설정한다.

2번째 데이터부터 차례로 오른쪽으로 가면서 pivot보다 작은 수를 찾는다.

맨 끝 데이터부터 차례로 왼쪽으로 가면서 pivot보다 큰 수를 찾는다.

 

5 4 9 0 3 1 6 2 7 8

 

찾은 후에는 데이터를 교환한다.

이를 pivot보다 작은 수pivot보다 큰 수위치가 엇갈릴 때까지 반복한다.

 


 

5 4 2 0 3 1 6 9 7 8

 

엇갈릴 때 pivot과 pivot보다 작은 수를 교환한다!

즉, 위의 예시에서는 1과 5를 교환한다.

 

1 4 2 0 3 5 6 9 7 8

 

그렇다면 pivot인 5를 기준으로 왼쪽은 pivot보다 작은 수, 오른쪽은 pivot보다 큰 수로 구성된다.

이 상태에서, 왼쪽과 오른쪽을 각각 또 정렬한다!

이 과정을 반복하면 전체 정렬이 완료된다.

 

0 1 2 4 3 5 6 9 7 8
0 1 2 4 3 5 6 8 7 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9

 

위의 예시에 해당하는 quick sort를 실행한 결과를 보여준 것이다.

각 과정에서 pivot을 표시했다.

 

 

퀵 정렬 소스코드)

array = [5, 7, 9, 0, 3, 1, 6, 2, 4, 8]

def quick_sort(array, start, end):
    if start >= end:    # 원소가 1개인 경우 종료
        return
    pivot = start   # pivot은 첫 번째 원소
    left = start + 1
    right = end
    while left <= right:
        # pivot보다 큰 데이터 찾을때까지 반복
        while left <= end and array[left] <= array[pivot]:
            left += 1
        # pivot보다 작은 데이터 찾을때까지 반복
        while right >= start and array[right] >= array[pivot]:
            right -= 1
        if left > right:    # 엇갈린다면 작은 데이터와 pivot 교체
            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)

# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 

시간 복잡도:

평균 시간 복잡도: O(NlogN)

최악 시간 복잡도: O(N^2) => 데이터가 이미 정렬되어 있을 경우!

 

 

 

계수 정렬(Count Sort)

특정한 조건이 부합할 때만 사용할 수 있는 알고리즘. 매우 빠르다!

최악의 경우에도 O(N + K)를 보장한다.(K: 데이터 중 최댓값)

 

하지만, 데이터의 크기 범위가 제한되어 정수 형태로 표현 가능할 경우에만 사용할 수 있다.

일반적으로 가장 큰 데이터와 가장 작은 데이터의 차이가 1,000,000을 넘지 않을 때 사용 가능하다.

위에 해당하는 이유는 모든 범위를 담을 수 있는 크기의 배열을 선언해야 하기 때문이다.

 

방법:

가장 큰 데이터와 가장 작은 데이터의 범위가 모두 담긴 하나의 리스트 생성 -> 리스트의 모든 데이터를 0으로 초기화 -> 데이터를 하나씩 확인하며 데이터의 값과 동일한 인덱스의 데이터를 1씩 증가

 

예시)

 

7 5 9 0 3 1 6 2 9 1 4 8 0 5 2

0 2 3 4 5 6 7 8 9
0 0 0 0 0 0 0 1 0 0

 

데이터 7에 해당하는 인덱스의 값을 1 늘려준다.

 

7 5 9 0 3 1 6 2 9 1 4 8 0 5 2

0 2 3 4 5 6 7 8 9
0 0 0 0 0 1 0 1 0 0

 

데이터 5에 해당하는 인덱스의 값을 1 늘려준다.

이를 반복하면 다음과 같다.

 

7 5 9 0 3 1 6 2 9 1 4 8 0 5 2

0 2 3 4 5 6 7 8 9
2 2 2 1 1 2 1 1 1 2

 

결과적으로 리스트에는 각 데이터가 몇 번 등장했는지 횟수가 기록된다.

이것 자체가 정렬된 형태라고 볼 수 있다!

출력만 하면 완료됨!!

 

 

계수 정렬 소스코드)

# 모든 원소의 값이 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 = ' ')

# 0 0 1 1 2 2 3 4 5 5 6 7 8 9 9

 

시간 복잡도: O(N + K)

공간 복잡도: O(N + K)

계수 정렬은 데이터의 크기가 한정되어 있고, 데이터의 크기가 많이 중복되어 있을수록 적합하다!

 

 

 

728x90

+ Recent posts