【C语言数据结构】排序

1.排序的概念

在深入研究各个排序算法之前,首先,我们要对排序有个大概的了解,即与排序相关的一些概念

Q:什么是排序?

A:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作

Q:什么是内部排序与外部排序?

A:内部排序:数据元素全部放在内存中的排序

    外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序

Q:稳定性是什么?

A:稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变

即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的

那关于排序的应用,这可太多了,可以说我们每天都在跟它打交道,这里就不展开了,接下来我们就来看看各个排序算法吧~

2.常见的排序算法的实现

a.插入排序

1.直接插入排序

直接插入排序是一种简单的插入排序法,其基本思想是:
把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

 

当插入第i(i>=1)个元素时,前面的a[0],a[1],…,a[i-1]已经排好序,此时用a[i]的排序码与a[i-1],a[i-2],…的排序码顺序进行比较,找到插入位置即将a[i]插入,原来位置上的元素顺序后移


直接插入排序代码实现

void PrintArray(int* a, int n)//用来辅助打印的函数 
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}void InsertSort(int* a, int n)  //直接插入排序
{for (int i = 0; i <= n - 2; i++){int end = i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}void TestInsertSort()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };InsertSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));//打印
}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestInsertSort();return 0;
}

直接插入排序的特性总结:
1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1),它是一种稳定的排序算法
4. 稳定性:稳定

 

2.希尔排序

希尔排序法又称缩小增量法希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成整数个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序
 

希尔排序代码实现

void PrintArray(int* a, int n)//用来辅助打印的函数 
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}void ShellSort(int* a, int n)//希尔排序
{int gap = n;while (gap > 1){gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++){int end = i;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}void TestShellSort()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };ShellSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));//打印
}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestShellSort();return 0;
}

希尔排序的特性总结:
1. 希尔排序是对直接插入排序的优化。
2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定

4.稳定性:不稳定
 

b.选择排序

1.直接选择排序

每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完

下面讲解一下这种排序的步骤:

1.在元素集合a[i] 到 a[n-1]中选择关键码最小的数据元素

2.若它(最小数据元素)不是这组元素中的第一个元素,则将它与这组元素中的第一个元素交换

3.在剩余的a[i+1] 到 a[n-1]集合中重复上述步骤,直到集合剩余1个元素

直接选择排序代码实现

void Swap(int* p1, int* p2)//交换
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void PrintArray(int* a, int n)//用来辅助打印的函数 
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}void SelectSort(int* a, int n)//直接选择排序
{int begin = 0, end = n - 1;while (begin < end){//  这里进行优化 ,每次都选出最小值和最大值两个值的下标而不是只选出最小值int mini = begin, maxi = begin;for (int i = begin + 1; i <= end; i++){if (a[i] < a[mini]){mini = i;}if (a[i] > a[maxi]){maxi = i;}}Swap(&a[begin], &a[mini]);if (maxi == begin)//重叠问题{maxi = mini;}Swap(&a[end], &a[maxi]);begin++;end--;}
}void TestSelectSort()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };SelectSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));//打印
}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestSelectSort();return 0;
}

直接选择排序的特性总结:
1. 直接选择排序思考非常好理解,但是效率不是很好,所以实际中很少使用
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

 

2.堆排序

关于什么是堆:堆是一颗完全二叉树,这里的具体知识要结合二叉树部分进行讲解,就不是这篇博客的重点了,所以建议学完二叉树的小伙伴再来观看关于堆排序的内容哦~

堆排序是指利用堆这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。


 

下面是堆排序的具体步骤

1.建堆,需要注意的是排升序要建大堆,排降序建小堆

2.将最后一个数与堆顶进行交换,然后将除最后一个数据之外的所有数据重新向下调整

3.重复第2步,直至完全升序

堆排序代码实现

void Swap(int* p1, int* p2)//交换
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void PrintArray(int* a, int n)//用来辅助打印的函数 
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}//向下调整 升序要建大堆
void AdjustDown(int* a, int n, int parent)
{int child = parent * 2 + 1;while (child < n){// 假设法,选出左右孩子中大的那个孩子if (child + 1 < n && a[child + 1] > a[child]){child++;}if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(int* a, int n)
{// a数组直接建堆 O(N)for (int i = (n - 1 - 1) / 2; i >= 0; --i){AdjustDown(a, n, i);}int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}
}void TestHeapSort()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };HeapSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));//打印
}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestHeapSort();return 0;
}

堆排序的特性总结:
1. 堆排序使用堆来选数,效率就高了很多。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(1)
4. 稳定性:不稳定

 

c.交换排序

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动
 

1.冒泡排序

冒泡排序,这是排序中我们最早接触的算法,这个排序的取名很形象,所以思想也很简单

冒泡排序代码实现

void Swap(int* p1, int* p2)//交换
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}void PrintArray(int* a, int n)//用来辅助打印的函数 
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}void BubbleSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int flag = 0;for (int j = 1; j < n - i; j++){if (a[j - 1] > a[j]){Swap(&a[j - 1], &a[j]);flag = 1;}}if (flag == 0)break;}
}void TestBubbleSort()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };BubbleSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));//打印
}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestBubbleSort();return 0;
}

冒泡排序的特性总结:
1. 冒泡排序是一种非常容易理解的排序
2. 时间复杂度:O(N^2)
3. 空间复杂度:O(1)
4. 稳定性:稳定

 

2.快速排序

快速排序是Hoare大佬提出的一种二叉树结构的交换排序方法,其基本思想为:

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止

快速排序是排序里的一块重点内容,也是面试容易考察的一种排序,所以下面我们重点来讲解一下这个排序~
 

版本一:hoare版本

代码实现如下:y

void QuickSort1(int* a, int left, int right)//快排hoare版本
{if (left >= right)// 区间只有一个值或者不存在就是最小子问题return;int begin = left, end = right;int keyi = left;while (left < right){// right先走,找小while (left < right && a[right] >= a[keyi]){right--;}// left再走,找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}//出循环的时候left = right,相遇Swap(&a[left], &a[keyi]);keyi = left;//keyi来到相遇点// [begin, keyi-1]  keyi   [keyi+1, end] 左右进行递归QuickSort1(a, begin, keyi - 1);QuickSort1(a, keyi + 1, end);
}void TestQuickSort1()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };QuickSort1(a, 0, sizeof(a) / sizeof(int) - 1);PrintArray(a, sizeof(a) / sizeof(int));
}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestQuickSort1();return 0;
}

对于hoare版本,我们有两点可以优化,分别是

1.选key的优化

2.小区间优化

先说第一个,选key的优化

我们可以采用三数取中法

所谓三数取中法,就是我们在区间的第一个,中间位置,和最后一个位置中选出中间值作为key,下面来写一下

int GetMidi(int* a, int left, int right)// 三数取中优化  left  mid  right
{int mid = (left + right) / 2;if (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right]){return left;}else{return right;}}else // a[left] > a[mid]{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]){return left;}else{return right;}}
}

然后我们可以把这个函数嵌入之前的快排程序了~

void QuickSort1(int* a, int left, int right)//快排hoare版本优化1.0
{if (left >= right)// 区间只有一个值或者不存在就是最小子问题return;int begin = left, end = right;// 三数取中int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int keyi = left;while (left < right){...//为了节约空间,这里就不重复写了~}//出循环的时候left = right,相遇Swap(&a[left], &a[keyi]);keyi = left;//keyi来到相遇点// [begin, keyi-1]  keyi   [keyi+1, end] 左右进行递归QuickSort1(a, begin, keyi - 1);QuickSort1(a, keyi + 1, end);
}

再说一下第二个,小区间优化

因为递归要开辟栈帧,越到后面消耗越大,所以当递归到小的子区间时,我们可以使用插入排序~

插入排序的代码上面写过了,我们之间拿来用就行~,下面是优化后的代码

void QuickSort1(int* a, int left, int right)//快排hoare版本优化2.0
{if (left >= right)// 区间只有一个值或者不存在就是最小子问题return;if (right - left + 1 < 10)// 小区间选择走插入,可以减少90%左右的递归,一般是区间长度小于10比较合理{InsertSort(a + left, right - left + 1);}else{int begin = left, end = right;// 三数取中int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int keyi = left;while (left < right){...//同样为了省空间。这里就不写了}//出循环的时候left = right,相遇Swap(&a[left], &a[keyi]);keyi = left;//keyi来到相遇点// [begin, keyi-1]  keyi   [keyi+1, end] 左右进行递归QuickSort1(a, begin, keyi - 1);QuickSort1(a, keyi + 1, end);}
}

好,说完了hoare版本以及优化,

下面我们看一下快速排序的第二个版本:挖坑法

其实挖坑法的整体思路与hoare法类似,博主倾向于认为它是hoare版本的一个教学优化版

下面是挖坑法的过程图:

下面是代码:(注意,接下来的几个版本其实都是可以用上面的三数取中和小区间优化,这里博主就不再多写了,毕竟是一样的东西)

void QuickSort2(int* a, int left, int right)//快排挖坑法,其实是hoare版本的教学优化版
{if (left >= right)return;int begin = left, end = right;int key = a[left];int hole = left;while (left < right){while (left < right && a[right] >= key){right--;}a[hole] = a[right];hole = right;while (left < right && a[left] <= key){left++;}a[hole] = a[left];hole = left;}a[hole] = key;QuickSort2(a, begin, hole - 1);QuickSort2(a, hole + 1, end);
}void TestQuickSort2()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };QuickSort2(a, 0, sizeof(a) / sizeof(int) - 1);PrintArray(a, sizeof(a) / sizeof(int));
}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestQuickSort2();return 0;
}

版本三:前后指针法

这种方法博主认为理解后是这些快排里代码最好写的,如果面试时要求手写一个快排代码,用前后指针法绝对是一个不二的选择!

下面是前后指针法的图示过程

下面是代码:(同样,这里的优化也不加了)

void QuickSort3(int* a, int left, int right)// 前后指针法
{if (left >= right)return;int keyi = left;int prev = left;int cur = left + 1;while (cur <= right){if (a[cur] < a[keyi])  //cur < key , prev++ , 交换prev与cur的位置{ prev++;Swap(&a[prev], &a[cur]);}cur++;}Swap(&a[keyi], &a[prev]);keyi = prev;//将key的下标换到prev// [left, keyi-1] keyi [keyi+1, right]	左右进行递归QuickSort3(a, left, keyi - 1);QuickSort3(a, keyi + 1, right);
}void TestQuickSort3()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };QuickSort3(a, 0, sizeof(a) / sizeof(int) - 1);PrintArray(a, sizeof(a) / sizeof(int));
}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestQuickSort3();return 0;
}

最后一种:  非递归实现

将快排本来要用的递归变成非递归,这也是面试是喜欢考的东西

这里我们需要用栈的数据结构,因为本来递归用的是栈帧,而非递归用的是数据结构的栈

因为在C语言阶段,我们也只能自己手写,到了C++阶段就有现成的可以使用咯,这里我们CV一下(doge)

非递归的步骤是:    取栈顶区间,单趟排序,右左子区间入栈

#include"Stack.h"void QuickSortNonR(int* a, int left, int right)//快排非递归
{ST st;STInit(&st);// 取栈顶区间,保持先入右,再入左STPush(&st, right);STPush(&st, left);while (!STEmpty(&st))//栈不为空,还有区间要处理,就继续{int begin = STTop(&st);STPop(&st);int end = STTop(&st);STPop(&st);// 单趟排序int keyi = begin;int prev = begin;int cur = begin + 1;while (cur <= end){if (a[cur] < a[keyi] && ++prev != cur)Swap(&a[prev], &a[cur]);cur++;}Swap(&a[keyi], &a[prev]);keyi = prev;// [begin, keyi-1] keyi [keyi+1, end] //右左子区间入栈if (keyi + 1 < end){  STPush(&st, end);STPush(&st, keyi + 1);}if (begin < keyi - 1){STPush(&st, keyi - 1);STPush(&st, begin);}}STDestroy(&st);
}void TestQuickSortNonR()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };QuickSortNonR(a, 0, sizeof(a) / sizeof(int) - 1);PrintArray(a, sizeof(a) / sizeof(int));}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestQuickSortNonR();return 0;
}

好了,至此,我们的快速排序的各个方法也就讲完啦,下面小小的进行一个总结

快速排序的特性总结:
1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(logN)
4. 稳定性:不稳定

 

讲完了快速排序,再来看一下归并排序吧~

d.归并排序

归并排序是建立在归并操作上的一种有效的排序算法,它也是采用分治法的一个非常典型的应用

将已有序的子序列合并,得到完全有序的序列 ,即先使每个子序列有序,再使子序列段间有序

归并排序核心步骤:
 

下面是归并排序的动态图示过程:

归并排序的实现可分为递归与非递归两种类型,我们先看一下递归的实现

//归并排序子函数
void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin == end)return;int mid = (begin + end) / 2;// [begin, mid] [mid+1, end]_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid + 1, end, tmp);// 下面是归并int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;// 依次比较,取小的尾插到tmp数组while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}//归并排序 
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}_MergeSort(a, 0, n - 1, tmp);free(tmp);tmp = NULL;
}void TestMergeSort()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };MergeSortNonR(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestMergeSort();return 0;
}

其实这里用递归的方式写还是比较轻松的,毕竟我们之前学了二叉树,真正难的是下面的非递归

这里的非递归与之前的快速排序不同,我们如果用一个栈就不太好操作了

于是我们想着用一个变量gap来归并每组的数据个数,让gap从1到2到4一直增长直到这个数据排到有序为止

我们可能会写出下面的代码:


void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;//gap是每组的数据个数while (gap < n){for (int j = 0; j < n; j += 2 * gap){int begin1 = j, end1 = begin1 + gap - 1;int begin2 = begin1 + gap, end2 = begin2 + gap - 1;int i = j;// 依次比较,取小的尾插到tmp数组while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + j, tmp + j, sizeof(int) * (2*gap));}gap *= 2;}free(tmp);tmp = NULL;
}

我们测试上面的代码,会得到这样的结果:

这里其实说明我们的数组越界了,这是怎么回事呢?

我们看一下下面这个数组,当gap = 2 的时候,它要怎么归并呢?

这里是不是就出现了越界,所以为了阻止这个问题,我们需要增加一处地方以及修改一处地方

void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){return;}int gap = 1;//gap是每组的数据个数while (gap < n){for (int j = 0; j < n; j += 2 * gap){int begin1 = j, end1 = begin1 + gap - 1;int begin2 = begin1 + gap, end2 = begin2 + gap - 1;int i = j;// 这里增加了对越界的问题处理if (end1 >= n || begin2 >= n)break;if (end2 >= n)end2 = n - 1;while (begin1 <= end1 && begin2 <= end2){.....}while (begin1 <= end1){...}while (begin2 <= end2){...}//这里将拷贝的个数从2*gap修正为 end2 - j + 1memcpy(a + j, tmp + j, sizeof(int) * (end2 - j + 1));}gap *= 2;}free(tmp);tmp = NULL;
}

在上面的代码中我们增加了对越界问题的处理以及将memcpy拷贝的个数这里进行了修改,下面我们在test.c文件测试一下吧

void TestMergeSortNonR()
{int a[] = { 6,1,2,7,9,3,4,5,10,8 };MergeSortNonR(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));}int main()
{// 原数组为 6,1,2,7,9,3,4,5,10,8TestMergeSortNonR();return 0;
}

Perfect!

最后我们看一下非比较排序吧~

e.非比较排序

之前我们所有的排序都属于 比较排序,它们通过比较大小来排序,

下面要介绍的是非比较排序,它的排序核心用的不是比较

非比较排序主要有:计数排序,基数排序和桶排序

不过这里要说的是,非比较排序都是小众的,局限的,所以下面我就说一种计数排序来给大家感受一下,它也是这三种里面应用性最高的

计数排序的操作步骤如下:

1. 统计相同元素出现次数
2. 根据统计的结果将序列回收到原来的序列中

 

计数排序的代码如下:(yysy这个代码与之前几个相比就简单很多了)

void CountSort(int* a, int n)
{int min = a[0], max = a[0];for (int i = 1; i < n; i++){if (a[i] > max)max = a[i];if (a[i] < min)min = a[i];}int range = max - min + 1;int* count = (int*)malloc(sizeof(int) * range);if (count == NULL){perror("malloc fail");return;}memset(count, 0, sizeof(int) * range);//将range个0赋给count数组,完成初始化// 统计次数for (int i = 0; i < n; i++){count[a[i] - min]++;}// 排序int i = 0;int j = 0;for (i = 0;i<range;i++){while (count[i]){count[i]--;a[j++] = i + min;}}free(count);count = NULL;
}void TestCountSort()
{int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };CountSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}int main()
{// 原数组为5, 3, 9, 6, 2, 4, 7, 1, 8 TestCountSort();return 0;
}

有这个算法思想我们可以看出,计数排序适合的是对数据的范围集中的数据进行排序,但如果数据比较分散就不太适合了,空间浪费有点大

另外,它只能排整型,如果是什么浮点型,字符型甚至结构体类型,它都不是不能用的,这也是它的局限性

好了,至此,排序的实现我们就都完成了,鼓掌鼓掌!

最后,我们把先前在开头的图拿过来,在我们学完所有的排序之后再看一遍一定会有新的感知

结语

好啦,到此为止,今天的排序内容就结束啦,如果文中分析,题解代码有不足的地方欢迎大家在评论区讨论和指正

让我们在接下来的时间里一起学习,一起进步吧~

c0fe1378f4b1464abb37998a472b5961.jpg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/767247.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何实现跨标签页通讯

什么是跨标签页通讯 同一浏览器&#xff0c;可以打开多个标签页&#xff0c;跨标签页通讯就是&#xff0c;一个标签页能够发消息给另一标签页。 有哪些实现方案 localStorage &#xff08;window.onstorage事件监听&#xff09;BroadcastChannel&#xff08;广播&#xff09…

C语言经典算法-9

文章目录 其他经典例题跳转链接46.稀疏矩阵47.多维矩阵转一维矩阵48.上三角、下三角、对称矩阵49.奇数魔方阵50.4N 魔方阵51.2(2N1) 魔方阵 其他经典例题跳转链接 C语言经典算法-1 1.汉若塔 2. 费式数列 3. 巴斯卡三角形 4. 三色棋 5. 老鼠走迷官&#xff08;一&#xff09;6.…

pytest框架的封装以及用例管理框架

pytest框架的封装以及用例管理框架 公共类统一封装requests_util02.pytest_api01.py 自动化测试的基础自动化测试的介入点自动化测试和手工测试占比自动化实施过程 pytest元素定位元素定位查找元素定位的方式通过 ID 定位通过 Name 定位通过 Class Name 定位通过 Tag Name 定位…

唯众物联网安装调试员实训平台物联网一体化教学实训室项目交付山东技师学院

近日&#xff0c;山东技师学院物联网安装调试员实训平台及物联网一体化教学实训室采购项目已顺利完成交付并投入使用&#xff0c;标志着学院在物联网技术教学与实践应用方面迈出了坚实的一步。 山东技师学院作为国内知名的技师培养摇篮&#xff0c;一直以来致力于为社会培养高…

windows11 openssh服务开启;第三方ping不通局域网windows电脑;ssh连接内部ubuntu系统

参考&#xff1a;https://blog.csdn.net/2301_77554343/article/details/134328867 1、windows11 openssh开启 1&#xff09;我这边可选功能在设置-系统里面&#xff1b;其他网上看在应用下&#xff1b;添加可选openssh服务器安装 2&#xff09;安装后打开&#xff0c;管理员…

光伏户用开发技巧

一、开发户用光伏的技巧有哪些&#xff1f; 1.项目可行性分析 电站开发前需要先进行可行性分析&#xff0c;从当地的气象条件、电网的接入能力、政策环境等方便分析。可以自行收集数据分析&#xff0c;也可以邀请专业机构进行评估。 2.选址和电站设计 光伏电站的选址&#…

agent利用知识来做规划:《KnowAgent: Knowledge-Augmented Planning for LLM-Based Agents》笔记

文章目录 简介KnowAgent思路准备知识Action Knowledge的定义Planning Path Generation with Action KnowledgePlanning Path Refinement via Knowledgeable Self-LearningKnowAgent的实验结果 总结参考资料 简介 《KnowAgent: Knowledge-Augmented Planning for LLM-Based Age…

Spring Cloud四:微服务治理与安全

Spring Cloud一&#xff1a;Spring Cloud 简介 Spring Cloud二&#xff1a;核心组件解析 Spring Cloud三&#xff1a;API网关深入探索与实战应用 文章目录 一、服务注册中心的选型与最佳实践1. 主流服务注册中心概述2. 最佳实践建议(1)、选型建议(2)、高可用性与稳定性1). 高可…

北京密云广电许可证办理要求与流程

北京密云广电许可证办理攻略&#xff1a;要求与流程全解析 一、引言 各位老板好&#xff0c;我是经典世纪胡云帅&#xff0c;随着广播电视行业的快速发展&#xff0c;越来越多的企业和个人希望进入这一领域&#xff0c;开展节目制作、传播等业务。而在北京密云&#xff0c;这一…

二进制王国(蓝桥杯备赛)【sort/cmp的灵活应用】

二进制王国 题目链接 https://www.lanqiao.cn/problems/17035/learning/?contest_id177 题目描述 思路 这里就要灵活理解字典序排列&#xff0c;虽然string内置可以直接比较字符串字典序&#xff0c;但是在拼接时比较特殊&#xff0c;比如 11的字典序小于110&#xff0c;但…

HTTP --- 下

目录 1. HTTP请求方式 1.1. HTML 表单 1.2. GET && POST方法 1.2.1. 用 GET 方法提交表单数据 1.2.2. 用 POST 方法提交表单数据 1.2.3. 总结 1.3. 其他方法 2. HTTP的状态码 2.1. 重定向 2.1.1. 临时重定向 && 永久重定向 2.1.2. 302 &&…

【MySQL系列】Public Key Retrieval is not allowed

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Python灰帽子网络安全实践

教程介绍 旨在降低网络防范黑客的入门门槛&#xff0c;适合所有中小企业和传统企业。罗列常见的攻击手段和防范方法&#xff0c;让网站管理人员都具备基本的保护能力。Python 编程的简单实现&#xff0c;让网络运维变得更简单。各种黑客工具的理论和原理解剖&#xff0c;让人知…

基于Matlab的眼底图像血管分割,Matlab实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

车道线检测论文:《Ultra Fast Structure-aware Deep Lane Detection》

该论文标题为《Ultra Fast Structure-aware Deep Lane Detection》&#xff0c;作者是浙江大学计算机科学与技术学院的Zequn Qin、Huanyu Wang和Xi Li。论文提出了一种新颖的、简单而有效的车道检测方法&#xff0c;旨在解决具有挑战性场景下的车道检测问题&#xff0c;并实现极…

借用QStyledItemDelegate 在DataView中创建各种子控件

在Model/View 中&#xff0c;二维表格的输入方式可能各种各样&#xff0c;我们需要在单元格嵌入各种类型控件&#xff0c;但是能把各种控件整合一个委托类吗&#xff1f;本文进行了尝试。将创建各种控件的逻辑整合到一个委托类。效果图如下&#xff1a; 本文实现了在DataView 的…

【SpringMVC】知识汇总

SpringMVC 短暂回来&#xff0c;有时间就会更新博客 文章目录 SpringMVC前言一、第一章 SpingMVC概述二、SpringMVC常用注解1. Controller注解2. RequestMapping注解3. ResponseBody注解4. RequestParam5. EnableWebMvc注解介绍6. RequestBody注解介绍7. RequestBody与RequestP…

Echarts功能记录

基础配置 工具箱toolbox 对应功能 案例中使用到的第三方脚本

解决Animate.css动画效果无法在浏览器运行问题

背景 在开发官方网站的时候&#xff0c;临时更换了电脑&#xff0c;发现原本正常的动画效果突然不动了。 经过 chrome、Microsoft Edge都无法运行。 Animate.css | A cross-browser library of CSS animations. 问题排查 通过审查元素后发现类名是注入并且生效的。 验证 然…

【图解物联网】第3章 物联网设备

3.1 设备——通向显示世界的接口 3.1.1 为什么要学习设备的相关知识 经过前两章的学习&#xff0c;想必各位读者已经掌握物联网这个词描绘出的世界和用于实现物联网的系统架构了。基于这点&#xff0c;这一章将会为大家介绍在物联网世界中起着核心作用的因素&#xff0c;即设…