ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Sort Algorithm(Bubble/Insert/Selection/Merge/Quick)과 시간복잡도
    Algorithm 2021. 2. 25. 15:18

    1. Bubble Sort

    : 인접한 원소끼리 대소를 비교하고 swap시켜 unsorted part중 가장 작은(sort priority가) element를 sorted part의 직전에 위치시키는 정렬

    bubbleSort(A[1 ... N], N):
      for i in [1 ... N]:
          for j in [1 ... N-i]:
              if A[j] > A[j+1]:
                  swap A[j] and A[j+1]
    • bubble sort의 validaity: i번째 stage 후에 Array에서 i번째로 큰 값은 항상 A[n-1-i]번째에 위치하게 된다(loof invariant). 즉, 정렬되어 위치해야 할 올바른 위치에 자리잡게 된다.
    • time complexity: O(N²)
    • i번째 stage에서 가장 큰 element를 sorted part로 보내는 과정에서 비교 연산을 N-1-i번 수행한다
    • Array의 크기를 N이라 할 때 N-1번의 stage를 수행한다

     

    2. Selection Sort

    : unsorted part에서 가장 큰(sort priority) element를 찾아 sorted part의 직후의 element와 swap시키는 정렬

    selectionSort(A[1 ... N], min, max):
    	if max <= min: return
        min_index = min
        
        for i in [min ... max+1):
        	if A[i] < A[min_index]:
            	min_index = i
        
        swap A[min] and A[min_index]
        selectionSort(A, min+1, max)
    • Recursion, Iteration 두 가지 방법으로 구할 수 있다.
    • 한번 호출 시 마다 sorted part에서의 element의 위치가 결정된다(loof invariant)
    • time complexity: O(N²)
    • selectionSort 함수는 한번 호출 시 Array의 size를 N이라 할 때 linear search를 통해 총 O(N)번의 비교 연산을 수행한다.
    • selectionSort 함수는 총 N번 호출되며 i번째 호출 시 subArray 크기는 N-i이다.

     

    3. Insertion Sort

    : Array를 sorted part와 unsorted part로 나눈 뒤, i번째 stage에서 해당 시점의 unsorted part의 제일 왼쪽 element를 sorted part에 insert하는 정렬.

    insertionSort(A[1 ... N], N):
      for i in [2 ... N]:
          for j in [i-1 ... 0] and A[j] > A[j+1]:
              swap A[j] and A[j+1]

     

     

    • 각각의 stage에서는 insertion을 통해 sort를 수행하고, 최종적으로 sorted part의 크기를 N으로 만든다.
    • i번째 stage가 끝나면 A[0 ... i]가 sorted part가 된다(loof invariant)
    • insertion: unsorted part의 제일 왼쪽 element를 sorted part의 오른쪽 원소부터 비교해가며 swap한다. 현재보다 작은 element가 발견될 경우 이후의 모든 element와는 비교할 필요가 없다.
    • time complexity: O(N²)
    • 거의 정렬된 Array를 정렬할 경우 빠른 성능을 보인다.
    • 계속해서 인접한 element와의 비교 및 swap을 진행하기 때문에 참조 지역성의 원리를 잘 만족한다.

     

     

    4. Merge Sort

    : Array를 작은 subarray로 Divide하고, sorted된 subarray들을 Merge해가면서 전체 Array를 정렬.

    mergeSort(A[1 ... N], left, right):
        if right <= left: 
        	return
            
    	middle = (left + right) / 2
        
        left = mergeSort(A, min, middle-1)
        right = mergeSort(A, middle, max)
        merge(A, left, middle, right)
    end
    
    
    merge(A[0 ... N], left, mid, right):
    	l = mid - left + 1
        r = right - mid
        
        allocate L[1 ... l] and R[1 ... r]
        
        for i = 1 to l:
        	L[i] = A[left -1 + i]
        for j = 1 to r:
        	R[i] = A[mid + j]
        
        L[l + 1] = R[r + 1] = INF
        
        i = 1
        j = 1
        for k = left to right:
        	if L[i] <= R[j]:
            	A[k] = L[i]
                i = i + 1
            else:
            	A[k] = R[j]
                j = j + 1
    end

     

     

    • divide and conquer 알고리즘을 이용한 정렬.
    • 인접한 덩어리를 merge하기에 참조 지역성의 원리를 잘 만족하지만 merge에서 할당하는 추가적인 공간 낭비가 심하다(Array size에 비례하게). in-place sorting이 아니다.
    • size N인 array를 sort하는 시간 = (size가 n/2인 subarray를 merge sort) + (두 subarray를 merge하는 시간)
    • T(n) = 2*T(n/2) + O(n) => time complexity: O(N logN)

    5. Quick Sort

    : pivot을 선정하여 기준으로 잡고 대소관계를 따져 subarray를 분할한 뒤 merge하는 정렬

    quickSort(A[1 ... N], left, right):
    	if right <= left:
        	return
        else:
            pivot = partitioning(A, left, right)
            quickSort(left, pivot - 1)
            quickSort(pivot + 1, right)
    end
    
    partitioning(A[1 ... N], left, right):
    	pivot = A[right]
        
        i = left - 1
        for j = left to high - 1:
        	if A[j] < pivot:
            	i++
            	swap A[i] and A[j]
        
        swap A[i + 1] and A[right]
        return i + 1
    end
        	
    • pivot을 잘못 선정할 경우 recursion tree가 skewed binary tree(편향 이진 트리)의 형태를 띔
    • size N인 array를 sort하는 이상적인(best-case) 시간 = (size가 n/2인 subarray를 quick sort) + (두 subarray를 merge하는 시간)
    • time complexity: worst-case O(N²) / average-case O(N logN) -> pivot을 uniformly random하게 선정한다고 가정.
    • bast-case에 해당하는 pivot인 median을 O(n)에 구하는 알고리즘인 Median of Medians Algorithm이 존재함(2jinishappy.tistory.com/127?category=903864)
     

    Quick Select를 O(n)에 구현 가능한 Median of Medians 알고리즘

    2jinishappy.tistory.com/124 Array의 k번째 작은 element 찾기 - QuickSelect 알고리즘 Size가 n인 int형 Array에서 k번째로 작은 element를 찾는 방법은 여러 가지가 있다. 가장 formal하게, 배열을 sort한 뒤..

    2jinishappy.tistory.com

     

    https://lamfo-unb.github.io/img/Sorting-algorithms/Complexity.png

    해당 알고리즘들을 포함한 sort algorithm들의 best/average/worst case의 time complexity를 정리한 표 이다.

    참고로 Java와 Python에서는 Insertion Sort와 Merge Sort를 결합한 Tim Sort 알고리즘을 Sort Algorithm으로 사용하고 있다.

Designed by Tistory.