目录
前言
本文利用Microsoft Visual Studio的C语言编写平台及其自带的输出窗口尝试制作了十种基本排序算法的简单可视化。最终的输出都是文字、数字与符号,笔者已经尽力调整其位置以让结果尽可能清楚。
本来是自己在学习排序算法的时候希望可以看到输入的数据在计算机内部被操作的过程,这样方便理解,同时又想用尽可能简单(懒)的方式实现,所以做了这些尝试。相比于网上的动画式可视化工具,本方法在视觉效果上略逊一筹。
创作这些代码的过程也是检验自己对算法理解程度的过程,然鹅笔者非相关专业学生,水平有限,希望愿意看完的读者可以在评论中指出文章的错误或者不合理的地方。
代码使用提示:以下各部分代码虽然分组展示,但是使用的时候务必所有代码作为整体一起复制后使用,因为其中存在相互调用的内容。同时,由于输出格式上的一些原因,输入的数据最好在一定范围内。堆排序程序的输入在-10~10的范围内,负数用整型数据,基数排序程序的输入非负,计数排数输入-10~10的整型数据,其余七个的输入在-100~100范围内,小数点后不超过2位。(虽然超过这个范围也能排序,但是在显示格式上可能会出一些bug)
另外,本文没有对算法本身进行过多的讲解,而是重在展示算法运行的过程。
一、插入排序
1.算法简介
插入排序的基本思路是对数组中的数据逐个遍历并将其插入到数组中合适的位置使整个数组有序。在排序的过程中,可以将整个数组分为已经被排序的部分和没有被排序的部分。已经被排序的部分(记为S,意为sorted)是有序的,从数组的第一个元素开始,并占据数组的前面一部分。没有被排序的部分(记为U,意为unsorted)位于数组的后面一部分,并且其中的元素是无序的。排序开始时S仅含一个元素,即数组的第一个元素,而U包含第二个到最后一个元素。排序过程中不断的将U的首个元素插入到S中的合适位置,使U不断向数组末尾缩短,并使S始终保持有序。最终当U的长度为0,即完成对数组最后一个元素的插入后,排序终止。
2.代码
#include<stdio.h>
//插入排序
float* Insertionsort(float* Array, int lenth) {
printf("\n食用方法:\n&表示本轮插入排序需要移动的数,?表示正在寻找插入的位置(没有?就是放在原来的位置),*表示\
将&下方的数插入到这里\n采用升序排列\n");
float tmp;
int ins_pos;
for (int j = 1; j < lenth; j++) {
int k, l;
for (k = j - 1; Array[k] > Array[j] && k >= 0; k--) {
//打印注释
printf("\n");
for (int note1 = 0; note1 < j; note1++) {
if (note1 == k) {
printf("?");
}
printf("\t");
}
printf("&\n");//
printArrayF(Array, 0, lenth - 1);
}
ins_pos = k + 1;//找到插入的位置
//打印注释
printf("\n");
for (int note2 = 0; note2 < j; note2++) {
if (note2 == ins_pos) {
printf("*");
}
printf("\t");
}
printf("&");//
//显示当前数组
printf("\n");
printArrayF(Array, 0, lenth - 1);
if (ins_pos == j) {
continue;
}//这种情况不用插入
tmp = Array[j];
for (l = j - 1; l >= ins_pos; l--) {
Array[l + 1] = Array[l];
}//移动
Array[l + 1] = tmp;//插入
}
printf("排序结果为:\n");
printArrayF(Array, 0, lenth - 1);
return Array;
}
3.运行效果
对这些符号略作解释:排序开始时,S={3},U={5,-4,-2.9},需要将5插入到S中合适的位置,因此,5的上方有“&”记号,表示本轮运算考察的数据。由于5>3,所以不需要移动,直接进入下一轮运算。此时S={3,5},U={-4,-2.9},需要考察-4应该移动到S中的什么位置,所以-4的上方有“&”记号。“?”表示正在搜索-4需要插入的位置,当最终搜索完成,发现-4需要插入到S第一个位置上时,对应位置的数据上放有“*”,即这里是最终插入的位置,此后将S中的原有数据向右移动一格,并插入-4。其余过程同理。
二、冒泡排序
1.算法简介
如算法名称所示,冒泡排序通过不断的将数组未排序部分中最大/最小的数移动到数组已排序部分(这一过程即“冒泡”),并逐渐缩短未排序部分的长度,最终构成有序的数组。冒泡的过程是通过两两比较完成的。此处的冒泡排序是经过优化的冒泡,即每一轮冒泡后记录最后一次数据交换的位置,以减少下一轮冒泡的操作量。
2.代码
#include<stdio.h>
float* Bubblesort(float* Array, int lenth) {
printf("食用方法:上方有“*”标记的两个数表示接下来需要被交换;“!”表示本轮冒泡最后一次交换的位置\n");
printf("采用升序排列,每一轮冒泡通过两两交换把未排序部分最大的数移动到后面\n");
int U_end = lenth - 1, i;
for (int Unsort = lenth - 1; Unsort > 0; Unsort = U_end) {
for (i = 0, U_end = 0; i < Unsort; i++) {
if (Array[i] > Array[i + 1]) {
//打印交换标记
printf("\n");
for (int j = 0; j < i; j++) {
printf("\t");
}
printf("*\t*\n");
printArrayF(Array, 0, lenth - 1);
//
int tmp = Array[i];
Array[i] = Array[i + 1];
Array[i + 1] = tmp;
U_end = i;
}
}
//打印感叹号
printf("\n");
for (int j = 0; j < U_end; j++) {
printf("\t");
}
printf("!\n");
printArrayF(Array, 0, lenth - 1);
}
printf("\n排序的结果为:\n");
printArrayF(Array, 0, lenth - 1);
return Array;
}
3.运行效果
解释:从第一行看起。由于采用升序排列,每一次冒泡需要U中把最大的数移动到U的最后。初始时U为整个数组,S为空集。从最前面开始看,因为-32<45,所以这两个数不需要交换,接着看45和9,由于45>9,所以交换这两个数的位置,图中这两个数上方的“*”即交换两个数的位置。之后通过不断交换,将U中最大的数(45)移动到了最后,然后S从最后向前延申一格,U向前缩短一格。此时S={45},U={-32,9,-6.7,4.55}。由于最后一次交换发生在倒数第二个数和倒数第一个数间,所以用“!”记录了本轮冒泡最后一次交换的位置。
三、选择排序
1.算法简介
同样将整个数组分为已经排序和未排序两个部分。开始时U即整个数组,S是空集。进行每一轮运算的时候都遍历U中所有的元素并找到最小的元素,之后将最小的元素和U的第一个元素交换,再将U向数组末尾缩减一格,S向数组末尾延申一格。最后直到U只剩一个元素,即数组的最后一个元素时,排序结束
2.代码
#include<stdio.h>
float* Selectionsort(float* Array, int lenth) {
printf("“?”表示正在寻找未排序部分的最小元素,上方有&的数是当前程序记录的未排序部分的最小值\
,上方有“*”的数需要被交换\n采用升序排列\n");
int min, i, j;
for (i = 0; i < lenth - 1; i++) {
for (j = i, min = i; j <= lenth - 1; j++) {//寻找未排序部分最小值
if (j == min) {
//&和?重合,只打印&
printf("\n");
for (int k = 1; k <= i; k++) {
printf("\t");
}
printf("[");
for (int k = 1; k <= min - i; k++) {
printf("\t");
}
printf("&");
for (int k = 1; k <= lenth - min; k++) {
printf("\t");
}
printf("]\n");
printArrayF(Array, 0, lenth - 1);
//
}
else {//打印?和&
printf("\n");
for (int k = 1; k <= i; k++) {
printf("\t");
}
printf("[");
for (int k = 1; k <= min - i; k++) {
printf("\t");
}
printf("&");
for (int k = 1; k <= j - min; k++) {
printf("\t");
}
printf("?");
for (int k = 1; k <= lenth - j; k++) {
printf("\t");
}
printf("]\n");
printArrayF(Array, 0, lenth - 1);
//
}
if (Array[min] > Array[j]) {
min = j;
}
}
printf("\n未排序部分最小值的位置是:\n");//打印寻找未排序部分最小值的结果
for (int k = 1; k <= i; k++) {
printf("\t");
}
printf("[");
for (int k = 1; k <= min - i; k++) {
printf("\t");
}
printf("&");
for (int k = 1; k <= lenth - min; k++) {
printf("\t");
}
printf("]\n");
printArrayF(Array, 0, lenth - 1);
if (min != i) {
//打印交换符号
printf("\n交换:\n");
for (int k = 1; k <= i; k++) {
printf("\t");
}
printf("[*");
for (int k = 1; k <= min - i; k++) {
printf("\t");
}
printf("*");
for (int k = 1; k <= lenth - min; k++) {
printf("\t");
}
printf("]\n");
printArrayF(Array, 0, lenth - 1);
//
int tmp = Array[i];
Array[i] = Array[min];
Array[min] = tmp;
}
else {
printf("本轮不需要交换!\n");
}
}
printf("排序结果为:\n");
printArrayF(Array, 0, lenth - 1);
return Array;
}
3.运行效果
解释:初始时U={2.9,-2.9,3,5,4},S=,此时开始寻找U中的最小元素。在没有开始搜索前,自动设定U部分的第一个元素是暂存的最小元素,所以2.9的上方有“&”记号。“?”表示正在逐个搜索,每次遇到比当前记录的最小值更小的元素时,程序会更新最小值的记录,并在数字上方用“&”显示。最终一轮搜索完成后会标出本轮U中的最小值,并和U中的第一个元素交换,之后开始下一轮。用“【】”括起来的部分表示U,没有括起来的部分表示S。
四、快速排序
1.算法简介
快速排序的每一轮运算都会选取数组当中的一个数作为标志(本文及程序称其为分割标志位),并且将整个数组的数分成三部分,即小于标志位的数,标志位本身,大于标志位的数。运算时会通过移动将小于标志位的数被放在标志位左侧,大于标志位的数被放在标志位右侧。此后,对标志位左侧的数字序列和标志位右侧的数字序列分别递归进行快速排序。此类递归在输入的数组长度为1时自动终止并返回上一层。当所有递归都完成后 ,数组中较小的数被放在左边,更大的数排在右边,这样就完成了排序。快速排序作为20世纪十大算法之一,在处理大型数据库时非常有效,有广泛的应用。
关于分割标志位的选取,较好的方式是尽可能随机的在数组中选取一个数作为标志位,这样可以取得更优的平均性能。不过本文为了追求简洁(懒),直接选取数组中间的那个数作为标志位。
2.代码
#include<stdio.h>
float* Quicksort(float* Array, int head, int end) {//head和end是头和尾的下标
if (head >= end)
return;
int M = (head + end) / 2;//M是分割标志位
float key = Array[M];//暂存标志位的值
float tmp;
int left = head, right = end;//左右指针
int printed = 0;//防止重复打印
printf("\n移动并分割:\n");
while (left < right) {
while (left < right && Array[right] > key) {//移动右指针
//打印标志
printf("\n");
for (int i = 1; i <= M; i++) {
printf("\t");
}
printf("*");
for (int i = 1; i <= right - M; i++) {
printf("\t");
}
printf("?\n");
for (int i = 1; i <= head; i++) {//把数组打印在正确的位置(有可能需要在前面空几格)
printf("\t");
}
printArrayF(Array, head, end);
//
right--;
printed = 1;
}
//打印标志
if (printed == 0) {
printf("\n");
for (int i = 1; i <= M; i++) {
printf("\t");
}
printf("*");
for (int i = 1; i <= right - M; i++) {
printf("\t");
}
printf("?\n");
for (int i = 1; i <= head; i++) {//把数组打印在正确的位置(有可能需要在前面空几格)
printf("\t");
}
printArrayF(Array, head, end);
//
}
if (left < right) {//交换
//打印标志
printf("\n");
for (int i = 1; i <= M; i++) {
printf("\t");
}
printf("*");
for (int i = 1; i <= right - M; i++) {
printf("\t");
}
printf("&\n");
for (int i = 1; i <= head; i++) {//把数组打印在正确的位置(有可能需要在前面空几格)
printf("\t");
}
printArrayF(Array, head, end);
//
printf("交换%.2f和%.2f\n", Array[right], Array[M]);
tmp = Array[right];
Array[right] = Array[M];
Array[M] = tmp;
M = right;
printed = 0;
}
while (left < right && Array[left] <= key) {//移动左指针
//打印标志
printf("\n");
for (int i = 1; i <= left; i++) {
printf("\t");
}
printf("?");
for (int i = 1; i <= M - left; i++) {
printf("\t");
}
printf("*\n");
for (int i = 1; i <= head; i++) {//把数组打印在正确的位置(有可能需要在前面空几格)
printf("\t");
}
printArrayF(Array, head, end);
//
left++;
printed = 1;
}
if (printed == 0) {
//打印标志
printf("\n");
for (int i = 1; i <= left; i++) {
printf("\t");
}
printf("?");
for (int i = 1; i <= M - left; i++) {
printf("\t");
}
printf("*\n");
for (int i = 1; i <= head; i++) {//把数组打印在正确的位置(有可能需要在前面空几格)
printf("\t");
}
printArrayF(Array, head, end);
//
}
if (left < right) {//交换
//打印标志
printf("\n");
for (int i = 1; i <= left; i++) {
printf("\t");
}
printf("&");
for (int i = 1; i <= M - left; i++) {
printf("\t");
}
printf("*\n");
for (int i = 1; i <= head; i++) {//把数组打印在正确的位置(有可能需要在前面空几格)
printf("\t");
}
printArrayF(Array, head, end);
//
printf("交换%.2f和%.2f\n", Array[left], Array[M]);
tmp = Array[left];
Array[left] = Array[M];
Array[M] = tmp;
M = left;
printed = 0;
}
}
Array[M] = key;//放回标志位的值
//打印分割的结果
printf("\n分割的结果:\n");
for (int i = 1; i <= M; i++) {
printf("\t");
}
printf("*\n");
for (int i = 1; i <= head; i++) {
printf("\t");
}
printArrayF(Array, head, end);
//
Quicksort(Array, head, M - 1);//左侧递归
Quicksort(Array, M + 1, end);//右侧递归
return Array;
}
3.运行效果
解释:对于初始序列,选取中间的数(0)作为第一次快速排序的分割标志位,因此0的上方有“*”记号表示次此处是标志位。随后另设变量暂存标志位,然后从数组的最右端开始向前逐个查看,如果正在查看的数小于标志位,则将该数与标志位交换(这样就把小于标志位的数换到标志位左边),并将搜索对象调整为从最左端开始向后查看,如果当前查看的数大于标志位,则将该数与标志位交换(这样就把大于标志位的数换到标志位右边),随后从上一次右端搜索停止的那一个数的左边一个开始向前搜索。如此反复进行,直到左右搜索的指针撞到一起,则本轮排序完成,然后把最开始暂存的标志位的值存放到左右指针重合的位置。输出窗口的“?”表示正在搜索,而“&”表示需要和标志位交换。如此进行最后得到分割的结果,然后可以看到对标志位左侧的数组进行下一轮快速排序。
五、归并排序
1.算法简介
归并排序采用典型的分治策略,对数组不断的进行分割,直到分割出来的小数组长度为1,然后从长度为1的数组开始逐渐合并,合并时保证两个小数组合并成的大数组有序。这可以通过“递归+合并函数”实现。
2.代码
#include<stdio.h>
#include<stdlib.h>
float* Mergesort(float* Array, int head, int tail) {
if (head >= tail) {//如果只剩一个元素或者为空,直接返回
return;
}
if (tail - head == 1) {
//打印
printf("\n分割:\n%.2f\t\t%.2f\n", Array[head], Array[tail]);
//
if (Array[tail] < Array[head]) {
float tmp = Array[tail];
Array[tail] = Array[head];
Array[head] = tmp;
}
//合并
printf("\n合并:\n%.2f\t\t%.2f\n", Array[head], Array[tail]);
//
return;
}
int M = (head + tail) / 2;
//打印
printf("\n分割:\n");
printArrayF_no_enter(Array, head, M - 1);
printf("\t");
printArrayF_no_enter(Array, M, tail);
printf("\n");
//
Mergesort(Array, head, M - 1);//分割
Mergesort(Array, M, tail);//分割
int len = tail - head + 1;
float* a, * b;
if ((a = (float*)malloc(sizeof(float) * len)) == NULL) {
printf("malloc failed! a is NULL!");
exit;
}
if ((b = (float*)malloc(sizeof(float) * len)) == NULL) {
printf("malloc fialed! b is NULL!");
exit;
}
for (int i = head; i <= M - 1; i++) {
a[i] = Array[i];//复制前一半
}
for (int i = M; i <= tail; i++) {
b[i] = Array[i];//复制后一半
}
//打印
printf("\n合并:\n");
printArrayF_no_enter(Array, head, M - 1);
printf("\t");
printArrayF(Array, M, tail);
//
for (int i = head, j = head, k = M; i <= tail; ) {//j是a的下标,k是b的下标
if (j == M && k <= tail) {
//打印
printf("\n数组a:\n");
for (int p = 1; p <= M - 1 - head; p++) {
printf("\t");
}
printf("*\n");
printArrayF(a, head, M - 1);
printf("\n数组b:\n");
for (int p = 1; p <= k - M; p++) {
printf("\t");
}
printf("?\n");
printArrayF(b, M, tail);
//
Array[i++] = b[k++];
printf("\n\n逐步展示合并后的数组:");
printArrayF(Array, head, i - 1);
}
else if (k == tail + 1 && j <= M - 1) {
//打印
printf("\n数组a:\n");
for (int p = 1; p <= j - head; p++) {
printf("\t");
}
printf("?\n");
printArrayF(a, head, M - 1);
printf("数组b:\n");
for (int p = 1; p <= tail - M; p++) {
printf("\t");
}
printf("*\n");
printArrayF(b, M, tail);
//
Array[i++] = a[j++];
printf("\n逐步展示合并后的数组:");
printArrayF(Array, head, i - 1);
}
else
{
//打印
printf("\n数组a:\n");
for (int p = 1; p <= j - head; p++) {
printf("\t");
}
printf("?\n");
printArrayF(a, head, M - 1);
printf("数组b:\n");
for (int p = 1; p <= k - M; p++) {
printf("\t");
}
printf("?\n");
printArrayF(b, M, tail);
//
if (a[j] < b[k]) {
Array[i++] = a[j++];
}
else {
Array[i++] = b[k++];
}
printf("\n逐步展示合并后的数组:");
printArrayF(Array, head, i - 1);
printf("\n");
}
}
return Array;
}
3.运行效果
解释:每一步分割时,都会从数组中间将整个数组拆开,因此对于初始输入{1,-9,2,-5.5,0},将其分割为{1,-9}和{2,-5.5,0},可以看到两个短数组之间留有较大空格表示分割。然后进行递归, 对左侧的数组运行归并排序,则将其分割为{1}和{-9},同样两个数之间留有较大空格表示分割。随后由于数组长度为1,递归终止,开始合并。因为只有两个数,所以合并较为简单,直接把小的数放在前面组成新的数组即可。完成合并后对{1,-9}的处理结束,转向{2,-5.5,0}进行递归,分割为{2}和{-5.5,0},然后同理再将右边一个分解成{-5.5}和{0},之后将其合并为{-5.5,0}。上述步骤都较为简单,因为合并的过程需要处理的数据只有两个。从下一步开始较为复杂一些,可以看到合并{2}和{-5.5,0}这两个子数组的过程被展示在了输出窗口中。记{2}为数组a,{-5.5,0}为数组b,可以看到2和-5.5上方各有一个“?”,这表示程序当前正在查看并比较两个数组的首个元素。程序会把两个数中较小的一个(-5.5)放入合并后的数组,并将对应的指针后移一位(?移动到0的上方),接着比较2和0。同理,将0放入合并后的数组。由于0是数组b的最后一个数,所以接下不需要再考虑数组b,0上方的“*”记号表示数组b已经遍历完成,只要把数组a的数逐个按序移动到合并后的数组即可。后面的过程不再展示,与此同理。
六、堆排序
1.算法简介
堆排序是在构建堆的基础上进行的,此处堆是一棵二叉树,且所谓“最大堆”满足“任意一个节点值大于(大于等于)其所有子节点值”的性质,即可以看作在每一棵二叉子树内部数据从上到下逐渐减小,但是不同的树之间的数据不考虑其大小关系。首先通过维护最大/最小堆的性质的函数构建最大/最小堆,然后将根节点(最大/最小的那个)和数组末尾元素交换,随后对第一个到倒数第二个元素再构建最大/最小堆,然后再把根节点(后来构建的堆中最大/最小的那个)和数组倒数第二个元素交换,然后再从第一个到倒数第三个元素构建最大/最小堆再交换,以此类推,直到整个数组有序。
2.代码
(请输入在-10~10的范围内,负数用整型数据
)
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
void Print_heap(float* ArrayH, int lenthH) {
printf("\n构建最大堆:\n");
int n;//层数
n = (int)(log((double)lenthH) / log((double)2)) + 1;//计算层数
printf("\n");
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= pow(2, (double)(n - 1 - i)); j++) {
printf("\t");
}
if (i == n) {//最后一行特殊处理,空半个tab
printf(" ");
}
for (int j = pow(2, (double)(i - 1)); j <= pow(2, (double)i) - 1 && j <= lenthH; j++) {
printf("%.2f", ArrayH[j]);
if (i == n) {//最后一行特殊处理,空半个tab
printf(" ");
}
else {
for (int k = 1; k <= pow(2, (double)(n - i)); k++) {
printf("\t");
}
}
}
printf("\n");
}
printf("\n");
}
void Print_heapify(float* ArrayH, int lenthH, int k, int index_max) {
printf("\n调整元素顺序:\n");
int left = 2 * k, right = 2 * k + 1;
int n;//层数
n = (int)(log((double)lenthH) / log((double)2)) + 1;//计算层数
printf("\n");
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= pow(2, (double)(n - 1 - i)); j++) {
printf("\t");
}
if (i == n) {//最后一行特殊处理,空半个tab
printf(" ");
}
for (int j = pow(2, (double)(i - 1)); j <= pow(2, (double)i) - 1 && j <= lenthH; j++) {
printf("%.2f", ArrayH[j]);
if (j == left || j == right || j == k) {
printf("#");
}
if (i == n) {//最后一行特殊处理,空半个tab
printf(" ");
}
else {
for (int k = 1; k <= pow(2, (double)(n - i)); k++) {
printf("\t");
}
}
}
printf("\n");
}
if (index_max != k)
printf("\n交换%.2f和%.2f,并继续比较%.2f及其子节点\n", ArrayH[k], ArrayH[index_max], ArrayH[k]);
else
printf("\n不需要调整\n");
}
void Print_exchange(float* ArrayH, int lenthH, int tail) {
printf("\n交换%.2f和%.2f,并继续对第1到第%d个元素排序:\n", ArrayH[1], ArrayH[tail], tail - 1);
int n;//层数
n = (int)(log((double)lenthH) / log((double)2)) + 1;//计算层数
printf("\n");
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= pow(2, (double)(n - 1 - i)); j++) {
printf("\t");
}
if (i == n) {//最后一行特殊处理,空半个tab
printf(" ");
}
for (int j = pow(2, (double)(i - 1)); j <= pow(2, (double)i) - 1 && j <= lenthH; j++) {
printf("%.2f", ArrayH[j]);
if (j == 1 || j == tail) {
printf("#");
}
if (i == n) {//最后一行特殊处理,空半个tab
printf(" ");
}
else {
for (int k = 1; k <= pow(2, (double)(n - i)); k++) {
printf("\t");
}
}
}
printf("\n");
}
printf("\n");
}
void Heapify(float* ArrayH, int lenthH, int i) {
int left = 2 * i;
int right = 2 * i + 1;
int index_max = i;
if (left <= lenthH && ArrayH[i] < ArrayH[left]) {
index_max = left;
}
if (right <= lenthH && ArrayH[index_max] < ArrayH[right]) {
index_max = right;
}
Print_heapify(ArrayH, lenthH, i, index_max);
if (index_max != i) {
int tmp = ArrayH[i];
ArrayH[i] = ArrayH[index_max];
ArrayH[index_max] = tmp;
Heapify(ArrayH, lenthH, index_max);
}
}
void Build_maxheap(float* ArrayH, int lenthH) {
for (int i = lenthH / 2; i >= 1; i--) {
Heapify(ArrayH, lenthH, i);
}
Print_heap(ArrayH, lenthH);
}
float* Heapsort(float* Array, int lenth) {
//将Array中的元素向后移动一格
float* ArrayH;
if ((ArrayH = (float*)malloc(sizeof(float) * (lenth + 1))) == NULL) {
printf("error!");
exit;
}
ArrayH[0] = lenth;
for (int i = 0; i < lenth; i++) {
ArrayH[i + 1] = Array[i];
}
//
int lenthH = lenth;
for (int j = 1; j <= lenth; j++) {
Build_maxheap(ArrayH, lenthH);
Print_exchange(ArrayH, lenthH, lenthH);
int tmp = ArrayH[lenthH];
ArrayH[lenthH] = ArrayH[1];
ArrayH[1] = tmp;
lenthH--;
}
//换回Array
for (int i = 1; i <= lenth; i++) {
Array[i - 1] = ArrayH[i];
}
//
printf("排序的结果是:\n");
printArrayF(Array, 0, lenth - 1);
return Array;
}
3.运行效果
解释:现在请发挥你的想象力把这些数字看成一个堆。首先按照数组的前后顺序从左到右、从上到下构造一个堆,然后开始调用Heapify函数维护最大堆的性质,以便最终构造一个最大堆。从堆的最后一个含有子节点的节点开始向前调整各个子树中元素的顺序,使得子树的根节点值在该棵子树中是最大的。本例从6开始,子树为{6,-9},现在比较这棵子树中的各个元素并寻找最大值放在子树的根节点处,所以6和-9的边上有“#”记号表示正在被查看。此处不需要调整顺序,所以直接向前看子树{3,0,7},此时3,0,7边上带有“#”记号表示正在被查看。很明显需要交换3和7的位置。完成交换后需要递归的对被交换的位置(即原来是7,现在是3的位置)维护调用heapify函数维护最大堆性质(因为一旦交换,则原来以7为根的子树发生了变化,需要重新调整以保证这是一个最大堆),由于这棵子树只含有{3}这一个元素,所以不需要任何调整。然后再往前到子树{-5,7,6},可以看到这三个数旁边带有“#”记号。交换-5和7后需要对原来以7为根、现在以-5为根的子树递归的维护最大堆性质。以此类推。
直到最后构建完成一个最大堆,然后交换根节点7和数组最后一个元素-9的位置,接着不考虑数组末尾的7,对剩余的元素{-9,3,6,0,5}再构建最大堆,然后重复上述步骤。
七、计数排序
1.算法简介
这种排序方法并非基于数据的两两比较,其远行时间可以缩短到O(n+k),总之是线性的,但是这以额外的空间为代价。
计数排序额外构造一个计数器数组(记为C),首先统计输入数组(记为A)中各个元素出现的次数。例如,A={0,2,3,3},则1出现1次,2出现1次,3出现2次,所以会有C[0]=1,C[1]=0,C[2]=1,C[3]=2。概括的讲,即C[A[i]]=“A[i]这个值出现的次数”(当A[i]很大时C会变得很长,所以输入别太大)。然后,做运算C[i]=C[i]+C[i-1],此时C[A[i]]=“A中数值<=A[i]的元素的个数”(这一步可能有点抽象),例如C[0]=1,即A中<=0的元素有1个,C[3]=4,即A中<=3的元素有4个。最后再构造一个数组B,用来存储最终的结果。B的构造思路如下:假设A[i]=x,C[x]=y,则A中共有y个数<=x,所以在升序排列的数组中,x应当位于第y个位置。例如,A中有1个数<=0,所以0位于第一位。对于重复的2个3来说,一开始A中有4个元素<=3,所以3位于第4位。完成这一步后将C[3]-1,所以下一个3的C[3]=3,则下一个3位于第3位,这样保证重复的数据不会冲突。
看完上述解释,你可能会觉得这个算法比较抽象。所以笔者附上伪代码,将抽象进行到底!
(注:伪代码来源麻省理工学院开放课程.算法导论.05.线性时间排序_哔哩哔哩_bilibili)
for i <- 0 to n
C[i]=0
for j <- 1 to n
C[A[j]] <- C[A[j]] + 1
for j <- 1 to n
C[j] <- C[j] + 1
for j <- n downto 1
B[C[A[j]]] <- A[j]
C[A[j]] <- C[A[j]] - 1
2.代码
(请输入-10~10的整型数据
)
#include<stdio.h>
#include<stdlib.h>
int* Countingsort(int* Array, int lenth) {
int* C, * B;
int i, max, min, move_lenth = 0;
for (i = 1, max = min = Array[0]; i < lenth; i++) {
if (max < Array[i])
max = Array[i];
if (min > Array[i])
min = Array[i];
}
//把A中的数都变为正整数,并记录移动的值
if (min < 0) {
move_lenth = 0 - min;
for (i = 0; i < lenth; i++) {
Array[i] = Array[i] + move_lenth;
}
max = max + move_lenth;
printf("\n为排序需要,将所有数+%d,使最小的数>=0\n", move_lenth);
}
//
if ((C = (int*)malloc(sizeof(int) * (max + 1))) == NULL) {
printf("NULL point C!\n");
exit;
}
for (i = 0; i <= max; i++) {
C[i] = 0;//初始化计数器
}
//打印初始化的计数器
printf("\n计数器初始化:\n");
for (i = 0; i <= 2; i++) {
printf("C[%d]\t", i);
}
printf("……\t");
for (i = max - 1; i <= max; i++) {
printf("C[%d]\t", i);
}
printf("\n");
for (i = 0; i <= 2; i++) {
printf("0\t");
}
printf("……\t");
for (i = max - 1; i <= max; i++) {
printf("0\t");
}
printf("\n");
//
printf("\n计数:\n");
for (i = 0; i < lenth; i++) {
C[Array[i]]++;//计数
//打印
printf("\n");
for (int j = 1; j <= i; j++) {
printf("\t");
}
printf("*\n");
printArrayI(Array, 0, lenth - 1);
printf("C[%d] = %d\n", Array[i], C[Array[i]]);
//
}
//打印计数结果
printf("\n计数结果:(…表示被省略的部分数值为0)\n");
for (int j = 0; j <= max; j++) {
//打印数组符号
if (C[j] == 0 && j == 0) {
printf("…\t");
}
if (C[j] == 0 && j >= 1 && C[j - 1] != 0) {
printf("…\t");
}
if (C[j] != 0) {
printf("C[%d]\t", j);
}
//
//换行并打印数字
int minus;
if (j != 0 && (j + 1) % 10 == 0 || j == max) {
printf("\n");
if (j != 0 && (j + 1) % 10 == 0) {
minus = 9;
}
else if (j == max) {
minus = max % 10;
}
for (int k = j - minus; k <= j; k++) {
if (C[k] == 0 && k == 0) {
printf("…\t");
}
if (C[k] == 0 && k >= 1 && C[k - 1] != 0) {
printf("…\t");
}
if (C[k] != 0) {
printf("%d\t", C[k]);
}
}
printf("\n");
}
//
}
//
printf("\n基于上述计数结果,可以得到排序思路:\n");
for (i = 1; i <= max; i++) {
C[i] = C[i] + C[i - 1];//累加
//打印
printf("\n");
if (i - 1 == 0) {
for (int z = 0; z < lenth; z++)
if (i == Array[z]) {
printf("比%d小的数有%d个,在排序后的数组中%d应该从第%d位开始", i, C[i - 1], i, C[i - 1] + 1);
break;
}
if (C[i] - C[i - 1] > 1) {
printf(",一共有%d个%d,则最后一个%d应该在第%d位\n", C[i] - C[i - 1], i, i, C[i]);
}
else {
printf("\n");
}
}
else if(i - 1 > 0) {
for(int z = 0; z < lenth; z++)
if (i == Array[z]) {
printf("令C[%d]=C[%d]+C[%d]\n", i - 1, i - 1, i - 2);
printf("比%d小的数有%d个,在排序后的数组中%d应该从第%d位开始", i, C[i - 1], i, C[i - 1] + 1);
break;
}
if (C[i] - C[i - 1] > 1) {
printf(",一共有%d个%d,则最后一个%d应该在第%d位\n", C[i] - C[i - 1], i, i, C[i]);
}
else {
printf("\n");
}
}
//
}
//打印累加结果
int InArray;
printf("\n数组C的累加结果:(!下的值含有Array(可能被调整过以保证最小元素>=0)中元素的位置信息)\n");
for (int j = 0; j <= max; j++) {
//打印!
InArray = 0;
for (int a = 0; a < lenth; a++) {
if (Array[a] == j) {
InArray = 1;
break;
}
}
if (InArray == 0) {
printf("\t");
}
else if(InArray == 1) {
printf("!\t");
}
//
int minus;
if (j != 0 && (j + 1) % 10 == 0 || j == max) {
printf("\n");
if (j != 0 && (j + 1) % 10 == 0) {
minus = 9;
}
else if (j == max) {
minus = max % 10;
}
//打印数组符号
for (int k = j - minus; k <= j; k++) {
printf("C[%d]\t", k);
}
//
printf("\n");
//打印数字
for (int k = j - minus; k <= j; k++) {
printf("%d\t", C[k]);
}
//
printf("\n");
}
}
if ((B = (int*)malloc(sizeof(int) * lenth)) == NULL) {
printf("NULL point B!");
exit;
}
printf("\n构造数组B存储结果:\n");
for (i = lenth - 1; i >= 0; i--) {
B[C[Array[i]] - 1] = Array[i] - move_lenth;
C[Array[i]]--;
//打印
printf("\n");
printf("Array[%d]=%d\t", i, Array[i]);
printf("C[%d]=%d\n", Array[i], C[Array[i]]);
printf("令B[%d]=%d,", C[Array[i]], Array[i]);
printf("C[%d]=%d-1=%d\n", Array[i], C[Array[i]], C[Array[i]] - 1);
//
}
printf("排序的数组是:\n");
printArrayI(B, 0, lenth - 1);
return B;
}
3.运行效果
解释:由于数组的下标不能为负数,所以在排序之前,会先自动检测输入的数据中有无负数,如果有,则将所有数据加上最小负数的相反数,使得最小的数>=0。然后就是计数器初始化与计数,C[i]的意义就是i出现过几次。然后C[1]=C[1]+C[0]=1,则<=1的数据有1个,即升序中2应该排在第二位,其余同理。文中“数组C的累加结果”即各个C[i]如上式求和后的结果,上面标有!的表示数组下标的值包含在Array中,下一步构造数组B要用到。构造B的过程以第一个为例,A[3]=2且2在A中只出现了一次,所以计数器对应的有C[2]=1,即A中<=2的数有1个,2放在第一位,即B[1]=2,之后把C[2]-1防止重复。其余同理。
八、基数排序
1.算法简介
“基数排序是非比较型整数排序算法,其原理是将整数按位分割进行排序。基数排序适用于大范围数据排序,打破了计数排序的限制。”(《数据结构算法———基数排序》,作者小白~夏秋~)
基数排序有最低位优先法(从最低位向最高位按位排序)和最高位优先法(从最高位向最低位按位排序),思路都是先将所有数据按某一位分组,这一位上的数字相同的放在同一组,之后再按照该位的大小把数据整合到一个数组内。然后依次对所有位进行该操作。
2.代码
(请输入非负整数)
#include<stdio.h>
#include<stdlib.h>
typedef struct NODE {
int value;
struct NODE* next;
}node;
int* Radixsort(int* Array, int lenth) {
printf("\n最低位优先排序\n");
node** HEADS, **TAILS, **POS;
node* p;
int max = Array[0], M = 1, num = 1;
if ((HEADS = (node**)malloc(sizeof(node*) * 10)) == NULL) {//十个数字
printf("error!");
exit;
}
if ((TAILS = (node**)malloc(sizeof(node*) * 10)) == NULL) {//十个数字
printf("error!");
exit;
}
for (int i = 1; i < lenth; i++) {//找到最大值
if (Array[i] > max)
max = Array[i];
}
while (M < max) {
for (int i = 0; i < 10; i++) {//HEADS链表初始化
if ((HEADS[i] = (node*)malloc(sizeof(node))) == NULL) {
printf("error!");
exit;
}
HEADS[i] = NULL;
}
for (int i = 0; i < 10; i++) {//TAILS链表初始化
if ((TAILS[i] = (node*)malloc(sizeof(node))) == NULL) {
printf("error!");
exit;
}
TAILS[i] = NULL;
}
for (int i = 0; i < lenth; i++) {//对某位排序
if ((p = (node*)malloc(sizeof(node))) == NULL) {
printf("error!");
exit;
}
p->value = Array[i];
p->next = NULL;
int index = ((Array[i] / M) % 10);
if (HEADS[index] == NULL) {
HEADS[index] = TAILS[index] = p;
}
else {
TAILS[index] = TAILS[index]->next = p;
}
}
//打印
printf("\n对从右向左第%d位排序:\n", num);
printf("按位分组:\n");
for (int i = 0; i <= 9; i++) {
printf("********");
}
printf("\n");
for (int i = 0; i <= 9; i++){
printf("%d\t", i);
}
printf("\n");
for (int i = 0; i <= 9; i++) {
printf("********");
}
printf("\n");
if ((POS = (node**)malloc(sizeof(node*) * 10)) == NULL) {//十个数字
printf("error!");
exit;
}
for (int i = 0; i < 10; i++) {//POS链表初始化
if ((POS[i] = (node*)malloc(sizeof(node))) == NULL) {
printf("error!");
exit;
}
POS[i] = HEADS[i];
}
int printed_num = 0;
while (printed_num != lenth) {
for (int i = 0; i < 10; i++) {
if (POS[i] != NULL) {
printf("%d\t", POS[i]->value);
POS[i] = POS[i]->next;
printed_num++;
}
else {
printf("\t");
}
}
printf("\n");
}
printf("\n");
//
for (int i = 0, index = 0; i < lenth; index++) {//把排好序的结果存入Array
node* head, * tail = HEADS[index];
while (tail != NULL) {
Array[i++] = tail->value;
head = tail;
tail = tail->next;
free(head);
}
}
printf("\n按位排序的结果是:\n");
printArrayI(Array, 0, lenth - 1);
M = M * 10;
num++;
}
return Array;
}
3.运行效果
解释:最低位优先法排序。按个位上的数字把数据分为10组,然后从最低位上数值最小的一组(0那一组)开始按组把数据取出,得到第一步的按位排序结果。之后再按照十位排序,再按照百位排序,……
九、桶排序
1.算法简介
桶排序就是把数据按一定的方法分组(放到不同的桶里面),这一步需要保证位于前面的桶中的数据更小,位于后面的桶中的数据更大。然后在桶内进行排序,最后将桶内数据依次取出构成最终的有序数列。
2.代码
#include<stdio.h>
#include<stdlib.h>
float* Bucketsort(int* Array, int head, int end) {
int lenth = end - head + 1;//数组长度
int max = Array[head], min = Array[head], num_bucket;
int** bucket;
int *each_bucket_size;
int size;
for (int i = head + 1; i <= end; i++) {//找最大、最小值
if (Array[i] > max) {
max = Array[i];
}
else if (Array[i] < min) {
min = Array[i];
}
}
size = (max - min) / lenth + 1;//桶的大小
num_bucket = (max - min) / size + 1;//桶的数量
//打印
printf("\n桶的大小:%d\n", size);
printf("一共%d个桶\n", num_bucket);
//
if ((bucket = (int**)malloc(sizeof(int*) * num_bucket)) == NULL) {//构造桶
printf("error!No space!");
exit;
}
for (int i = 0; i < num_bucket; i++) {//给桶分配空间
if ((bucket[i] = (int*)malloc(sizeof(int) * size)) == NULL) {
printf("error!No space!");
exit;
}
}
if ((each_bucket_size = (int*)malloc(sizeof(int) * num_bucket)) == NULL) {//每个桶放了多少个数
printf("error!No space!");
exit;
}
for (int i = 0; i < num_bucket; i++) {//初始化,每个桶现在有0个数
each_bucket_size[i] = 0;
}
for (int i = head; i <= end; i++) { //向桶内分配数据
int index = (Array[i] - min) / size;
bucket[index][each_bucket_size[index]++] = Array[i];
}
//打印
printf("\n");
for (int i = 0; i <= size; i++) {
for (int j = 0; j < num_bucket; j++) {
if (i == size) {
printf("|_");
if (i < each_bucket_size[j]) {
printf("%3d", bucket[j][i]);
}
else {
printf(" ");
}
printf("_| ");
}
else {
printf("| ");
if (i < each_bucket_size[j]) {
printf("%3d", bucket[j][i]);
}
else {
printf(" ");
}
printf(" | ");
}
}
printf("\n");
}
//
for (int i = 0; i < num_bucket; i++) {//对每个桶快速排序
Qsort_I(&(bucket[i]), 0, each_bucket_size[i] - 1);
}
//打印
printf("\n");
printf("在桶内排序:\n");
for (int i = 0; i <= size; i++) {
for (int j = 0; j < num_bucket; j++) {
if (i == size) {
printf("|_");
if (i < each_bucket_size[j]) {
printf("%3d", bucket[j][i]);
}
else {
printf(" ");
}
printf("_| ");
}
else {
printf("| ");
if (i < each_bucket_size[j]) {
printf("%3d", bucket[j][i]);
}
else {
printf(" ");
}
printf(" | ");
}
}
printf("\n");
}
printf("\n从前往后、从上往下逐个桶取出数据得到有序数组\n");
//
//从桶中取数
int index_array = 0;
for (int i = 0; i < num_bucket; i++) {
for (int j = 0; j < each_bucket_size[i]; j++) {
Array[index_array++] = bucket[i][j];
}
}
printf("排序结果为:\n");
printArrayI(Array, head, end);
return Array;
}
3.运行效果
解释:这个应该比较形象。另外,图中桶的大小相对于其中的数据来说有些太大,看上去不是很合理,这是因为笔者使用的最简易的方式进行桶的构造。桶的构造对该算法的性能有较大的影响。
十、希尔排序
1.算法简介
希尔排序是插入排序的改进版本,先设定一个gap值(间隔长度),将数组按照这个gap进行分组,在每一组内部使用插入排序,然后再对排序结果进行整合。随后不断缩小gap,并重复分组排序的步骤,直到gap=1并对整个数组完成排序。
2.代码
#include<stdio.h>
float* Shellsort(float* Array, int lenth) {
int gap = lenth / 2, i;
for (i = 2; gap >= 1; i = i * 2, gap = lenth / i) {
//打印
printf("\n分组:\n");
for (int i = 0; i < gap; i++) {
for (int j = 0; j < lenth; j = j + 1) {
if ((j - i) % gap == 0) {
printf("%.2f\t", Array[j]);
}
else {
printf("\t");
}
}
printf("\n");
for (int k = 0; k <= lenth * 8; k++) {
printf("*");
}
printf("\n");
}
printf("\n");
//
for (int k = gap; k < lenth; k = k + 1) {
int tmp = Array[k];
int pos = k;
for (int j = k - gap; ; j = j - gap) {
if (j < 0) {
Array[pos] = tmp;
break;
}
else {
if (Array[j] > tmp) {
Array[j + gap] = Array[j];
pos = j;
}
}
}
}
//打印
printf("\n分组排序:\n");
for (int i = 0; i < gap; i++) {
for (int j = 0; j < lenth; j = j + 1) {
if ((j - i) % gap == 0) {
printf("%.2f\t", Array[j]);
}
else {
printf("\t");
}
}
printf("\n");
for (int k = 0; k <= lenth * 8; k++) {
printf("*");
}
printf("\n");
}
printf("\n");
printf("排序后:\n");
printArrayF(Array, 0, lenth - 1);
printf("\n");
//
}
printf("排好序的数组是:\n");
printArrayF(Array, 0, lenth - 1);
return Array;
}
3.运行效果
解释:首先以gap=3分组,然后组内排序并整合,再以gap=1分组、排序、整合,即得到最终结果。
十一、其它的一些函数
除了上述排序用的函数以外,本程序还写了了一些其它功能的函数进行功能上的辅助,如菜单显示、打印数组等等,一并列在下面。
1.主函数
#include<stdio.h>
#include<stdlib.h>
//显示菜单
void menu();
//构建数组
float* MakeSortArrayF(float*, int*);
int* MakeSortArrayI(int*, int*);
//打印数组
void printArrayF(float*, int, int);
void printArrayI(int*, int, int);
void printArrayF_no_enter(float*, int, int);
//十种排序
float* Insertionsort(float*, int);
float* Bubblesort(float*, int);
float* Selectionsort(float*, int);
float* Quicksort(float*, int, int);
float* Mergesort(float*, int, int);
float* Heapsort(float*, int);
int* Countingsort(int*, int);
int* Radixsort(int*, int);
int* Bucketsort(int*, int, int);
float* Shellsort(float*, int);
//辅助函数
int* Qsort_I(int**, int, int);
void Build_maxheap(float*, int);
void Heapify(float*, int, int);
void Print_heap(float*, int);
void Print_heapify(float*, int, int, int);
void Print_exchange(float*, int, int);
int main() {
int c = 0;
int lenth = 0;//记录输入数组的长度
int* SortArrayI = (int*)malloc(sizeof(int)*100);//整形数据输入
float* SortArrayF = (float*)malloc(sizeof(float)*100);//浮点型数据输入
menu();
printf("选择序号:");
scanf_s("%d", &c);//初始化显示
while (1) {//主判断程序
if (c == 12)
break;
switch (c) {
case 1: SortArrayF = MakeSortArrayF(SortArrayF, &lenth);
SortArrayF = Insertionsort(SortArrayF, lenth);
break;
case 2:SortArrayF = MakeSortArrayF(SortArrayF, &lenth);
SortArrayF = Bubblesort(SortArrayF, lenth);
break;
case 3:SortArrayF = MakeSortArrayF(SortArrayF, &lenth);
SortArrayF = Selectionsort(SortArrayF, lenth);
break;
case 4:SortArrayF = MakeSortArrayF(SortArrayF, &lenth);
printf("食用方法:*下方的数表示分割标志位,?表示正在遍历并查看是否需要和标志位上的数交换,&表示下方的数需要和标志位交换。\n");
SortArrayF = Quicksort(SortArrayF, 0, lenth - 1);
printf("排序后的数组为:\n");
printArrayF(SortArrayF, 0, lenth - 1);
break;
case 5:SortArrayF = MakeSortArrayF(SortArrayF, &lenth);
printf("食用方法:(采用升序排列)在合并被分割的两个数组时,用?表示对比a、b两数组中当前的数\
,并取最小的数,这一结果显示在“逐步展示合并后的数组”一行。上方有*标记的数组表示已经对整个数组完成处理,所以直接把另一个数组的数加入合并后的数组即可\n");
SortArrayF = Mergesort(SortArrayF, 0, lenth - 1);
printf("排序后的数组为:\n");
printArrayF(SortArrayF, 0, lenth - 1);
break;
case 6:SortArrayF = MakeSortArrayF(SortArrayF, &lenth);
printf("\n#表示需要比较带有这个标记的数,并选出最大的数\n");
SortArrayF = Heapsort(SortArrayF, lenth);
break;
case 7:SortArrayI = MakeSortArrayI(SortArrayI, &lenth);
SortArrayI = Countingsort(SortArrayI, lenth);
break;
case 8:SortArrayI = MakeSortArrayI(SortArrayI, &lenth);
SortArrayI = Radixsort(SortArrayI, lenth);
break;
case 9:SortArrayI = MakeSortArrayI(SortArrayI, &lenth);
SortArrayI = Bucketsort(SortArrayI, 0, lenth - 1);
break;
case 10:SortArrayF = MakeSortArrayF(SortArrayF, &lenth);
SortArrayF = Shellsort(SortArrayF, lenth);
break;
case 11:menu();
break;
default:printf("输入正确的值!\n");
}
printf("选择序号:");
getchar();
scanf_s("%d", &c);
}
return 0;
}
2.输入数据并构造浮点型数组
#include<stdio.h>
//构建浮点型数组存储输入数据
float* MakeSortArrayF(float* Array, int* p_lenth) {
float input;
*p_lenth = 0;//指向i,记录数组长度,先做初始化
printf("逐个输入数据,每个数据后接回车,随便扣一个字母结束:\n");
while (scanf_s("%f", &input)) {
Array[(*p_lenth)++] = input;
getchar();
}
if (*p_lenth == 0) {
printf("空输入!\n");
}
else {
printf("输入的数组是:\n");
printArrayF(Array, 0, *p_lenth - 1);
}
return Array;
}
3.输入数据并构造整型数组
#include<stdio.h>
//构建整型数组存储输入数据
int* MakeSortArrayI(int* Array, int *p_i) {
int input;
*p_i = 0;//指向i,记录数组长度,先做初始化
printf("逐个输入数据(整数),每个数据后接回车,随便扣一个字母结束:\n");
while (scanf_s("%d", &input)) {
Array[(*p_i)++] = input;
getchar();
}
if (*p_i == 0) {
printf("空输入!\n");
}
else {
printf("输入的数组是:\n");
for (int j = 0; j < *p_i; j++) {
printf("%d\t", Array[j]);
}
printf("\n");
}
return Array;
}
4.显示菜单
#include<stdio.h>
void menu() {
printf("***************************** 菜单 *****************************\n");
printf("1.插入排序\n");
printf("2.冒泡排序\n");
printf("3.选择排序\n");
printf("4.快速排序\n");
printf("5.归并排序\n");
printf("6.堆排序\n");
printf("7.计数排序\n");
printf("8.基数排序\n");
printf("9.桶排序\n");
printf("10.希尔排序\n");
printf("11.显示菜单\n");
printf("12.退出\n");
printf("**************************************************************\n");
}
5.打印浮点型数组
#include<stdio.h>
void printArrayF(float* Array, int head, int tail) {//head、tail是下标
if (tail < head) {
printf("Empty!\n");
return;
}
for (int print_i = head; print_i <= tail; print_i++) {
printf("%.2f\t", Array[print_i]);
}
printf("\n");
return;
}
6.打印浮点型数组(结尾不加换行符)
#include<stdio.h>
void printArrayF_no_enter(float* Array, int head, int tail) {//head、tail是下标
if (tail < head) {
printf("Empty!\n");
return;
}
for (int print_i = head; print_i <= tail; print_i++) {
printf("%.2f\t", Array[print_i]);
}
return;
}
7.打印整型数组
#include<stdio.h>
void printArrayI(int* Array, int head, int tail) {//head、tail是下标
if (tail < head) {
printf("Empty!\n");
return;
}
for (int print_i = head; print_i <= tail; print_i++) {
printf("%d\t", Array[print_i]);
}
printf("\n");
return;
}
8.桶排序内部使用的快速排序
#include<stdio.h>
int* Qsort_I(int** Array, int head, int end) {//head和end是头和尾的下标
if (head >= end)
return;
int M = (head + end) / 2;//M是分割标志位
int key = (*Array)[M];//暂存标志位的值
int tmp;
int left = head, right = end;//左右指针
while (left < right) {
while (left < right && (*Array)[right] > key) {//移动右指针
right--;
}
if (left < right) {//交换
tmp = (*Array)[right];
(*Array)[right] = (*Array)[M];
(*Array)[M] = tmp;
M = right;
}
while (left < right && (*Array)[left] <= key) {//移动左指针
left++;
}
if (left < right) {//交换
tmp = (*Array)[left];
(*Array)[left] = (*Array)[M];
(*Array)[M] = tmp;
M = left;
}
}
(*Array)[M] = key;//放回标志位的值
Qsort_I(Array, head, M - 1);//左侧递归
Qsort_I(Array, M + 1, end);//右侧递归
return;
}