八大排序 详解(中)

接上篇博文——《八大排序 详解(上)》

在这篇博文中本人就不过多解释排序的重要性了,接着是本人上篇博文——《八大排序 详解(上)》来讲,在这篇博文中,本人要进行讲解的是剩下的四大排序——插入排序、希尔排序(插入排序的进阶版)、桶排序以及归并排序

五、插入排序
插入排序的原理是:
将所需排列的数组分为两半——已排和未排
每次将未排的第一个数取出,然后从已排数组的第一个开始比较,遇到比它大的数,就插到那个数的前面。

那么,我们现在用一张动态图来展示一下这个排序算法:
在这里插入图片描述
那么,用代码来实现的结果如下:

void insertSort(int *array, int count) {
	int i;
	int j;
	int t;
	int tmp;

	for (i = 1; i < count; i++) {
		tmp = array[i];
		for (j = 0; j < i && tmp > array[j]; j++)
			;
		for (t = i; t > j; t--) {
			array[t] = array[t-1];
		}
		array[j] = tmp;
	}
}

从这里我们能够发现,插入排序的时间复杂度是O(n ^2)。

六、希尔排序(插入排序的进阶版):
希尔排序和别的几大排序相比,算是比较中庸那种,因为它的最差情况和最优情况基本没多大差别,它的基本思想如下:
首先,我们将每隔step个数的每个数放在一起,这样分好类之后,再用新的step分组,再进行如上操作(每个step都是数组长度每次除以2所得),直到step为0时结束

现在,我们用一个动态图来实现这个排序算法:
在这里插入图片描述
而作为插入排序的进阶版,这个排序算法和插入排序算法的代码都基本一样。
相关代码如下:

static void insertSortOnce(int *array, int count, int start, int step) {
	int i;
	int j;
	int t;
	int tmp;

	for (i = start + step; i < count; i += step) {
		tmp = array[i];
		for (j = start; j < i && tmp > array[j]; j += step)
			;
		for (t = i; t > j; t -= step) {
			array[t] = array[t-step];
		}
		array[j] = tmp;
	}
}

void ShellSort(int *array, int count) {
	int step;
	int start;

	printf("\nShell sort\n");
	for (step = count/2; step; step /= 2) {
		for (start = 0; start < step; start++) {
			insertSortOnce(array, count, start, step);
		}
	}
}

这里对上面的代码做下解释:
第一个函数的类型前都有个static,意思是这个函数仅在该文件中使用,我们所编写的函数在接下来的代码中能够发现,提供给用户使用的只是这八大排序,至于构成这八大函数的子函数,不会提供给用户,这也是我们在数据结构与算法专题中要提到的,编写的.h的.c函数能够提供给外部使用的函数,在.h文件中声明;至于不能给外部使用的函数,就在.c文件中声明,我们在未来编程的过程中会了解到我们所做的工具函数和这里一样,并不是把所有的函数都提供给用户使用。

从这里我们能够发现,希尔排序的时间复杂度是O(n ^(1.3 ~ 2))。

七、归并排序:
归并排序的基本思想是:
分而治之,先两个两个比较,凑齐两组两两排好的组后,再进行四个四个比较,之后就是八个八个...直至比较的范围是整个数组的长度,待排完这轮后就结束排序。

现在,我们通过一张动态图来展示下这个排序算法:
在这里插入图片描述
那么,相关代码如下:

static void merge(int* array, int left, int right, int mid, int* temp) {
	int i = left;
	int j = mid + 1;
	int tempLeft = left;
	int t = 0;
	
	while (i <= mid && j <= right) {
		temp[t] = array[i] <= array[j] ? array[i++] : array[j++];
		t++;
	}
	while (i <= mid) {
		temp[t++] = array[i++];
	}
	while (j <= right) {
		temp[t++] = array[j++];
	}								//以上三个循环的目的是将整个数组排好序再存入temp数组中

	t = 0;
	while (tempLeft <= right) {		//这个循环的目的是将排好序的数组内的值赋值给原数组
		array[tempLeft++] = temp[t++];
	}
}

static void mSort(int* array, int left, int right, int* temp) {
	int mid;

	if (left >= right) {
		return;
	}
	
	mid = (left + right) / 2;
	mSort(array, left, mid, temp);
	mSort(array, mid + 1, right, temp);
	merge(array, left, right, mid, temp);

}

void mergeSort(int* array, int count) {
	int *temp;
	
	temp = (int *)calloc(sizeof(int), count);
	mSort(array, 0, count-1, temp);
}

从这里我们能够发现,归并排序的时间复杂度是O(n * log n)。

八、桶排序:
桶排序作为我们八大排序的最后一个来讲,是因为这个排序算法在我看来比较偏激,因为它变相地运用了“用空间换时间”的思想,它的大致原理如下:
首先,我们放置10个桶,分别代表某一位为0~9哪一位的数都有谁,最后将桶中所有数取出放入原数组中,这样就完成了桶排序。

现在,我们通过一张动态图来展示下这个排序算法:
在这里插入图片描述
那么,代码如下:

//寻找原数组中的最大值
static int findMaxNum(const int *array, int count) {
	int max = array[0];
	int i;

	for (i = 1; i < count; i++) {
		if (array[i] > max) {
			max = array[i];
		}
	}

	return max;
}

//寻找原数组中的最小值
static int findMinNum(const int *array, int count) {
	int min = array[0];
	int i;

	for (i = 1; i < count; i++) {
		if (array[i] < min) {
			min = array[i];
		}
	}

	return min;
}

void bucketSort(int *array, int count){
	int i = 0;
	int j = 0;
	int max = findMaxNum(array, count);
	int min = findMinNum(array, count);
	int length = max - min;	//通过最大值和最小值 来获取结果“桶”的容量

	int *res = (int *)calloc(sizeof(int), length+1);

	//用 结果数组中的适当下标的单元中的数,来记录垓下标在原数组中的出现次数
	for (i = 0; i < count; i++) {
		res[array[i] - min]++;
	}
 
	//为原数组赋排序后的值
	for(i = 0; i <= length; ++i) {
		while(res[i]--) {
			array[j++] = i + min;
		}
	}
	free(res);
}

我们可以在这里发现,桶排序的其实就是先放入桶中,再从桶中取出,放回原数组,这样下来,时间复杂度为O(n),但是空间复杂度比较高,所以它也算是很偏激那种排序算法,如果对时间需求高,这个排序算法无疑是最好的选择。

那么,到目前为止,我们的八大排序就讲解完成了,希望同学们在看完本人的讲解之后能够有所收获!
关于本人有关排序的最后一篇博文——《八大排序 详解(下)》,将会使用《八大排序 详解(上)》和本篇博文所编写的所有函数,并以一种十分凶悍的方法进行调用函数,也会用到本人之前的博文《指向函数的指针——消费未来》那篇所运用的知识,希望看到这里的同学能够耐心将下篇看完,谢谢!!!

《八大排序 详解(上)》
《八大排序 详解(下)》

posted @ 2020-03-04 20:24  在下右转,有何贵干  阅读(273)  评论(0编辑  收藏  举报