1. 程式人生 > >常見排序演算法(描述與實現)

常見排序演算法(描述與實現)

常見排序演算法

效率比較

排序演算法效率比較

演算法實現

◎氣泡排序

氣泡排序(英語:Bubble Sort)是一種簡單的排序演算法。它重複地遍歷要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。遍歷數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。

氣泡排序演算法流程如下:

  • 比較相鄰的元素。如果第一個比第二個大(升序),就交換他們兩個。
  • 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。
  • 針對所有的元素重複以上的步驟,除了最後一個。
  • 持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。

程式碼實現

 void bubbleSort(int a[],int len)
 {
 	int i, j, temp;
	for (j=0; j<len; j++)
	{
		for (i=0; i<len-j; i++)
		{
			if (a[i] > a[i+1])
			{
				temp = a[i];
				a[i] = a[i+1];
				a[i+1] = temp;
			}
		}
	}
}

◎快速排序

快速排序(英語:Quicksort),又稱劃分交換排序(partition-exchange sort),快速排序是對氣泡排序的一種改進,由C. A. R. Hoare在1962年提出。

快速排序演算法流程如下:

  • 從數列中挑出一個元素,稱為"基準"(pivot)。
  • 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分割槽結束之後,該基準就處於數列的中間位置。這個稱為分割槽(partition)操作。
  • 遞迴地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。

程式碼實現

/*
* 函式引數:
* a[]: 待排序陣列
* start:陣列起始下標
* end:陣列末位下標
*/

void quickSort(int a[], int start, int end)
{
    if(start >= end)
    {
        return ;
    }
    int low = start;
    int high = end;
    int key = a[start];		// 基準

    while(low < high)
    {
        while(low < high && key <= a[high])
        {
            high--;
        }
        a[low] = a[high];

        while(low < high && key >= a[low])
        {
            low++;
        }
        a[high] = a[low];
    }

    a[low] = key;
    quickSort(a, start, low-1);
    quickSort(a, low+1, end);
}

◎插入排序

插入排序(英語:Insertion Sort)是一種簡單直觀的排序演算法。它的工作原理是通過構建有序序列,對於未排序資料,在已排序序列中從後向前掃描,找到相應位置並插入。插入排序在實現上,在從後向前掃描過程中,需要反覆把已排序元素逐步向後挪位,為最新元素提供插入空間。

程式碼實現

void InsertSort(int a[],int len)
{
    int i, j, temp;
    for (i=0; i<len; i++)
    {
        j = i-1;
        temp = a[i];
        while (j>=0 && temp<a[j])
        {
            a[j+1] = a[j];
            j--;
        }
        a[j+1] = temp;
    }
}

◎希爾排序

希爾排序(Shell Sort)是插入排序的一種。也稱縮小增量排序,是直接插入排序演算法的一種更高效的改進版本。希爾排序是非穩定排序演算法。該方法因DL.Shell於1959年提出而得名。

希爾排序演算法流程如下:

  • 希爾排序是把記錄按下標的一定增量分組,對每組使用直接插入排序演算法排序。
  • 隨著增量逐漸減小,每組包含的關鍵詞越來越多,當增量減至1時,整個檔案恰被分成一組,演算法便終止。

程式碼實現

void shellSort(int a[], int len)
{
	int i, j, temp;
	int gap=0;
	
    while (gap <= len)		// 生成初始增量
    {
        gap = 3*gap + 1;
    }
    while (gap >= 1)
    {
        for (i=gap; i<len; i++)
        {
            j = i-gap;
            temp = a[i];
            while (j>=0 && a[j]>temp)
            {
                a[j+gap] = a[j];
                j = j-gap;
            }
            a[j+gap] = temp;
        }
        gap = (gap-1) / 3;	// 遞減增量
    }
}

◎選擇排序

選擇排序(Selection sort)是一種簡單直觀的排序演算法。

選擇排序演算法流程如下:

  • 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  • 再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。
  • 以此類推,直到所有元素均排序完畢。

程式碼實現

void selectSort(int a[], int len)
{
    int i;         // 有序區的末尾位置
    int j;         // 無序區的起始位置
    int min;       // 無序區中最小元素位置
    int temp;

    for (i=0; i<len; i++)
    {
        min=i;

        for (j=i+1; j<len; j++)
        {
            if(a[j] < a[min])
                min=j;
        }

        if (min != i)
        {
        	temp = a[i];
        	a[i] = a[min];
        	a[min] = temp;
		}
    }
}

◎堆排序

堆排序(Heapsort)是指利用堆積樹(堆)這種資料結構所設計的一種排序演算法,它是選擇排序的一種。

堆排序演算法流程如下:

  • 將待排序的序列構造成一個大頂堆(或小頂堆)。
  • 此時,整個序列的最大值就是堆頂的根結點。將它移走(就是將其與堆陣列的末尾元素交換,此時末尾元素就是最大值)。
  • 然後將剩餘的n-1個序列重新構造成一個堆,這樣就會得到n個元素中的次大值。
  • 如此反覆執行,便能得到一個有序序列了。

程式碼實現

/*
* parentIndex:父結點下標
* leftIndex: 左孩子結點下標
*/

// 調整大根堆
void adjustHeap(int a[], int parentIndex, int len)
{
	int leftIndex, temp;
	temp = a[parentIndex];

	for (leftIndex=2*parentIndex; leftIndex<=len; leftIndex*=2)
	{
		if (leftIndex<len && a[leftIndex] < a[leftIndex+1])
		{
			leftIndex++;
		}
		if (temp > a[leftIndex])
		{
			break;
		}
		a[parentIndex] = a[leftIndex];
		parentIndex = leftIndex;
	}
	a[parentIndex] = temp;
}

// 交換元素
void swap(int arr[], int i, int j)
{
	int temp;
	temp = arr[i];
	arr[i] = arr[j];
	arr[j] = temp;
}

// 堆排序
void heapSort(int arr[],int len)
{
	int index;
	for (index=len/2 ; index>0; index--)
	{
		adjustHeap(arr, index, len);
	}
	for (index=len; index>1; index--)
	{
		// 交換堆頂元素與末尾元素
		swap(arr, 1, index);
		
		adjustHeap(arr, 1, index-1);
	}
}

◎歸併排序

歸併排序(MERGE-SORT)是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。

歸併排序演算法流程如下:

  • 將待排序陣列遞迴的分解至最小,分成許多個左右子序列。
  • 對每個子序列分別進行排序。
  • 將已有序的子序列合併排序,使子序列段間有序,得到完全有序的序列。

程式碼實現

#include <stdio.h>

// 合併陣列
void Merge(int sourceArr[],int tempArr[], int start, int mid, int end)
{
    int i = start, j=mid+1, k = start;
    while (i!=mid+1 && j!=end+1)
    {
        if (sourceArr[i] > sourceArr[j])
            tempArr[k++] = sourceArr[j++];
        else
            tempArr[k++] = sourceArr[i++];
    }

	// 追加剩餘元素
    while (i != mid+1)
        tempArr[k++] = sourceArr[i++];
    while (j != end+1)
        tempArr[k++] = sourceArr[j++];

    for (i=start; i<=end; i++)
        sourceArr[i] = tempArr[i];
}

// 歸併排序
void MergeSort(int sourceArr[], int tempArr[], int start, int end)
{
    int mid;
    if(start < end)
    {
        mid = start + (end-start) / 2;
        MergeSort(sourceArr, tempArr, start, mid);
        MergeSort(sourceArr, tempArr, mid+1, end);
        Merge(sourceArr, tempArr, start, mid, end);
    }
}

// 列印陣列
void printArray(int a[], int n)
{
	int i;
	for (i=0; i<n; i++)
	{
		printf("%3d", a[i]);
	}
	printf("\n");
}

void main()
{
	int sourceArr[9] = {44,33,11,22,66,88,77,55,99};
	int n = sizeof(sourceArr) / sizeof(int);
	int tempArr[9];

	printArray(sourceArr, n);
	MergeSort(sourceArr, tempArr, 0, n-1);
	printArray(tempArr, n);
}