1. 程式人生 > >資料結構——排序(C語言)

資料結構——排序(C語言)

在資料結構中我們常見的排序演算法有:直接插入排序、希爾排序、選擇排序、堆排序、交換排序(氣泡排序)、快速排序、歸併排序,接下來我給大家分享一下我在寫這些程式碼時的想法(從小到大,從左到右),以及各個排序的比較

首先我們得寫一個交換函式,因為後面基本每個排序都有使用。

void Swap(int *x, int *y)//交換函式
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

一、直接插入排序

1、思路

插入排序顧名思義就是把資料一個一個插入到有序序列中。

2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }

第一次插入【21】25 49 25* 16 8

第二次插入【21 25】49 25* 16 8

第三次插入【21 25 49】25* 16 8

第四次插入【21 25 25* 49】16 8

第五次插入【16 21 25 25* 49】8

第六次插入【8 16 21 25 25* 49】

3、原始碼

void InsertSort(int *a, int n)//插入排序
{
	assert(a);
	for (int i = 1; i < n; i++)
	{
		int j = i - 1;
		while (j >= 0)
		{
			if (a[j + 1] < a[j])
			{
				Swap(&a[j], &a[j + 1]);
				j--;
			}
			else
				break;
		}
	}
}

二、希爾排序

1、思路

希爾排序其實就是插入排序的優化,因為當原陣列時倒序時,每進行一次插入都要將前面的元素向後移動,所以我們可以進行預排序將陣列每相隔gap個數進行排序。

2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }

gap=3:21 16 8 25* 25 49

gap=1:8 16 21 25* 25 49

3、原始碼

void ShellSort(int *a, int n)//希爾排序
{
	assert(a);
	for (int gap = n / 2; gap > 0; gap /= 2)//gap為相隔個數當相隔為1時就是直接插入排序
	{
		for (int i = gap; i < n; i++)
		{
			int j = i;
			while (j - gap >= 0)
			{
				if (a[j] < a[j - gap])
					Swap(&a[j], &a[j - gap]);
				j -= gap;
			}
		}
	}
}

三、選擇排序

1、思路

選擇排序也顧名思義,就是選出來一個最大和最小的的元素,一個一個排下去。

2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }

第一次排序8 【25 21 25*16】49

第二次排序8 16【21 25*】25 49

第三次排序8 16 21 25* 25 49

3、原始碼

void SelectSort(int *a, int n)//選擇排序
{
	assert(a);
	int min = 0, max = 0, left = 0, right = n-1;
	while (left<=right)
	{
		for (int i = left; i<=right;i++)//選出最大數和最小數
		{
			if (a[min] > a[i])
				min = i;
			if (a[max] < a[i])
				max = i;
		}
		Swap(&a[min], &a[left]);
		if (max == left)//當最大數在左邊時,上一次的交換以將最大數換走,所以改變最大數下標
			max = min;
		Swap(&a[max], &a[right]);
		left++;
		right--;
	}
}

四、堆排序

1、思路

將一位陣列按完全二叉樹的順序儲存方式儲存,而且父親結點元素值大於它的孩子結點值,升序建大堆,降序建小堆。

2、例:陣列 { 21,

                 25,    49,

               25*, 16, 8 }

第一次向上調整21 25 49 25* 16 8

第二次向上調整49 25 21 25* 16 8

第一次向下調整49 25 21 25* 16 8交換後8 25 21 25* 16【49】

第二次向下調整25 25* 21 8 16【49】交換後16 25* 21 8【25 49】

第三次向下調整25* 16 21 8【25 49】交換後8 16 21【25* 25 49】

第四次向下調整21 16 8【25* 25 49】交換後8 16【21 25* 25 49】

第五次向下調整16 8【21 25* 25 49】交換後8【16 21 25* 25 49】

3、原始碼

void Heap(int *a, int root)
{
	assert(a);
	
	for (int parent = (root - 1) / 2; parent >= 0;parent--)
	{
		int child = 2 * parent + 1;
		if (child != root&&a[child] < a[child + 1])//選出孩子中最大元素
			++child;
		if (a[parent] < a[child])
			Swap(&a[parent], &a[child]);
	}
}
void HeapSort(int *a, int n)//堆排序,建大堆
{
	assert(a);
	Heap(a, n);//向上調堆
	while (n>1)
	{
		for (int parent = 0; parent <= (n - 1) / 2; parent++)//向下調堆
		{
			int child = parent * 2 + 1;
			if (child < (n - 1) && a[child] < a[child + 1])
				++child;
			if (child < n&&a[parent] < a[child])
				Swap(&a[parent], &a[child]);
		}
		Swap(&a[0], &a[n - 1]);//將最後一個元素和開始元素奇交換
		n--;
	}
}

五、交換排序(氣泡排序)

1、思路

每次交換將最大的值放置右端,然後從右向左排下去。

2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }

第一次交換21 25 25* 16 8【49】

第二次交換21 25 16 8【25* 49】

第三次交換21 16 8【25 25* 49】

第四次交換16 8【21 25 25* 49】

第五次交換8【16 21 25 25* 49】

第六次交換【8 16 21 25 25* 49】

3、原始碼

void BubbleSort(int *a, int n)//氣泡排序
{
	assert(a);
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j + 1 < n - i; j++)
		{
			if (a[j]>a[j + 1])
				Swap(&a[j], &a[j + 1]);
		}
	}
}

六、快速排序

1、思路

在陣列中選出一個標記值,比它大的排右邊,比他小的排左邊,排完後的位置就是有序後的位置。

2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }

第一次快速排序8【25 49 25* 16 21】

第二次快速排序8【【16】21【25* 49 25】】

第三次快速排序8【【16】21【【25*】 25 【49】】】

3、原始碼

int Quick(int *a, int left, int right)
{
	assert(a);
	while (left < right)
	{
		for (; left<right; left++)//找到大的進行交換
		{
			if (a[left] > a[right])
			{
				Swap(&a[left], &a[right]);
				right--;
				break;
			}
		}
		for (; left < right; right--)//找到小的進行交換
		{
			if (a[right] < a[left])
			{
				Swap(&a[left], &a[right]);
				left++;
				break;
			}
		}
	}
	return right;//返回標記值的有序下標
}

void QuickSort(int *a, int left, int right)//快速排序
{
	assert(a);
	if (left < right)
	{
		int div = Quick(a, left, right);//區分左右區間
		QuickSort(a, left, div - 1);//處理比標記值小的
		QuickSort(a, div + 1, right);//處理比標記值大的
	}
}

七、歸併排序

1、思路

將陣列每次平均劃分下去,最後有序進行歸併。

2、例:陣列a[] = { 21, 25, 49, 25*, 16, 8 }

第一次劃分【21 25 49】【25* 16 8】

第二次劃分【【21】【25 49】】【【25*】【16 8】】

第三次劃分【【21】【【25】【49】】】【【25*】【【16】【8】】】

第一次歸併【【21】【25 49】】【【25*】【8 16】】

第二次歸併【21 25 49】【8 16 25*】

第三次歸併8 16 21 25 25* 49

3、原始碼

void Merge(int *a, int left, int div, int right)
{
	assert(a);
	int llen = div - left + 1;
	int rlen = right - div;
	int i = 0, j = 0, x = 0;
	int *l, *r;
	l = (int*)malloc(sizeof(int)*llen);
	r = (int*)malloc(sizeof(int)*rlen);
	for (i = 0; i < llen; i++)
		l[i] = a[left + i];
	for (i = 0; i < rlen; i++)
		r[i] = a[div + i + 1];
	i = 0;
	j = 0;
	while (i < llen && j < rlen)
	{
		if (l[i] <= r[j])
			a[left + x++] = l[i++];
		else
			a[left + x++] = r[j++];
	}
	while (i < llen)
		a[left + x++] = l[i++];
	while (j < rlen)
		a[left + x++] = r[j++];
}

void MergeSort(int *a, int left, int right)//歸併排序
{
	assert(a);
	int div = left + ((right - left) >> 1);
	if (left < right)
	{
		MergeSort(a, left, div);
		MergeSort(a, div + 1, right);
		Merge(a, left, div, right);
	}
}

八、測試程式碼

int main()
{
	int a[] = { 21, 25, 49, 25, 16, 8 };
	//int a[] = { 9, 17, 65, 23, 45, 78, 87, 53, 31 };
	//InsertSort(a, sizeof(a) / sizeof(a[0]));
	//ShellSort(a, sizeof(a) / sizeof(a[0]));
	//SelectSort(a, sizeof(a) / sizeof(a[0]));
	//HeapSort(a, sizeof(a) / sizeof(a[0]));
	//BubbleSort(a, sizeof(a) / sizeof(a[0]));
	//QuickSort(a, 0, sizeof(a) / sizeof(a[0]) - 1);
	MergeSort(a, 0, sizeof(a) / sizeof(a[0]) - 1);
	for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
		printf("%d ", a[i]);
	printf("\n");
	return 0;
}

九、各個排序比較

排序方法 時間複雜度 空間複雜度 穩定性
插入排序 O(N^2) O(1) 穩定
希爾排序 O(N*1.3) O(1) 不穩定
選擇排序 O(N^2) O(1) 不穩定
堆排序 O(N*lgN) O(1) 不穩定
氣泡排序 O(N^2) O(1) 穩定
快速排序 O(N*lgN) O(lgN) 不穩定
歸併排序 O(N*lgN) O(N) 穩定