c语言实现排序算法详解

因为笔者数据结构要考试了,所以在这里总结了一些用c语言实现的七大排序方法(均为升序实现),希望对于刚学数据结构与算法的同学能起到帮助。

一、冒泡排序

基本思路:

本质上是每两个数字之间的比较,每次比较后只需要把数字大的移动到后面即可,即每一次比较之后都会有现存数组中值最大的被移动到最后面,同时被固定住,在进行下次比较时,就不必再考虑。

代码实现

那么这里实现显然就需要两层循环,第一层循环的目的是控制选取的次数,即需要选取每次移动到最后的值的个数,故需要循环(n-1)次。第二层就是每次将基准值与后面的进行比较,一直将现存的值中最大的移动到后面,且固定住即可。

时间复杂度:O(n2)

稳定性:稳定 显然,当两个数值相同的时候,不会进行换序的操作的。

具体代码如下:

void BubbleSort(int a[], int n) {
for (int i = 0; i < n - 1; i++) {
	for (int j = 0; j < n - 1 - i; j++) {
		if (a[j] > a[j + 1]) {
			int t = a[j + 1];
			a[j + 1] = a[j];
			a[j] = t;
		}
	}
}
return;
}

二、快速排序

基本思路:

先选取一个基准值(一般是数组的第一个元素),然后思路类似于折半查找,但是多一个赋值操作。从右端开始,如果比基准大就往左走,比基准值小就赋值给左端。对于左端还是一样的思路,最终再将基准值插入到左右指针相遇的位置即可。之后在继续切割成左右两端,进行相同的操作。

代码实现:

整体上的思路显然是进行递归操作。对于其中的一个子过程,可以先类比于二分查找,先设立low,high指针。一个从左边指,一个从右边指。将基准值用一个变量进行存贮。先从右边进行判断,如果比基准值大,就直接往左走,但是如果比基准值要小,就直接将这个值赋值给low所指的值,同时low往右移动,然后进入对low的操作,与high的操作相同,不再赘述。之后别忘了将基准值赋值给中间相遇点。之后,进行递归即可。

时间复杂度:O(nlogn)

稳定性:不稳定

具体代码如下:

void QuickSort(int a[], int low, int high) {
int i = low;
int j = high;
int t = a[i];
if (i >= j) {
	return;
}
while (i < j) {
	while (i<j&&a[j]>t) {
		j--;
	}
	if (a[j] < t&& i < j)
	{
		a[i++] = a[j];
	}
	while (i < j&&a[i] < t)
	{
		i++;
	}
	if (a[i] > t && i < j) {
		a[j--] = a[i];
	}
}
a[i] = t;
QuickSort(a, low, i - 1);
QuickSort(a, i + 1, high);
}

三、堆排序

基本思路:

如果是想要最终进行升序排序,那么在进行调整的时候就要一直维持大根堆的形状。最后采用的是一种先确定后面,再确定前面的思路。即每次将大根堆的根节点与堆的最后一个节点交换,然后将最后一个值固定,再次进行adjust,重复操作即可。

代码实现:

需要两个函数,一个是用于做大根堆的调整,一个是用于进行堆排序的具体操作的。对于做大根堆的adjust函数,本质上就是判断每个根节点的左右字树的值是否大于根节点的值,若大于就将左右字树大的与根节点的值进行交换,若不大于就直接跳过。对于进行排序的函数来说,先要进行一些初始化,即从倒数第二层开始,先依次进行调整,这就是初始化(大家可以思考一下为什么)。然后就是依次将完全二叉树的根节点与最后一个节点进行交换即可,每次交换完进行大根堆的维持即可。

稳定性:不稳定

时间复杂度:O(nlogn)

void HeapAdjust(int a[],int v,int n) {
int i;
int t = a[v];
for (i = 2 * v + 1; i < n; i = 2 * i + 1) {
	if (a[i + 1] > a[i]&&i+1<n) {
		i++;
	}
	if (t > a[i])
		break;
	a[v] = a[i];
	v = i;
}
a[v] = t;
return;
}
void HeapSort(int a[], int n) {
for (int i = n / 2-1; i >= 0; i--) {
	HeapAdjust(a, i, n);
}
for (int j = n - 1; j >= 0; j--) {
	int t = a[j];
	a[j] = a[0];
	a[0] = t;
	HeapAdjust(a, 0, j);
}
return;
}

四、归并排序

基本思路:总体上就是字面意思,先拆开,最其中的小单元进行排序,然后逐渐整合,再对大单元进行逐层整合与排序,最终完成排序。

代码实现:由上述的解析可以看出,肯定是需要两个函数来进行操作。一个是微观的,对于有小到大的具体的排序过程的操作,就是设立两个指针,一个额外数组,一个从左端开始指,一个从中间开始指,找到一个小的就将其存入额外数组中,然后指针后移即可,最后如果一端到了尽头,就将剩下的未到尽头的一端全部赋值给额外数组即可。然后对于,总体来说,对于这种需要一直重复操作的思想肯定是递归。具体的参考代码即可,相信大家都是很容易理解的。

稳定性:稳定

时间复杂度:O(nlogn)

void merge(int a[], int left,int mid,int right, int t[]) {
int i = left;
int j = mid + 1;
int k = left;
while (i <= mid && j <= right) {
	if (a[i] < a[j]) {
		t[k++] = a[i++];
	}
	else {
		t[k++] = a[j++];
	}
}
	while (i <= mid) {
		t[k++] = a[i++];
	}
	while (j <= right) {
		t[k++] = a[j++];
	}
memcpy(a + left, t + left, sizeof(int)*(right - left + 1));
return;
}
void MergeSort(int a[], int left,int right,int t[]) {
if (left >= right)
	return;
int mid = (left + right) / 2;
MergeSort(a, left, mid,t);
MergeSort(a, mid + 1, right,t);
merge(a, left, mid, right, t);
}

五、简单插入排序

基本思路:

从第二个位置向数组的后面进行查找,如果发现有比后面的值大的,说明后面的值的位置出现了问题,于是需要再往前找,找到一个左右的值的大小都符合要求的位置,将其插入即可。

代码实现:

两个循环,第一个循环就是找不符合位置的值,并用一个变量将其存储起来,第二个循环就是从该值的前一个位置找起,将前一个值后移,等到找到一个与变量值相等或者小于变量值的位置后,跳出循环。那么该值的下一个位置就是你要插入的位置。

稳定性:稳定 因为在往前找的条件就是<=,当遇到我相等的值的时候,并不会改变顺序。

时间复杂度:O(n2)

void InsertSort(int a[], int n) {
int i, j;
for (i = 1; i <n; i++) {
	if (a[i - 1] > a[i]) {
		int t = a[i];
		for (j = i - 1; j >= 0 && a[j]>t; j--) {
				a[j + 1] = a[j];
		}
		a[j + 1] = t;
	}
}
return;
}

六、简单选择排序

基本思路:

依次判断最小的是不是剩余数组的第一个元素,若是,将第一个元素固定住,往后面继续查找。若不是,就进行交换。

代码实现:

显然两层循环,两层循环是为了确定剩余数组中的最小值的位置,比较简单,不在赘述。然后就判断是否是剩余数组第一个元素的位置,不是就交换。

稳定性:不稳定 例如:53518,在进行第一次排序之后,两个5的顺序其实是会发生改变的。

时间复杂度:O(n2)

void SelectSort(int a[], int n) {
int v;
int i, j;
for (i = 0; i < n; i++) {
	int v = i;
	int min = a[i];
	for (j = i + 1; j < n; j++) {
		if (a[j] < min) {
			v = j;
			min = a[j];
		}
	}
	if (v != i) {
		int temp = a[i];
		a[i] = a[v];
		a[v] = temp;
	}
}
return;
}

七、希尔排序

基本思想:

其实就是划分了多个GAP的插入排序,在每一个GAP组里面进行插排,然后不断减小GAP的值,到一为止,其实最后就是插排。

代码实现:与插排大部分类似,就是外面多了一层控制GAP的循环条件,然后里面对于移动到前一个位置的加减1全部换成加减GAP即可。

稳定性:不稳定 ,虽然插排是稳定的,但是对于划分了多个GAP的插排,他只是在每一个GAP里面符合稳定的特征而已,在每一个GAP组之间是不好判断的。

时间复杂度:经计算应该是O(n1.3)左右。

void ShellSort(int a[], int n) {
int gap, i, j, k;
for (gap = n / 2; gap > 0; gap /= 2) {
	for (i = 0; i < gap; i++) {
		for (j = i + gap; j < n; j+=gap) {
			if (a[j - gap] > a[j]) {
				int t = a[j];
				for (k = j - gap; k >= 0 && a[k] > t; k-=gap) {
					a[k + gap] = a[k];
				}
				a[k + gap] = t;
			}
		}
	}
}
return;
}

以下是代码整体以及测试主函数(经测试代码正确)

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//冒泡排序
void bubblesort(int a[], int n) {
for (int i = 0; i < n - 1; i++) {
	for (int j = 0; j < n - 1 - i; j++) {
		if (a[j] > a[j + 1]) {
			int t = a[j + 1];
			a[j + 1] = a[j];
			a[j] = t;
		}
	}
}
return;
}
void BubbleSort(int a[], int n) {
bubblesort(a, n);
for (int i = 0; i < n; i++) {
	printf("%d ", a[i]);
}
}
//快速排序
void quicksort(int a[], int low, int high) {
int i = low;
int j = high;
int t = a[i];
if (i >= j) {
	return;
}
while (i < j) {
	while (i<j&&a[j]>t) {
		j--;
	}
	if (a[j] < t&& i < j)
	{
		a[i++] = a[j];
	}
	while (i < j&&a[i] < t)
	{
		i++;
	}
	if (a[i] > t && i < j) {
		a[j--] = a[i];
	}
}
a[i] = t;
quicksort(a, low, i - 1);
quicksort(a, i + 1, high);
}
void QuickSort(int a[], int low,int n) {
quicksort(a, low, n-1);
for (int i = 0; i < n; i++) {
	printf("%d ", a[i]);
}
}
//堆排序
void HeapAdjust(int a[],int v,int n) {
int i;
int t = a[v];
for (i = 2 * v + 1; i < n; i = 2 * i + 1) {
	if (a[i + 1] > a[i]&&i+1<n) {
		i++;
	}
	if (t > a[i])
		break;
	a[v] = a[i];
	v = i;
}
a[v] = t;
return;
}
void heapsort(int a[], int n) {
for (int i = n / 2-1; i >= 0; i--) {
	HeapAdjust(a, i, n);
}
for (int j = n - 1; j >= 0; j--) {
	int t = a[j];
	a[j] = a[0];
	a[0] = t;
	HeapAdjust(a, 0, j);
}
return;
}
void HeapSort(int a[], int n) {
heapsort(a, n);
for (int i = 0; i < n; i++) {
	printf("%d ", a[i]);
}
}
//归并排序
void merge(int a[], int left,int mid,int right, int t[]) {
int i = left;
int j = mid + 1;
int k = left;
while (i <= mid && j <= right) {
	if (a[i] < a[j]) {
		t[k++] = a[i++];
	}
	else {
		t[k++] = a[j++];
	}
}
	while (i <= mid) {
		t[k++] = a[i++];
	}
	while (j <= right) {
		t[k++] = a[j++];
	}
memcpy(a + left, t + left, sizeof(int)*(right - left + 1));
return;
}
void mergesort(int a[], int left,int right,int t[]) {
if (left >= right)
	return;
int mid = (left + right) / 2;
mergesort(a, left, mid,t);
mergesort(a, mid + 1, right,t);
merge(a, left, mid, right, t);
}
void MergeSort(int a[], int left, int right, int t[]) {
mergesort(a, left, right, t);
for (int i = 0; i < right + 1; i++) {
	printf("%d ", a[i]);
}
}
//插入排序
void insertsort(int a[], int n) {
int i, j;
for (i = 1; i <n; i++) {
	if (a[i - 1] > a[i]) {
		int t = a[i];
		for (j = i - 1; j >= 0 && a[j]>t; j--) {
				a[j + 1] = a[j];
		}
		a[j + 1] = t;
	}
}
return;
}
void InsertSort(int a[], int n) {
insertsort(a, n);
for (int i = 0; i < n; i++) {
	printf("%d ", a[i]);
}
return;
}
//选择排序
void selectsort(int a[], int n) {
int v;
int i, j;
for (i = 0; i < n; i++) {
	int v = i;
	int min = a[i];
	for (j = i + 1; j < n; j++) {
		if (a[j] < min) {
			v = j;
			min = a[j];
		}
	}
	if (v != i) {
		int temp = a[i];
		a[i] = a[v];
		a[v] = temp;
	}
}
return;
}
void SelectSort(int a[], int n) {
selectsort(a, n);
for (int i = 0; i < n; i++) {
	printf("%d ", a[i]);
}
}
//希尔排序
void shellsort(int a[], int n) {
int gap, i, j, k;
for (gap = n / 2; gap > 0; gap /= 2) {
	for (i = 0; i < gap; i++) {
		for (j = i + gap; j < n; j+=gap) {
			if (a[j - gap] > a[j]) {
				int t = a[j];
				for (k = j - gap; k >= 0 && a[k] > t; k-=gap) {
					a[k + gap] = a[k];
				}
				a[k + gap] = t;
			}
		}
	}
}
return;
}
void ShellSort(int a[], int n) {
shellsort(a, n);
for (int i = 0; i < n; i++) {
	printf("%d ", a[i]);
}
}
int main() {
int a[10] = { 5,6,4,7,8,9,2,3,1,10 };
int n = 10;
int t[10];
//BubbleSort(a, n);
//QuickSort(a, 0, n);
//HeapSort(a, n);
//MergeSort(a, 0, n - 1, t);
//InsertSort(a, n);
//SelectSort(a, n);
//ShellSort(a, n);
system("pause");
return 0;
}

希望大家对于出现的错误在评论区进行探讨,谢谢大家!!!

posted @ 2021-06-30 17:04  邻菲子  阅读(779)  评论(0)    收藏  举报