排序算法總結(C語言版)


排序算法總結(C語言版)

1.    插入排序

1.1     直接插入排序

1.2     Shell排序

2.    交換排序

2.1     冒泡排序

2.2     快速排序

3.    選擇排序

3.1     直接選擇排序

3.2     堆排序

4.    歸並排序

4.1     二路歸並排序

4.2     自然合並排序

5.    分布排序

5.1     基數排序

1.插入排序

1.1      直接插入排序

將已排好序的部分num[0]~num[i]后的一個元素num[i+1]插入到之前已排好序的部分中去。

代碼:

/* 
 * 直接插入排序,由小到大
 */
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
# define NUM 10

void InsertSort(int num[],int n)
{
	int i, j;
	int temp;

	for (i = 1; i < n; i++)
	{
		temp = num[i];
		for (j = i - 1; j >= 0 && num[j] > temp; j--)
		{
			num[j + 1] = num[j];
		}
		num[j + 1] = temp;
	}
}
void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	InsertSort(num, NUM);

	for (i = 0; i < NUM; i++)
		printf("%-3d", num[i]);
}

1.2      Shell排序

先將整個待排記錄序列分隔成若干個子序列分別進行直接插入排序,待整個序列中的記錄基本有序時,再對全體記錄進行一次直接插入排序。

子序列選擇:將相隔某個增量的記錄組成一個序列。

“增量序列”選擇注意事項:應使增量序列中的值沒有除1之外的公因子,並且最后一個增量值必須為1

代碼:

/*
 * 希爾排序,由小到大
 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define NUM 10

void ShellSort(int num[], int n)
{
	int i, j, k;
	int temp;
	int inc;

	//增量序列為{5,2,1}
	for (inc = 5; inc >= 1; inc /= 2)
	{
		//如果增量為inc,則需要分成inc組進行直接插入排序
		for (k = 0; k < inc; k++)
		{
			for (i = k + inc; i < n; i += inc)
			{
				temp = num[i];
				for (j = i - inc; j >= 0 && num[j] > temp; j -= inc)
				{
					num[j + inc] = num[j];
				}
				num[j + inc] = temp;
			}
		}
	}
}

void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	ShellSort(num, NUM);

	for (i = 0; i < NUM; i++)
		printf("%-3d ", num[i]);
}

2.交換排序

2.1      冒泡排序

代碼:

/*
 * 冒泡排序,由小到大
 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define NUM 10

void BubbleSort(int num[], int n)
{
	int i, j;
	int temp;

	for (i = 0; i < n - 1; i++)
	{
		for (j = 0; j < n - i - 1; j++)
		{
			if (num[j] > num[j + 1])
			{
				temp = num[j];
				num[j] = num[j + 1];
				num[j + 1] = temp;
			}
		}
	}
}
void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	BubbleSort(num, NUM);

	for (i = 0; i < NUM; i++)
		printf("%-3d ", num[i]);
}

2.2      快速排序

快速排序(Quick Sort)是對冒泡排序的一種改進。基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以達到整個序列有序。

設要排序的數組是A[0]……A[N-1],首先任意選取一個數據(通常選用數組的第一個數)作為樞軸,然后將所有比它小的數都放到它前面,所有比它大的數都放到它后面,這個過程稱為一趟快速排序。

代碼1

/*
 * 快速排序,由小到大。
 * 利用快速排序的基本思想:樞軸左邊的所有元素均不大於樞軸右邊的所有元素。
 * 常規的做法是同時從后往前和從前往后遍歷,直至head和tail指向同一個元素。
 * 此處的做法是只從前往后遍歷,將比樞軸元素小的元素移動到樞軸之前。
 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define NUM 10

void QuickSort(int num[], int head, int tail)
{
	int temp;
	int pivot = head;
	int i,j;

	//一次划分,從head+1開始,將比樞軸元素小的元素移動到樞軸之前。
	for (i = head + 1; i <= tail; i++)
	{
		if (num[i] < num[pivot])
		{
			temp = num[i];
			for (j = i - 1; j >= pivot; j--)
			{
				num[j + 1] = num[j];
			}
			num[pivot++] = temp;
		}
	}

	//一次划分之后,將分成的兩個序列分別進行快速排序。
	if (head != pivot && head != pivot - 1)
	{
		QuickSort(num, head, pivot - 1);
	}
	if (pivot != tail && pivot + 1 != tail)
	{
		QuickSort(num, pivot + 1, tail);
	}
}

void sort(int num[], int n)
{
	QuickSort(num, 0, n - 1);
}

void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	sort(num, NUM);

	for (i = 0; i < NUM; i++)
		printf("%-3d ", num[i]);
}

代碼2

/*
 * 快速排序,由小到大。
 * 常規的做法:同時從后往前和從前往后遍歷,直至head和tail指向同一個元素。
 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define NUM 10

/* 
 * 一次划分函數。
 * 函數功能:將序列num[head]~num[tail]利用num[head]作為樞軸,分成兩個子序列。
 * 且左邊序列所有元素的值不大於右邊序列所有元素的值。
 * 返回值:函數返回樞軸的位置。
 */
int Partition(int num[], int head, int tail)
{
	int temp = num[head];

	while (head != tail)
	{
		while (num[tail] >= temp && head != tail)
		{
			tail--;
		}
		num[head] = num[tail];
		while (num[head] <= temp && head != tail)
		{
			head++;
		}
		num[tail] = num[head];
	}
	num[head] = temp;

	return head;
}

void QuickSort(int num[], int head, int tail)
{
	int pivot;

	pivot = Partition(num, head, tail);
	if (head != pivot&&head != pivot - 1)
	{
		QuickSort(num, head, pivot - 1);
	}
	if (pivot != tail&&pivot + 1 != tail)
	{
		QuickSort(num, pivot + 1, tail);
	}
}

void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	QuickSort(num, 0, NUM - 1);

	for (i = 0; i < NUM; i++)
		printf("%-3d ", num[i]);
}

3.選擇排序

3.1      直接選擇排序

代碼:

/* 
 * 直接選擇排序,由小到大
 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define NUM 10

void SelectSort(int num[], int n)
{
	int i, j;
	int temp;
	
	for (i = 0; i < n - 1; i++)
	{
		for (j = i + 1; j < n; j++)
		{
			if (num[i] > num[j])
			{
				temp = num[i];
				num[i] = num[j];
				num[j] = temp;
			}
		}
	}
}

void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	SelectSort(num, NUM);

	for (i = 0; i < NUM; i++)
		printf("%-3d", num[i]);
}

3.2      堆排序

堆:n個元素序列{k1,k2...ki...kn},當且僅當滿足下列關系時稱之為堆:

(ki <= k2i,ki <= k2i+1)或者(ki >= k2i,ki >= k2i+1), (i = 1,2,3,4...n/2)

若將和此次序列對應的一維數組(即以一維數組作此序列的存儲結構)看成是一個完全二叉樹,則堆的含義表明,完全二叉樹中所有非終端結點的值均不大於(或不小於)其左、右孩子結點的值。由此,若序列{k1,k2,…,kn}是堆,則堆頂元素(或完全二叉樹的根)必為序列中n個元素的最小值(或最大值)。

若在輸出堆頂的最小值之后,使得剩余n-1個元素的序列重建一個堆,則得到n個元素中的次小值。如此反復執行,便能得到一個有序序列,這個過程稱之為堆排序。

問題:

1.      如何由一個無序序列建成一個堆?

2.      如何在輸出堆頂元素之后,調整剩余元素成為一個新的堆?

代碼:

/*
 * 堆排序,由小到大
 * 由小到大排序時,建立的堆為大頂堆;
 * 由大到小排序時,建立的堆為小頂堆。
 */
# define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define NUM 10

/*
 * 初始條件:num[root]~num[end]中除了num[root]外均滿足堆的定義。
 * 函數功能:調整num[root],使得num[root]~num[end]為大頂堆。
 */
void HeapAdjust(int num[],int root,int end)
{
	int i;
	int temp;
	for (i = root * 2 + 1; i <= end; i = 2 * i + 1)
	{
		if (i + 1 <= end && num[i + 1] > num[i])
		{
			i++;	//i指向左右子結點中的大者
		}
		if (num[root] >= num[i])
		{
			break;
		}
		else
		{
			temp = num[root];
			num[root] = num[i];
			num[i] = temp;
			root = i;
		}
	}
}

void HeapSort(int num[], int n)
{
	int i;
	int temp;

	//將原始無序序列調整為堆。最后一個結點的雙親結點為最后一個非終端結點。故從i=(n-2)/2開始建堆。
	for (i = (n - 2) / 2; i >= 0; i--)
	{
		HeapAdjust(num, i, n - 1);
	}

	//將堆頂元素(未排序中的最大值)和未排序的最后一個元素交換位置;之后重新建堆。
	for (i = n - 1; i >= 1; i--)
	{
		temp = num[i];
		num[i] = num[0];
		num[0] = temp;
		HeapAdjust(num, 0, i - 1);
	}
}

void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	HeapSort(num, NUM);

	for (i = 0; i < NUM; i++)
		printf("%-3d ", num[i]);
}

4.歸並排序

4.1二路歸並排序

歸並:將兩個(或兩個以上)有序表合並成一個新的有序表。

假設初始序列含有n個記錄,則可以看成是n個有序的子序列,每個子序列的長度為1,然而兩兩歸並,得到[n/2]([x]表示不小於x的最小整數)個長度為21的有序子序列;再兩兩歸並,.....,如此重復,直至得到一個長度為n的有序序列為止。這種排序算法稱為2路歸並排序。

代碼1(遞歸形式的歸並排序)

/* 
 * 歸並排序,由小到大
 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <malloc.h>
#define NUM 10

/*
 * 函數功能:將已排好序的序列sourceArr[head~mid]和sourceArr[mid+1~tail]歸並在一起,
 * 使整個序列有序。最終的有序序列存儲在數組targetArr中。
 */
void Merge(int sourceArr[], int targetArr[], int head, int mid, int tail)
{
	int i, j, k;
	for (i = head,j = mid + 1,k = head; i <= mid && j <= tail; k++)
	{
		if (sourceArr[i] <= sourceArr[j])
		{
			targetArr[k] = sourceArr[i++];
		}
		else
		{
			targetArr[k] = sourceArr[j++];
		}
	}

	if (i <= mid)
	{
		for (; k <= tail; k++)
		{
			targetArr[k] = sourceArr[i++];
		}
	}

	if (j <= tail)
	{
		for (; k <= tail; k++)
		{
			targetArr[k] = sourceArr[j++];
		}
	}
}

/*
 * 歸並排序(遞歸):
 * 如果head!=tail,則對sourceArr[head~mid]和sourceArr[mid+1~tail]分別進行歸並排序,
 * 並將排序后的兩個序列歸並在一起,使整體有序。
 */
void MergeSort(int sourceArr[], int targetArr[], int head, int tail)
{
	int mid;
	int *tempArr;

	tempArr = (int *)malloc(sizeof(int)*(tail - head + 1));
	if (head == tail)
	{
		targetArr[head] = sourceArr[head];
	}
	else
	{
		mid = (head + tail) / 2;
		MergeSort(sourceArr, tempArr, head, mid);
		MergeSort(sourceArr, tempArr, mid + 1, tail);
		Merge(tempArr, targetArr, head, mid, tail);
	}
}

void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	MergeSort(num, num, 0, NUM-1);

	for (i = 0; i < NUM; i++)
		printf("%-3d ", num[i]);
}

代碼2(非遞歸形式的歸並排序)

/*
 * 歸並排序(非遞歸),由小到大
 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <malloc.h>
#define NUM 10

/*
 * 將2路有序序列歸並為1路。形參中只有sourceArr,沒有targetArr。
 */
void Merge(int sourceArr[], int head, int mid, int tail)
{
	int i, j, k;
	int *targetArr;

	targetArr = (int *)malloc(sizeof(int)*(tail - head + 1));

	for (i = head, j = mid + 1, k = 0; i <= mid&&j <= tail; k++)
	{
		if (sourceArr[i] > sourceArr[j])
		{
			targetArr[k] = sourceArr[j++];
		}
		else
		{
			targetArr[k] = sourceArr[i++];
		}
	}

	if (i <= mid)
	{
		for (; k < tail - head + 1; k++)
		{
			targetArr[k] = sourceArr[i++];
		}
	}

	if (j <= tail)
	{
		for (; k < tail - head + 1; k++)
		{
			targetArr[k] = sourceArr[j++];
		}
	}

	for (i = head,k = 0; i <= tail; i++,k++)
	{
		sourceArr[i] = targetArr[k];
	}
}

void MergeSort(int num[],int n)
{
	int interval;
	int head;

	for (interval = 2; interval <= n; interval *= 2)
	{
		for (head = 0; head + interval <= n; head += interval)
		{
			Merge(num, head, head + interval / 2 - 1, head + interval - 1);
		}
		Merge(num, head, head + interval / 2 - 1, n - 1);//處理head + interval > n的情況
	}
	Merge(num, 0, interval / 2 - 1, n - 1);//處理interval > n的情況
}

void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	MergeSort(num, NUM);

	for (i = 0; i < NUM; i++)
		printf("%-3d ", num[i]);
}

4.2 自然合並排序

自然合並排序是歸並排序算法的一種改進。

自然合並排序:對於初始給定的數組,通常存在多個長度大於1的已自然排好序的子數組段。例如,若數組a中元素為{48371562},則自然排好序的子數組段有{48}{37}{156}{2}。用一次對數組a的線性掃描就足以找出所有這些排好序的子數組段。然后將相鄰的排好序的子數組段兩兩合並,構成更大的排好序的子數組段({3478}{1256})。繼續合並相鄰排好序的子數組段,直至整個數組已排好序。

代碼:

/*
 * 自然合並排序,由小到大
 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <malloc.h>
#define NUM 10

int heads[NUM];

/*
 * 掃描數組,將已排好序的子數組段的段首加入數組heads中。
 * 函數返回段首的總數。
 */
int check(int num[], int n)
{
	int num_of_head = 1;
	int i;

	heads[0] = 0;
	for (i = 1; i < n; i++)
	{
		if (num[i] < num[i - 1])
		{
			heads[num_of_head++] = i;
		}
	}
	return num_of_head;
}

/*
 * 將2路有序序列歸並為1路。
 */
void Merge(int sourceArr[], int head, int mid, int tail)
{
	int i, j, k;
	int *targetArr;

	targetArr = (int *)malloc(sizeof(int)*(tail - head + 1));
	for (i = head, j = mid + 1, k = 0; i <= mid&&j <= tail; k++)
	{
		if (sourceArr[i] > sourceArr[j])
		{
			targetArr[k] = sourceArr[j++];
		}
		else
		{
			targetArr[k] = sourceArr[i++];
		}
	}

	for (; i <= mid; k++, i++)
	{
		targetArr[k] = sourceArr[i];
	}
	
	for (; j <= tail; k++, j++)
	{
		targetArr[k] = sourceArr[j];
	}
	
	for (i = head,k = 0; i <= tail; i++, k++)
	{
		sourceArr[i] = targetArr[k];
	}
}

void MergeSort(int num[], int n)
{
	int num_of_head;
	int i;

	while (1)
	{
		num_of_head = check(num, n);
		if (num_of_head == 1)
		{
			break;
		}
		else
		{
			for (i = 0; i < num_of_head - 1; i += 2)
			{
				if (i + 2 <= num_of_head - 1)
				{
					Merge(num, heads[i], heads[i + 1] - 1, heads[i + 2] - 1);
				}
				else
				{
					Merge(num, heads[i], heads[i + 1] - 1, n - 1);
				}	
			}
		}
	}
}

void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	MergeSort(num, NUM);

	for (i = 0; i < NUM; i++)
		printf("%-3d ", num[i]);
}

5.分布排序

5.1 基數排序

第一步

LSD為例,假設原來有一串數值如下所示:

73, 22, 93, 43, 55, 14, 28, 65, 39, 81

首先根據個位數的數值,在走訪數值時將它們分配至編號09的桶子中:

0

 

1

81

2

22

3

73   93   43

4

14

5

55   65

6

 

7

 

8

28

9

39

第二步

接下來將這些桶子中的數值重新串接起來,成為以下的數列:

81, 22, 73, 93, 43, 14, 55, 65, 28, 39

接着再進行一次分配,這次是根據十位數來分配:

0

 

1

14

2

22   28

3

39

4

43

5

55

6

65

7

73

8

81

9

93

第三步

接下來將這些桶子中的數值重新串接起來,成為以下的數列:

14, 22, 28, 39, 43, 55, 65, 73, 81, 93

這時候整個數列已經排序完畢;如果排序的對象有三位數以上,則持續進行以上的動作直至最高位數為止。

LSD的基數排序適用於位數小的數列,如果位數多的話,使用MSD的效率會比較好。MSD的方式與LSD相反,是由高位數為基底開始進行分配,但在分配之后並不馬上合並回一個數組中,而是在每個桶子中建立子桶,將每個桶子中的數值按照下一數位的值分配到子桶中。在進行完最低位數的分配后再合並回單一的數組中。

代碼1(LSD)

/*
 * 基數排序,由小到大。
 * 假設待排序數最多只有3位數。
 * 最低位優先基數排序(LSD)
 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <malloc.h>
#include <string.h>

#define NUM 10

/*
 * 用於獲取整數num的個位、十位、或者百位
 */
int getdigit(int num, int power)
{
	int result;

	switch (power)
	{
	case 1:result = num % 10; break;		//個位
	case 2:result = num % 100 / 10; break;	//十位
	case 3:result = num / 100; break;		//百位
	}
	return result;
}

void RadixSort(int num[], int n)
{
	int count[10] = {0};
	int *bucket;
	int i;
	int key;
	int digit;

	bucket = (int *)malloc(sizeof(int) * n);

	//key從1到3表示,依次比較個位、十位、百位。
	for (key = 1; key <= 3; key++)
	{
		//key=1時,count[0~9]分別表示個位數為0,1...,9的元素的數量
		for (i = 0; i < n; i++)
		{
			digit = getdigit(num[i], key);
			count[digit]++;
		}

		//key=1時,count[i]表示個位數為0~i的元素的總數量
		for (i = 1; i < 10; i++)
		{
			count[i] = count[i] + count[i - 1];
		}

		//從后往前遍歷序列,將元素放入對應的子桶中。從后往前是為了保證穩定性。
		for (i = n - 1; i >= 0; i--)
		{
			digit = getdigit(num[i], key);
			bucket[--count[digit]] = num[i];
		}

		for (i = 0; i < n; i++)
		{
			num[i] = bucket[i];
		}

		//count數組清零。
		memset(count, 0, sizeof(int) * 10);
	}
}

void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	RadixSort(num, NUM);

	for (i = 0; i < NUM; i++)
		printf("%-3d ", num[i]);
}

代碼2(MSD)

/*
 * 基數排序,由小到大。
 * 假設元素最高位3位數。
 * 最高位優先排序(MSD)。
 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <malloc.h>
#include <string.h>

#define NUM 10

int getdigit(int num, int power)
{
	int result;

	switch (power)
	{
	case 1:result = num % 10; break;
	case 2:result = num % 100 / 10; break;
	case 3:result = num / 100; break;
	}

	return result;
}

void RadixSort(int num[], int head,int tail,int key)
{
	int i,j;
	int digit;
	int *bucket;
	int count[10] = {0};
	int count_backup[10];
	int left, right;

	bucket = (int*)malloc(sizeof(int)*(tail - head + 1));

	for (i = head; i <= tail; i++)
	{
		digit = getdigit(num[i], key);
		count[digit]++;
	}

	for (i = 1; i < 10; i++)
	{
		count[i] = count[i] + count[i - 1];
	}

	memcpy(count_backup, count, sizeof(int) * 10);	//備份count數組中的數據

	for (i = tail; i >= head; i--)
	{
		digit = getdigit(num[i], key);
		bucket[--count[digit]] = num[i];
	}

	for (i = head, j = 0; i <= tail; i++, j++)
	{
		num[i] = bucket[j];
	}

	free(bucket);

	if(key != 1)
	{
		//對每個子桶中的數據分別按下一位進行基數排序。
		key--;
		if (count_backup[0] != 0 && count_backup[0] != 1)
		{
			RadixSort(num, head, head + count_backup[0] - 1, key);
		}
		for (i = 0; i <= 8; i++)
		{
			left = head + count[i];
			right = head + count[i + 1] - 1;

			if (left < right)
			{
				RadixSort(num, left, right, key);
			}
		}
	}
}

void main()
{
	int num[NUM];
	int i;

	for (i = 0; i < NUM; i++)
		scanf("%d", num + i);

	RadixSort(num, 0, NUM - 1, 3);

	for (i = 0; i < NUM; i++)
		printf("%-3d ", num[i]);
}

 

pdf版下載:http://pan.baidu.com/s/1bn1XL3T

工程文件打包下載:http://pan.baidu.com/s/1pJqegyf (開發環境為visual studio 2013)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM