接着上一篇:排序算法小全《上》
长话短说,直奔主题吧。
目录:
一:插入类
1.直接插入排序
2.希尔排序
二:选择类
1.直接选择排序
2.堆排序
小结
本篇内容如下
三,交换类
和选择类差别是,交换意味着,每个数一边比较时,一边互换位置。
1.直接交换排序
共n个数需要排序,储存于数组中。
步骤:
1.相邻两数比较,0:1,1:2,2:3等等(满足条件交换位置)
,比较到最后一个数(n-2:n-1)
2.总数变为n-1,执行第1步.如此循环直至,排序成功。
注意:不一定需要等到n变为1,可以检测某一次排序时,只进行了比较,没有一次交换,这时就可以退出循环了。
动画演示:
代码如下:
void swapSort(int *ar, int count) {
int i;
int j;
int temp;
char isSwap;
for (i = 0; i < count; i++) {
for (isSwap = 0, j = 0; j < count - i - 1 ; j++) { //每次循环isSwap都是赋值为0
if (ar[j] > ar[j + 1]) {
temp = ar[j];
ar[j] = ar[j + 1];
ar[j + 1] = temp;
isSwap = 1; //如果存在交换,isSwap为1
}
}
if (isSwap == 0) { //内循环结束后,如果isSwap为0,即上次已经排序成功了,所以可以退出。
return;
}
}
}
2.快速排序
快速排序是基于交换排序的改良。
基本思路:对于每一组数据,总是以第一个数为基准,将小于它的数交换至其左侧,大于它的数保留在其右侧。
步骤:
1.取出数据中第一个数(temp保存),用头、尾指针遍历数组。
从尾指针(t)开始(头指针(h)指向的事第一个数取出的空空间),如果ar[t] > temp;那么不交换,t–;,直至ar[t] > temp,不成立。若不成立,则该数(ar[t])和ar[h]交换。且h++;
接着比较ar[h]和temp;如果小于成立,下一个数比较(h++),如果不成立,交换ar[h]和ar[t],且,t–;
2.重复以上两个操作,直至h == t;停止,且ar[h] = temp;完成一轮操作
3.此时数据分为两个部分,分别对这两个数据组进行上述操作,直至排序成功。
动画演示:
代码如下:
static void quickOnce(int *ar, int startIndex, int endIndex) {
int head = startIndex;
int tail = endIndex;
int tmp;
if (head > tail) { //递归必须先给出结束。
return;
}
tmp = ar[head];
while (head < tail) {
while (ar[tail] > tmp && head < tail) {
--tail;
}
if (head < tail) {
ar[head++] = ar[tail];
}
while (ar[head] < tmp && head < tail) {
++head;
}
if (head < tail) {
ar[tail--] = ar[head];
}
}
ar[head] = tmp;
quickOnce(ar, startIndex, head - 1); //前半部分排序
quickOnce(ar, head + 1, endIndex); //后半部分排序
}
void quickSort(int *ar, int count) {
quickOnce(ar, 0, count - 1);
}
四,特殊范围类
1.简单桶排序
步骤:
1.遍历数组,找到最大值,最小值,等间距设置固定数量的空桶。
2.将每个数据放入对应范围的桶中。
3.对每个桶进行排序。
4.将每个桶按顺序链接,得到最终结果
动画演示:
代码暂时没有。
2.优化一点的桶排序–计数排序
基本思想:空间换时间
步骤:
1.遍历数组,找到最大值,最小值。
2.遍历的同时,用另一个频度数组freq[max ](初始全为零),将每一个数值做为下标,出现一次,频度加1
3.遍历频度数组,频度值为多少,就输出几次这个下标几次。排序成功。
代码暂无:
3.特殊桶排序–基排序
特殊在哪?
桶分别为0-9,10-99,100-999等等
步骤:
1.取出最大数。
2.取出每个数的个位上的数,如果为1,就放如1桶里,例如(11,161,5981,61不排序),一次取出
3.再取出每个数的十位上的数,如果为1,就放入1桶里,如(11,115,6415不排序),注意*此时0桶里的都是小于10的数,且排好序,*一次取出
4.再取出每个数的百位上的数,那么100之前已经排好。
5.继续进行千位,万位。。。直至排序完成
动画演示:
代码暂无
4.变化的桶–归并排序
基本思路:指数爆炸
步骤:
1.从第一个数开始,排序。
2.排序前两个数,同时排序第三、第四个数。(此时,相对于将这组数分为两个两个的)
3.归并前四个数(排序归并),同时排序5–8个数(此时,相当于把之前两个为一组的合成4个为一组。所有的都合成。)
4.继续归并前16个数,排序16至32个数
5.如此继续归并加排序,直至结束。
代码如下:
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);
}
五、小结
个人最喜欢的堆排序。
关于排序的算法,
首先,一定得理清每一个的思路,明白各自算法的优缺点;不同排序方法适用于场景各有差异。
接着,根据思路,找到每个方法的重复单元,做好手工过程。
然后,根据手工过程,开始编程,一定得变量跟踪,不然往往会结尾时出错。
最后,总结分析,每种方法的共同点,不同点,归类。
每一行代码可能会遗忘,但每一个自己遇到的错误,自己总结出的经验都会生根在我们心中。
感谢指导文章:五分钟学算法
感谢指导老师:铁血教主
笔者水平有限,目前只能描述以上问题,如果有其他情况,可以留言,有错误,请指教,有继续优化的,请分享,谢谢!
2019年12.22 图书馆