一文總結十大經典排序算法(思維導圖 + 動圖演示 + 代碼實現 C/C++/Python + 致命吐槽)

聲明

1)該文章整理自網上的大牛和專家無私奉獻的資料,具體引用的資料請看參考文獻。
2)本文僅供學術交流,非商用。如果某部分不小心侵犯了大家的利益,還望海涵,並聯系博主刪除。
3)博主才疏學淺,文中如有不當之處,請各位指出,共同進步,謝謝。
4)此屬於第一版本,若有錯誤,還需繼續修正與增刪。還望大家多多指點。大家都共享一點點,一起爲祖國科研的推進添磚加瓦。

0、寫在前面

最近終於是學到了排序算法,
在這裏插入圖片描述
排序,就是使一串記錄,按照其中的某個或某些關鍵字的大小,遞增或遞減的排列起來的操作。排序算法,就是如何使得記錄按照要求排列的方法。

爲什麼排序算法在很多領域被相當地重視?

主要是一個優秀的算法可以節省大量的資源,尤其是在大量數據的處理方面。

排序算法可以分爲內部排序和外部排序:

  • 內部排序是數據記錄在內存中進行排序,
  • 而外部排序是因排序的數據很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。

經典排序算法主要有一下幾個(思維導圖):

點擊查看大圖!
在這裏插入圖片描述
主要的性質肯定少不了:

  • 數據對象

數據對象
數組或者鏈表,或者both。

  • 穩定性

穩定性
假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且 r[i]r[j] 之前,而在排序後的序列中,r[i] 仍在 r[j] 之前,則稱這種排序算法是 穩定 的;否則稱爲 不穩定 的。

  • 時間複雜度

時間複雜度
時間複雜性,又稱時間複雜度,是一個定性描述該算法的運行時間的函數。

  • 空間複雜度

空間複雜度
空間複雜度是對一個算法在運行過程中臨時佔用存儲空間大小的量度。

點擊查看大圖!
在這裏插入圖片描述
在這裏插入圖片描述

1、冒泡排序

描述:

重複地走訪要排序的元素列,依次比較兩個相鄰的元素,如果順序錯誤,就把他們交換。

動圖演示:
在這裏插入圖片描述

  • 什麼時候最快?
    當輸入的數據已經是正序時(???那還用排序?都已經是正序了,還要你何用)。
  • 什麼時候最慢?
    當輸入的數據是反序時(死心眼嘛?循環反序再輸出數據不就行了,還用你是閒的嗎)。
    在這裏插入圖片描述

代碼:

#python
def bubbleSort(arr):
    for i in range(1, len(arr)):
        for j in range(0, len(arr)-i):
            if arr[j] > arr[j+1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
    return arr
//C
void swap(int *a,int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}
void bubble_sort(int arr[], int len){
    int i, j, temp;
    for (i = 0; i < len - 1; i++)
        for (j = 0; j < len - 1 - i; j++)
            if (arr[j] > arr[j + 1]) {
            	swap(&arr[j], &arr[j + 1]);
            }
}
//C++
template<typename T>
void bubble_sort(T arr[], int len) {
    int i, j;
    for (i = 0; i < len - 1; i++)
        for (j = 0; j < len - 1 - i; j++)
            if (arr[j] > arr[j + 1])
                swap(arr[j], arr[j + 1]);
}

寫一個 Swap 交換和 flag 提前跳出,加上第一層倒序循環,是可以的。


參考MOOC浙大數據結構

// C
void Swap(ElementType *a, ElementType *b){
	ElementType t = *a; *a = *b; *b = t;
}
void BubbleSort(ElementType A[], int N){
	int P, i;
	int flag;
	for (P = N - 1; P >= 0; P--){
		flag = 0;
		for (i = 0; i < P; i++){
			if (A[i] > A[i + 1]){
				Swap(&A[i], &A[i + 1]);
				flag = 1;
			}
		}
		if (flag = 0) break;
	}
}

在這裏插入圖片描述

2、選擇排序

描述:

第一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,然後再從剩餘的未排序元素中尋找到最小(大)元素,然後放到已排序的序列的末尾。以此類推,直到全部待排序的數據元素的個數爲零。

動圖演示:
在這裏插入圖片描述
(其實就是打牌,抓牌,看牌,馬牌)
在這裏插入圖片描述

代碼:

#python
def selectionSort(arr):
    for i in range(len(arr) - 1):
        # 記錄最小數的索引
        minIndex = i
        for j in range(i + 1, len(arr)):
            if arr[j] < arr[minIndex]:
                minIndex = j
        # i 不是最小數時,將 i 和最小數進行交換
        if i != minIndex:
            arr[i], arr[minIndex] = arr[minIndex], arr[i]
    return arr
//C
void swap(int *a,int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}
void selection_sort(int arr[], int len){
    int i,j;
    for (i = 0 ; i < len - 1 ; i++){
        int min = i;
        //遍歷未排序的元素
        for (j = i + 1; j < len; j++){
            if (arr[j] < arr[min])    	//找到目前最小值
                min = j;    			//記錄最小值
        	swap(&arr[min], &arr[i]);   //做交換
        }
    }
}
//C++
template<typename T> 
void selection_sort(std::vector<T>& arr) {
    for (int i = 0; i < arr.size() - 1; i++) {
        int min = i;
        for (int j = i + 1; j < arr.size(); j++){
            if (arr[j] < arr[min])
                min = j;
        	std::swap(arr[i], arr[min]);
        }
    }
}

參考MOOC浙大數據結構

// C
void Swap(ElementType *a, ElementType *b){
	ElementType t = *a; *a = *b; *b = t;
}
void SimpleSelectionSort(ElementType A[], int N){
	int i, j, min;
	for (i = 0; i < N-1; i++){
		min = i;
		for (j = i + 1; j < N; j++){
			if (A[i] < A[min])
				min = j;
			Swap(&A[i], &A[min]);
		}
	}
}

在這裏插入圖片描述

3、插入排序

描述:

通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。

動圖演示:
在這裏插入圖片描述
(慢,真的慢,不是假的)
在這裏插入圖片描述
代碼:

#python
def insertionSort(arr):
    for i in range(len(arr)):
        preIndex = i-1
        current = arr[i]
        while preIndex >= 0 and arr[preIndex] > current:
            arr[preIndex+1] = arr[preIndex]
            preIndex-=1
        arr[preIndex+1] = current
    return arr
//C
void insertion_sort(int arr[], int len){
    int i,j,key;
    for (i=1;i<len;i++){
        key = arr[i];
        j=i-1;
        while((j>=0) && (arr[j]>key)) {
            arr[j+1] = arr[j];
            j--;
        }
        arr[j+1] = key;
    }
}
//C++
void insertion_sort(int arr[],int len){
    for(int i=1;i<len;i++){
        int key=arr[i];
        int j=i-1;
        while((j>=0) && (key<arr[j])){
            arr[j+1]=arr[j];
            j--;
        }
        arr[j+1]=key;
    }
}

參考MOOC浙大數據結構

void InsertionSort( ElementType A[], int N ){
     int P, i;
     ElementType Tmp;
      
     for ( P=1; P<N; P++ ) {
         Tmp = A[P];
         for ( i=P; i>0 && A[i-1]>Tmp; i-- )
             A[i] = A[i-1];
         A[i] = Tmp;
     }
}

在這裏插入圖片描述

4、希爾排序

描述:

把記錄按下標的一定增量分組,對每組使用直接插入排序算法排序;隨着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個文件恰被分成一組,算法便終止。

動圖演示:
在這裏插入圖片描述
希爾排序是基於插入排序進行改進的,(改完確實快了好多啊,以前是老黃牛,現在是小摩托了)
在這裏插入圖片描述

代碼:

#python
def shellSort(arr):
    import math
    gap=1
    while(gap < len(arr)/3):
        gap = gap*3+1
    while gap > 0:
        for i in range(gap,len(arr)):
            temp = arr[i]
            j = i-gap
            while j >=0 and arr[j] > temp:
                arr[j+gap]=arr[j]
                j-=gap
            arr[j+gap] = temp
        gap = math.floor(gap/3)
    return arr
//C
void shell_sort(int arr[], int len) {
    int gap, i, j;
    int temp;
    for (gap = len >> 1; gap > 0; gap >>= 1){
        for (i = gap; i < len; i++) {
            temp = arr[i];
            for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
                arr[j + gap] = arr[j];
            arr[j + gap] = temp;
        }
    }
}
//C++
template<typename T>
void shell_sort(T array[], int length) {
    int h = 1;
    while (h < length / 3) {
        h = 3 * h + 1;
    }
    while (h >= 1) {
        for (int i = h; i < length; i++) {
            for (int j = i; j >= h && array[j] < array[j - h]; j -= h) {
                std::swap(array[j], array[j - h]);
            }
        }
        h = h / 3;
    }
}

參考MOOC浙大數據結構

void ShellSort( ElementType A[], int N ){
     int Si, D, P, i;
     ElementType Tmp;
     /* 這裏只列出一小部分增量 */
     int Sedgewick[] = {929, 505, 209, 109, 41, 19, 5, 1, 0};
      
     for ( Si=0; Sedgewick[Si]>=N; Si++ ) 
         ;
     for ( D=Sedgewick[Si]; D>0; D=Sedgewick[++Si] ){
         for ( P=D; P<N; P++ ){
             Tmp = A[P];
             for ( i=P; i>=D && A[i-D]>Tmp; i-=D )
                 A[i] = A[i-D];
             A[i] = Tmp;
         }
     }
}

在這裏插入圖片描述

5、歸併排序

描述:

採用分而治之法的一個非常典型的應用,將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。

動圖演示:
在這裏插入圖片描述
就是化整爲零,分家,哈哈
在這裏插入圖片描述

代碼:

#python
def mergeSort(arr):
    import math
    if(len(arr)<2):
        return arr
    middle = math.floor(len(arr)/2)
    left, right = arr[0:middle], arr[middle:]
    return merge(mergeSort(left), mergeSort(right))

def merge(left,right):
    result = []
    while left and right:
        if left[0] <= right[0]:
            result.append(left.pop(0))
        else:
            result.append(right.pop(0));
    while left:
        result.append(left.pop(0))
    while right:
        result.append(right.pop(0));
    return result
//C
void merge_sort_recursive(int arr[], int reg[], int start, int end) {
    if (start >= end)
        return;
    int len = end - start, mid = (len >> 1) + start;
    int start1 = start, end1 = mid;
    int start2 = mid + 1, end2 = end;
    merge_sort_recursive(arr, reg, start1, end1);
    merge_sort_recursive(arr, reg, start2, end2);
    int k = start;
    while (start1 <= end1 && start2 <= end2)
        reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
    while (start1 <= end1)
        reg[k++] = arr[start1++];
    while (start2 <= end2)
        reg[k++] = arr[start2++];
    for (k = start; k <= end; k++)
        arr[k] = reg[k];
}
void merge_sort(int arr[], const int len) {
    int reg[len];
    merge_sort_recursive(arr, reg, 0, len - 1);
}
//C++
void Merge(vector<int> &Array, int front, int mid, int end) {
    // preconditions:
    // Array[front...mid] is sorted
    // Array[mid+1 ... end] is sorted
    // Copy Array[front ... mid] to LeftSubArray
    // Copy Array[mid+1 ... end] to RightSubArray
    vector<int> LeftSubArray(Array.begin() + front, Array.begin() + mid + 1);
    vector<int> RightSubArray(Array.begin() + mid + 1, Array.begin() + end + 1);
    int idxLeft = 0, idxRight = 0;
    LeftSubArray.insert(LeftSubArray.end(), numeric_limits<int>::max());
    RightSubArray.insert(RightSubArray.end(), numeric_limits<int>::max());
    // Pick min of LeftSubArray[idxLeft] and RightSubArray[idxRight], 
    // and put into Array[i]
    for (int i = front; i <= end; i++) {
        if (LeftSubArray[idxLeft] < RightSubArray[idxRight]) {
            Array[i] = LeftSubArray[idxLeft];
            idxLeft++;
        } else {
            Array[i] = RightSubArray[idxRight];
            idxRight++;
        }
    }
}
void MergeSort(vector<int> &Array, int front, int end) {
    if (front >= end)
        return;
    int mid = (front + end) / 2;
    MergeSort(Array, front, mid);
    MergeSort(Array, mid + 1, end);
    Merge(Array, front, mid, end);
}

參考MOOC浙大數據結構

//C
/* L = 左邊起始位置, R = 右邊起始位置, RightEnd = 右邊終點位置*/
void Merge( ElementType A[], ElementType TmpA[], int L, int R, int RightEnd ){
/* 將有序的A[L]~A[R-1]和A[R]~A[RightEnd]歸併成一個有序序列 */
     int LeftEnd, NumElements, Tmp;
     int i;
      
     LeftEnd = R - 1; /* 左邊終點位置 */
     Tmp = L;         /* 有序序列的起始位置 */
     NumElements = RightEnd - L + 1;
      
     while( L <= LeftEnd && R <= RightEnd ) {
         if ( A[L] <= A[R] )
             TmpA[Tmp++] = A[L++]; /* 將左邊元素複製到TmpA */
         else
             TmpA[Tmp++] = A[R++]; /* 將右邊元素複製到TmpA */
     }
 
     while( L <= LeftEnd )
         TmpA[Tmp++] = A[L++]; /* 直接複製左邊剩下的 */
     while( R <= RightEnd )
         TmpA[Tmp++] = A[R++]; /* 直接複製右邊剩下的 */
          
     for( i = 0; i < NumElements; i++, RightEnd -- )
         A[RightEnd] = TmpA[RightEnd]; /* 將有序的TmpA[]複製回A[] */
}
void Msort( ElementType A[], ElementType TmpA[], int L, int RightEnd ){
/* 核心遞歸排序函數 */ 
     int Center;
      
     if ( L < RightEnd ) {
          Center = (L+RightEnd) / 2;
          Msort( A, TmpA, L, Center );              /* 遞歸解決左邊 */ 
          Msort( A, TmpA, Center+1, RightEnd );     /* 遞歸解決右邊 */  
          Merge( A, TmpA, L, Center+1, RightEnd );  /* 合併兩段有序序列 */ 
     }
}
void MergeSort( ElementType A[], int N ){
/* 歸併排序 */
     ElementType *TmpA;
     TmpA = (ElementType *)malloc(N*sizeof(ElementType));
      
     if ( TmpA != NULL ) {
          Msort( A, TmpA, 0, N-1 );
          free( TmpA );
     }
     else printf( "空間不足" );
}

在這裏插入圖片描述

6、快速排序

描述:

通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。

動圖演示:
在這裏插入圖片描述
快速排序是目前感覺最厲害的。
在這裏插入圖片描述

代碼:

#python
def quickSort(arr, left=None, right=None):
    left = 0 if not isinstance(left,(int, float)) else left
    right = len(arr)-1 if not isinstance(right,(int, float)) else right
    if left < right:
        partitionIndex = partition(arr, left, right)
        quickSort(arr, left, partitionIndex-1)
        quickSort(arr, partitionIndex+1, right)
    return arr

def partition(arr, left, right):
    pivot = left
    index = pivot+1
    i = index
    while  i <= right:
        if arr[i] < arr[pivot]:
            swap(arr, i, index)
            index+=1
        i+=1
    swap(arr,pivot,index-1)
    return index-1

def swap(arr, i, j):
    arr[i], arr[j] = arr[j], arr[i]
//C
void swap(int *x, int *y) {
    int t = *x;
    *x = *y;
    *y = t;
}

void quick_sort_recursive(int arr[], int start, int end) {
    if (start >= end)
        return;
    int mid = arr[end];
    int left = start, right = end - 1;
    while (left < right) {
        while (arr[left] < mid && left < right)
            left++;
        while (arr[right] >= mid && left < right)
            right--;
        swap(&arr[left], &arr[right]);
    }
    if (arr[left] >= arr[end])
        swap(&arr[left], &arr[end]);
    else
        left++;
    if (left)
        quick_sort_recursive(arr, start, left - 1);
    quick_sort_recursive(arr, left + 1, end);
}

void quick_sort(int arr[], int len) {
    quick_sort_recursive(arr, 0, len - 1);
}
//C++
template <typename T>
void quick_sort_recursive(T arr[], int start, int end) {
    if (start >= end)
        return;
    T mid = arr[end];
    int left = start, right = end - 1;
    //在整個範圍內搜尋比樞紐元值小或大的元素,然後將左側元素與右側元素交換
    while (left < right) { 
    	//試圖在左側找到一個比樞紐元更大的元素
        while (arr[left] < mid && left < right)
            left++;
        //試圖在右側找到一個比樞紐元更小的元素
        while (arr[right] >= mid && left < right)
            right--;
        std::swap(arr[left], arr[right]); //交換元素
    }
    if (arr[left] >= arr[end])
        std::swap(arr[left], arr[end]);
    else
        left++;
    quick_sort_recursive(arr, start, left - 1);
    quick_sort_recursive(arr, left + 1, end);
}

template <typename T> 
void quick_sort(T arr[], int len) {
    quick_sort_recursive(arr, 0, len - 1);
}

參考MOOC浙大數據結構

//c
//判斷數組規模,如果太小,直接就插入排序
void swap(int *a,int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}

void InsertionSort( ElementType A[], int N ){
     int P, i;
     ElementType Tmp;
      
     for ( P=1; P<N; P++ ) {
         Tmp = A[P];
         for ( i=P; i>0 && A[i-1]>Tmp; i-- )
             A[i] = A[i-1];
         A[i] = Tmp;
     }
}

ElementType Median3(ElementType A[], int Left, int Right){
	int Center=(Left+Right)/2;
	if(A[Left]>A[center])
		Swap(&A[Left], &A[center]);
	if(A[Left]>A[Right])
		Swap(&A[Left], &A[Right]);
	if(A[center]>A[Right])
		Swap(&A[Center], &A[Right]);
	/*此時A[Left]c=A[center]c=A[Right]*/
	Swap(&A[center], &A[Right-1]);	/*將基準Pivot藏到右邊*/
	/*只需要考慮A[Left+1]…A[Right-2]*/
	return A[Right-1];	/*返回基準 Pivot*/
}

void Qsort(Elementrype A[], int Left, int Right){
	/*核心遞歸函數*/
	int pivot, Cutoff, Low, High;
	if(Cutoff<=Right-Left){	/*如果序列元素充分多,進入快排*/
		Pivot=Median3(A, Left, Right);	/*選基準*/
		Low=Left;High=Right-1;
		while(1){	/*將序列中比基準小的移到基準左邊,大的移到右邊*/
			while(A[++Low]<Pivot);
			while(A[--High]>pivot);
			if(Low<High) Swap(&A[Low], &A[High]);
			else break;
		}
		Swap(&A[Low], &A[Right-1]);	/*將基準換到正確的位置*/
		Qsort(A, Left, Low-1);	/*遞歸解決左邊*/
		Qsort(A, Low+1, Right);	/*遞歸解決右邊*/
	}
	else Insertionsort(A+Left, Right-Left+1);	/*元素太少,用簡單排序*/
}

void Quicksort(ElementType A[]int N){
	/*統一接口*/
	Qsort(A, 0, N-1);
}

在這裏插入圖片描述

7、堆排序

描述:

堆排序是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。

動圖演示:
在這裏插入圖片描述
堆排序是其實就和之前的堆一樣,但是我不太會。
在這裏插入圖片描述

代碼:

#python
def buildMaxHeap(arr):
    import math
    for i in range(math.floor(len(arr)/2),-1,-1):
        heapify(arr,i)

def heapify(arr, i):
    left = 2*i+1
    right = 2*i+2
    largest = i
    if left < arrLen and arr[left] > arr[largest]:
        largest = left
    if right < arrLen and arr[right] > arr[largest]:
        largest = right

    if largest != i:
        swap(arr, i, largest)
        heapify(arr, largest)

def swap(arr, i, j):
    arr[i], arr[j] = arr[j], arr[i]

def heapSort(arr):
    global arrLen
    arrLen = len(arr)
    buildMaxHeap(arr)
    for i in range(len(arr)-1,0,-1):
        swap(arr,0,i)
        arrLen -=1
        heapify(arr, 0)
    return arr
//C
void swap(int *a, int *b) {
    int temp = *b;
    *b = *a;
    *a = temp;
}

void max_heapify(int arr[], int start, int end) {
    // 建立父節點指標和子節點指標
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end) { // 若子節點指標在範圍內才做比較
        if (son + 1 <= end && arr[son] < arr[son + 1]) // 先比較兩個子節點大小,選擇最大的
            son++;
        if (arr[dad] > arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數
            return;
        else { // 否則交換父子內容再繼續子節點和孫節點比較
            swap(&arr[dad], &arr[son]);
            dad = son;
            son = dad * 2 + 1;
        }
    }
}

void heap_sort(int arr[], int len) {
    int i;
    // 初始化,i從最後一個父節點開始調整
    for (i = len / 2 - 1; i >= 0; i--)
        max_heapify(arr, i, len - 1);
    // 先將第一個元素和已排好元素前一位做交換,再重新調整,直到排序完畢
    for (i = len - 1; i > 0; i--) {
        swap(&arr[0], &arr[i]);
        max_heapify(arr, 0, i - 1);
    }
}
//C++
void max_heapify(int arr[], int start, int end) {
    // 建立父節點指標和子節點指標
    int dad = start;
    int son = dad * 2 + 1;
    while (son <= end) { // 若子節點指標在範圍內才做比較
        if (son + 1 <= end && arr[son] < arr[son + 1]) // 先比較兩個子節點大小,選擇最大的
            son++;
        if (arr[dad] > arr[son]) // 如果父節點大於子節點代表調整完畢,直接跳出函數
            return;
        else { // 否則交換父子內容再繼續子節點和孫節點比較
            swap(arr[dad], arr[son]);
            dad = son;
            son = dad * 2 + 1;
        }
    }
}

void heap_sort(int arr[], int len) {
    // 初始化,i從最後一個父節點開始調整
    for (int i = len / 2 - 1; i >= 0; i--)
        max_heapify(arr, i, len - 1);
    // 先將第一個元素和已經排好的元素前一位做交換,再從新調整(剛調整的元素之前的元素),直到排序完畢
    for (int i = len - 1; i > 0; i--) {
        swap(arr[0], arr[i]);
        max_heapify(arr, 0, i - 1);
    }
}

參考MOOC浙大數據結構

void Swap( ElementType *a, ElementType *b )
{
     ElementType t = *a; *a = *b; *b = t;
}
  
void PercDown( ElementType A[], int p, int N )
{ /* 改編代碼4.24的PercDown( MaxHeap H, int p )    */
  /* 將N個元素的數組中以A[p]爲根的子堆調整爲最大堆 */
    int Parent, Child;
    ElementType X;
 
    X = A[p]; /* 取出根結點存放的值 */
    for( Parent=p; (Parent*2+1)<N; Parent=Child ) {
        Child = Parent * 2 + 1;
        if( (Child!=N-1) && (A[Child]<A[Child+1]) )
            Child++;  /* Child指向左右子結點的較大者 */
        if( X >= A[Child] ) break; /* 找到了合適位置 */
        else  /* 下濾X */
            A[Parent] = A[Child];
    }
    A[Parent] = X;
}
 
void HeapSort( ElementType A[], int N ) 
{ /* 堆排序 */
     int i;
       
     for ( i=N/2-1; i>=0; i-- )/* 建立最大堆 */
         PercDown( A, i, N );
      
     for ( i=N-1; i>0; i-- ) {
         /* 刪除最大堆頂 */
         Swap( &A[0], &A[i] ); /* 見代碼7.1 */
         PercDown( A, 0, i );
     }
}

在這裏插入圖片描述

8、計數排序

描述:

計數排序不是基於比較的排序算法,其核心在於將輸入的數據值轉化爲鍵存儲在額外開闢的數組空間中。

動圖演示:
在這裏插入圖片描述
如果不是一定需要比較的例子,計數要快不知道多少,比如工齡統計。
在這裏插入圖片描述

代碼:

#python
def countingSort(arr, maxValue):
    bucketLen = maxValue+1
    bucket = [0]*bucketLen
    sortedIndex =0
    arrLen = len(arr)
    for i in range(arrLen):
        if not bucket[arr[i]]:
            bucket[arr[i]]=0
        bucket[arr[i]]+=1
    for j in range(bucketLen):
        while bucket[j]>0:
            arr[sortedIndex] = j
            sortedIndex+=1
            bucket[j]-=1
    return arr
//C
void print_arr(int *arr, int n) {
    int i;
    printf("%d", arr[0]);
    for (i = 1; i < n; i++)
        printf(" %d", arr[i]);
    printf("\n");
}

void counting_sort(int *ini_arr, int *sorted_arr, int n) {
    int *count_arr = (int *) malloc(sizeof(int) * 100);
    int i, j, k;
    for (k = 0; k < 100; k++)
        count_arr[k] = 0;
    for (i = 0; i < n; i++)
        count_arr[ini_arr[i]]++;
    for (k = 1; k < 100; k++)
        count_arr[k] += count_arr[k - 1];
    for (j = n; j > 0; j--)
        sorted_arr[--count_arr[ini_arr[j - 1]]] = ini_arr[j - 1];
    free(count_arr);
}

在這裏插入圖片描述

9、桶排序

描述:

桶排序是計數排序的升級版。它利用了函數的映射關係,高效與否的關鍵就在於這個映射函數的確定。

動圖演示:

在這裏插入圖片描述
看來計數排序是真的很好用,不然不會有plus!
在這裏插入圖片描述
代碼:

struct ListNode{
    explicit ListNode(int i=0):mData(i),mNext(NULL){}
    ListNode* mNext;
    int mData;
};

ListNode* insert(ListNode* head,int val){
    ListNode dummyNode;
    ListNode *newNode = new ListNode(val);
    ListNode *pre,*curr;
    dummyNode.mNext = head;
    pre = &dummyNode;
    curr = head;
    while(NULL!=curr && curr->mData<=val){
        pre = curr;
        curr = curr->mNext;
    }
    newNode->mNext = curr;
    pre->mNext = newNode;
    return dummyNode.mNext;
}

ListNode* Merge(ListNode *head1,ListNode *head2){
    ListNode dummyNode;
    ListNode *dummy = &dummyNode;
    while(NULL!=head1 && NULL!=head2){
        if(head1->mData <= head2->mData){
            dummy->mNext = head1;
            head1 = head1->mNext;
        }else{
            dummy->mNext = head2;
            head2 = head2->mNext;
        }
        dummy = dummy->mNext;
    }
    if(NULL!=head1) dummy->mNext = head1;
    if(NULL!=head2) dummy->mNext = head2;
   
    return dummyNode.mNext;
}

void BucketSort(int n,int arr[]){
    vector<ListNode*> buckets(BUCKET_NUM,(ListNode*)(0));
    for(int i=0;i<n;++i){
        int index = arr[i]/BUCKET_NUM;
        ListNode *head = buckets.at(index);
        buckets.at(index) = insert(head,arr[i]);
    }
    ListNode *head = buckets.at(0);
    for(int i=1;i<BUCKET_NUM;++i){
        head = Merge(head,buckets.at(i));
    }
    for(int i=0;i<n;++i){
        arr[i] = head->mData;
        head = head->mNext;
    }
}

10、基數排序

描述:

基數排序是按照低位先排序,然後收集;再按照高位排序,然後再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序。最後的次序就是高優先級高的在前,高優先級相同的低優先級高的在前。

動圖演示:
在這裏插入圖片描述
基數排序就像撲克分類!
在這裏插入圖片描述
是數字還是色號?

代碼:

//C
void print(int *a, int n) {
  int i;
  for (i = 0; i < n; i++) {
    printf("%d\t", a[i]);
  }
}

void radixsort(int *a, int n) {
  int i, b[MAX], m = a[0], exp = 1;
  for (i = 1; i < n; i++) {
    if (a[i] > m) {
      m = a[i];
    }
  }

  while (m / exp > 0) {
    int bucket[BASE] = { 0 };

    for (i = 0; i < n; i++) {
      bucket[(a[i] / exp) % BASE]++;
    }

    for (i = 1; i < BASE; i++) {
      bucket[i] += bucket[i - 1];
    }

    for (i = n - 1; i >= 0; i--) {
      b[--bucket[(a[i] / exp) % BASE]] = a[i];
    }

    for (i = 0; i < n; i++) {
      a[i] = b[i];
    }

    exp *= BASE;

#ifdef SHOWPASS
    printf("\nPASS   : ");
    print(a, n);
#endif
  }
}
//C++
//輔助函數,求數據的最大位數
int maxbit(int data[], int n) {
    int maxData = data[0];              ///< 最大數
    /// 先求出最大數,再求其位數,這樣有原先依次每個數判斷其位數,稍微優化點。
    for (int i = 1; i < n; ++i) {
        if (maxData < data[i])
            maxData = data[i];
    }
    int d = 1;
    int p = 10;
    while (maxData >= p) {
        //p *= 10; // Maybe overflow
        maxData /= 10;
        ++d;
    }
    return d;
}
//基數排序
void radixsort(int data[], int n) {
    int d = maxbit(data, n);
    int *tmp = new int[n];
    int *count = new int[10]; //計數器
    int i, j, k;
    int radix = 1;
    //進行d次排序
    for(i = 1; i <= d; i++) {
        for(j = 0; j < 10; j++)
            count[j] = 0; //每次分配前清空計數器
        for(j = 0; j < n; j++){
            k = (data[j] / radix) % 10; //統計每個桶中的記錄數
            count[k]++;
        }
        for(j = 1; j < 10; j++)
            count[j] = count[j - 1] + count[j]; //將tmp中的位置依次分配給每個桶
        for(j = n - 1; j >= 0; j--) { //將所有桶中記錄依次收集到tmp中
            k = (data[j] / radix) % 10;
            tmp[count[k] - 1] = data[j];
            count[k]--;
        }
        for(j = 0; j < n; j++) //將臨時數組的內容複製到data中
            data[j] = tmp[j];
        radix = radix * 10;
    }
    delete []tmp;
    delete []count;
}

參考MOOC浙大數據結構

//c
/* 基數排序 - 次位優先 */
 
/* 假設元素最多有MaxDigit個關鍵字,基數全是同樣的Radix */
#define MaxDigit 4
#define Radix 10
 
/* 桶元素結點 */
typedef struct Node *PtrToNode;
struct Node {
    int key;
    PtrToNode next;
};
 
/* 桶頭結點 */
struct HeadNode {
    PtrToNode head, tail;
};
typedef struct HeadNode Bucket[Radix];
  
int GetDigit ( int X, int D )
{ /* 默認次位D=1, 主位D<=MaxDigit */
    int d, i;
     
    for (i=1; i<=D; i++) {
        d = X % Radix;
        X /= Radix;
    }
    return d;
}
 
void LSDRadixSort( ElementType A[], int N )
{ /* 基數排序 - 次位優先 */
     int D, Di, i;
     Bucket B;
     PtrToNode tmp, p, List = NULL; 
      
     for (i=0; i<Radix; i++) /* 初始化每個桶爲空鏈表 */
         B[i].head = B[i].tail = NULL;
     for (i=0; i<N; i++) { /* 將原始序列逆序存入初始鏈表List */
         tmp = (PtrToNode)malloc(sizeof(struct Node));
         tmp->key = A[i];
         tmp->next = List;
         List = tmp;
     }
     /* 下面開始排序 */ 
     for (D=1; D<=MaxDigit; D++) { /* 對數據的每一位循環處理 */
         /* 下面是分配的過程 */
         p = List;
         while (p) {
             Di = GetDigit(p->key, D); /* 獲得當前元素的當前位數字 */
             /* 從List中摘除 */
             tmp = p; p = p->next;
             /* 插入B[Di]號桶尾 */
             tmp->next = NULL;
             if (B[Di].head == NULL)
                 B[Di].head = B[Di].tail = tmp;
             else {
                 B[Di].tail->next = tmp;
                 B[Di].tail = tmp;
             }
         }
         /* 下面是收集的過程 */
         List = NULL; 
         for (Di=Radix-1; Di>=0; Di--) { /* 將每個桶的元素順序收集入List */
             if (B[Di].head) { /* 如果桶不爲空 */
                 /* 整桶插入List表頭 */
                 B[Di].tail->next = List;
                 List = B[Di].head;
                 B[Di].head = B[Di].tail = NULL; /* 清空桶 */
             }
         }
     }
     /* 將List倒入A[]並釋放空間 */
     for (i=0; i<N; i++) {
        tmp = List;
        List = List->next;
        A[i] = tmp->key;
        free(tmp);
     } 
}

/* 基數排序 - 主位優先 */
 
/* 假設元素最多有MaxDigit個關鍵字,基數全是同樣的Radix */
 
#define MaxDigit 4
#define Radix 10
 
/* 桶元素結點 */
typedef struct Node *PtrToNode;
struct Node{
    int key;
    PtrToNode next;
};
 
/* 桶頭結點 */
struct HeadNode {
    PtrToNode head, tail;
};
typedef struct HeadNode Bucket[Radix];
  
int GetDigit ( int X, int D )
{ /* 默認次位D=1, 主位D<=MaxDigit */
    int d, i;
     
    for (i=1; i<=D; i++) {
        d = X%Radix;
        X /= Radix;
    }
    return d;
}
 
void MSD( ElementType A[], int L, int R, int D )
{ /* 核心遞歸函數: 對A[L]...A[R]的第D位數進行排序 */
     int Di, i, j;
     Bucket B;
     PtrToNode tmp, p, List = NULL; 
     if (D==0) return; /* 遞歸終止條件 */
      
     for (i=0; i<Radix; i++) /* 初始化每個桶爲空鏈表 */
         B[i].head = B[i].tail = NULL;
     for (i=L; i<=R; i++) { /* 將原始序列逆序存入初始鏈表List */
         tmp = (PtrToNode)malloc(sizeof(struct Node));
         tmp->key = A[i];
         tmp->next = List;
         List = tmp;
     }
     /* 下面是分配的過程 */
     p = List;
     while (p) {
         Di = GetDigit(p->key, D); /* 獲得當前元素的當前位數字 */
         /* 從List中摘除 */
         tmp = p; p = p->next;
         /* 插入B[Di]號桶 */
         if (B[Di].head == NULL) B[Di].tail = tmp;
         tmp->next = B[Di].head;
         B[Di].head = tmp;
     }
     /* 下面是收集的過程 */
     i = j = L; /* i, j記錄當前要處理的A[]的左右端下標 */
     for (Di=0; Di<Radix; Di++) { /* 對於每個桶 */
         if (B[Di].head) { /* 將非空的桶整桶倒入A[], 遞歸排序 */
             p = B[Di].head;
             while (p) {
                 tmp = p;
                 p = p->next;
                 A[j++] = tmp->key;
                 free(tmp);
             }
             /* 遞歸對該桶數據排序, 位數減1 */
             MSD(A, i, j-1, D-1);
             i = j; /* 爲下一個桶對應的A[]左端 */
         } 
     } 
}
 
void MSDRadixSort( ElementType A[], int N )
{ /* 統一接口 */
    MSD(A, 0, N-1, MaxDigit); 
}

在這裏插入圖片描述
我還是喜歡浙大的代碼風格!

參考

  • 百度百科
  • https://www.runoob.com/
  • https://github.com/hustcc/JS-Sorting-Algorithm
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章