【数据结构】八种常见的排序算法

【数据结构】八种常见的排序算法

文章目录

1.排序概念及运⽤

1.1 概念

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

1.2 常⻅排序算法

在这里插入图片描述

2.实现常⻅排序算法

2.1 插⼊排序

基本思想

直接插⼊排序是⼀种简单的插⼊排序法,其基本思想是:把待排序的记录按其关键码值的⼤⼩逐个插⼊到⼀个已经排好序的有序序列中,直到所有的记录插⼊完为⽌,得到⼀个新的有序序列。

实际中我们玩扑克牌时,就⽤了插⼊排序的思想

2.1.1 直接插⼊排序

当插⼊第 i(i>=1) 个元素时,前⾯的 arr[0],arr[1],…arr[i-1] 已经排好序,此时⽤ arr[i] 的排序码与arr[i-1],arr[i-2]… 的排序码顺序进⾏⽐较,找到插⼊位置,将 array[i] 插⼊,原来位置上的元素顺序后移

代码实现

voidInsertSort(int* arr,int n){//i表示有序数组的最后一个值//arr[n-1]后面没有待排序的数,所以i<n-1for(int i =0; i < n-1; i++){int end = i ;int tmp = arr[end +1];while(end >=0){if(arr[end]> tmp)//降序用"<" { arr[end +1]= arr[end]; end--;}else{break;}} arr[end +1]= tmp;}}voidprintArr(int* arr,int n){for(int i=0;i<n;i++){printf("%d ",arr[i]);}printf("\n");}intmain(){int arr[]={5,3,9,6,2,4,7,1,8};int size=sizeof(arr)/sizeof(arr[0]);InsertSort(arr,size);printArr(arr,size);return0;}
直接插⼊排序的特性总结元素集合越接近有序,直接插⼊排序算法的时间效率越⾼时间复杂度:O(n2) 【最差情况:1+2+……+n-1,最好情况(O(n)):1+1+……+1】空间复杂度:O(1)

2.1.2 希尔排序

希尔排序法⼜称缩⼩增量法。希尔排序法的基本思想是:先选定⼀个整数(通常是gap=n/3+1),把待排序⽂件所有记录分成各组,所有的距离相等的记录分在同⼀组内,并对每⼀组内的记录进⾏排序,然后gap=gap/3+1得到下⼀个整数,再将数组分成各组,进⾏插⼊排序,当gap=1时,就相当于直接插⼊排序。(gap为n,就划分为n组)

它是在直接插⼊排序算法的基础上进⾏改进⽽来的,综合来说它的效率肯定是要⾼于直接插⼊排序算法的。

希尔排序的特性

1.希尔排序是对直接插⼊排序的优化。当 gap > 1 时都是预排序,⽬的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序的了,这样就会很快。这样整体⽽⾔,可以达到优化的效果。
在这里插入图片描述

代码实现:

voidShellSort(int* arr,int n){int gap = n;while(gap >1){ gap = gap /3+1;//+1是因为当gap=1时是直接插入排序 for(int i =0; i < n - gap; i++){int end = i;int tmp = arr[end + gap];while(end >=0){if(arr[end]> tmp){ arr[end + gap]= arr[end]; end -= gap;}else{break;}} arr[end + gap]= tmp;}}}
2.1.2.1 希尔排序的时间复杂度计算

希尔排序的时间复杂度估算:

外层循环: 外层循环的时间复杂度可以直接给出为:O(logn) 或者O(log3n) ,即O(logn)

内层循环:

在这里插入图片描述


在这里插入图片描述

gap取值有(以除3为例):n/3,n/9,n/27,… 2,1

• 当gap为n/3时,移动总数为: n/3∗(1 + 2) = n

• 当gap为n/9时,移动总数为: n/9*(1 + 2 + 3 + … + 8) = 4n

• 最后⼀趟,gap=1即直接插⼊排序,内层循环排序消耗为n

通过以上的分析,可以画出这样的曲线图:

在这里插入图片描述

因此,希尔排序在最初和最后的排序的次数都为n,即前⼀阶段排序次数是逐渐上升的状态,当到达某⼀顶点时,排序次数逐渐下降⾄n,⽽该顶点的计算暂时⽆法给出具体的计算过程

希尔排序时间复杂度不好计算,因为 gap 的取值很多,导致很难去计算,因此很多书中给出的希尔排序的时间复杂度都不固定。

《数据结构(C语⾔版)》—严蔚敏书中给出的时间复杂度为:

在这里插入图片描述

2.2 选择排序

选择排序的基本思想: 每⼀次从待排序的数据元素中选出最⼩(或最⼤)的⼀个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。

2.2.1 直接选择排序

代码实现:

//优化后voidSelectSort(int* arr,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(arr[i]> arr[maxi]){ maxi = i;}if(arr[i]< arr[mini]){ mini = i;}}if(begin == maxi){ maxi = mini;}Swap(&arr[mini],&arr[begin]);Swap(&arr[maxi],&arr[end]);++begin;--end;}}

直接选择排序的特性总结: 1. 直接选择排序思考⾮常好理解,但是效率不是很好,实际中很少使⽤

  1. 时间复杂度:O(N2)
  2. 空间复杂度:O(1)

2.2.2 堆排序

堆排序(Heapsort)是指利⽤堆积树(堆)这种数据结构所设计的⼀种排序算法,它是选择排序的⼀种。它是通过堆来进⾏选择数据。需要注意的是排升序要建⼤堆,排降序建⼩堆。

堆排序讲解链接

2.3 交换排序

交换排序基本思想: 所谓交换,就是根据序列中两个记录键值的⽐较结果来对换这两个记录在序列中的位置

交换排序的特点是:将键值较⼤的记录向序列的尾部移动,键值较⼩的记录向序列的前部移动

2.3.1 冒泡排序

前⾯在算法题中我们已经接触过冒泡排序的思路了,冒泡排序是⼀种最基础的交换排序。之所以叫做冒泡排序,因为每⼀个元素都可以像⼩⽓泡⼀样,根据⾃⾝⼤⼩⼀点⼀点向数组的⼀侧移动。

代码实现:

voidBubbleSort(int* arr,int n){int exchange =0;for(int i =0; i < n-1; i++){for(int j =0; j<n-1-i ; j++){if(arr[j]> arr[j +1]){ exchange =1;Swap(&arr[j],&arr[j +1]);}}if(exchange ==0){break;}}}

冒泡排序的特性总结

• 时间复杂度:O(N2)

• 空间复杂度:O(1)

2.3.2 快速排序

快速排序是Hoare于1962年提出的⼀种⼆叉树结构的交换排序⽅法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两⼦序列,左⼦序列中所有元素均⼩于基准值,右⼦序列中所有元素均⼤于基准值,然后最左右⼦序列重复该过程,直到所有元素都排列在相应位置上为⽌。

快速排序实现主框架:

//快速排序 voidQuickSort(int* arr,int left,int right){if(left >= right){return;}//找基准值,并把基准值放到指定的位置上 QuickSort(arr, left, meet -1);QuickSort(arr, meet +1, right);}

如何找基准值,并把基准值放到指定的位置上?

主要有以下⼏种实现⽅式:

2.3.2.1 hoare版本(重要)

算法思路: 1)创建左右指针,确定基准值

2)right:从右向左找出⽐基准值⼩的数据,left:从左向右找出⽐基准值⼤的数据,左右指针数据交换,进⼊下次循环

问题1:为什么跳出循环后right位置的值⼀定不⼤于key? 当 left > right 时,即right⾛到left的左侧,⽽left扫描过的数据均不⼤于key,因此right此时指向的数据⼀定不⼤于key

在这里插入图片描述

问题2:为什么left和right指定的数据和key值相等时也要交换? 相等的值参与交换确实有⼀些额外消耗。但是实际还有各种复杂的场景,假设数组中的数据⼤量重复时, ⽆法进⾏有效的分割排序。比如下面这种情况

在这里插入图片描述

代码实现:

tips:递归的时间复杂度=单次递归的时间* 循环次数=nlogn

循环次数是二叉树的高度,满二叉树的结点个数n=2k-1,则k=log2n+1
// 找基准值,时间复杂度不是n^2,而是n,因为两层while循环只循环了一遍数组int_QuickSort(int* arr,int left,int right){int keyi = left; left ++;while(left <= right){//right:从右往左找小于基准值的while(left <= right&&arr[right]> arr[keyi]){--right;}//left:从左往右找大于基准值的while(left <= right&&arr[left]< arr[keyi]){++left;}//这里找到left比基准值大,right比基准值小if(left <= right)Swap(&arr[left++],&arr[right--]);}//right的位置就是基准值的位置Swap(&arr[keyi],&arr[right]);return right;}//快速排序voidQuickSort(int* arr,int left,int right){if(left >= right){return;}//找基准值keyiint keyi =_QuickSort(arr, left, right);//左右序列递归继续排序QuickSort(arr, left, keyi -1);QuickSort(arr, keyi +1, right);}
如果基准值找的不好/数组有序,时间复杂度为n^2。
2.3.2.2 挖坑法

思路: 创建左右指针。⾸先从右向左找出⽐基准⼩的数据,找到后⽴即放⼊左边坑中,当前位置变为新的"坑",然后从左向右找出⽐基准⼤的数据,找到后⽴即放⼊右边坑中,当前位置变为新的"坑",结 束循环后将最开始存储的分界值放⼊当前的"坑"中,返回当前"坑"下标(即分界值下标)

在这里插入图片描述
int_QuickSort(int* arr,int left,int right){int hole = left;int key = arr[hole];while(left < right){while(left < right && arr[right]> key){--right;} arr[hole]= arr[right]; hole = right;while(left < right && arr[left]< key){++left;} arr[hole]= arr[left]; hole = left;} arr[hole]= key;return hole;}
2.3.2.3 lomuto前后指针(重要)

创建前后指针,从左往右找⽐基准值⼩的进⾏交换,使得⼩的都排在基准值的左边。

在这里插入图片描述
int_QuickSort(int* arr,int left,int right){int prev = left, cur = left +1;int key = left;while(cur <= right){if(arr[cur]< arr[key]&&++prev != cur){Swap(&arr[cur],&arr[prev]);}++cur;}Swap(&arr[key],&arr[prev]);return prev;}

快速排序特性总结: 1. 时间复杂度:O(nlogn) 2. 空间复杂度:O(logn)

2.3.2.4 ⾮递归版本(重要)

⾮递归版本的快速排序需要借助数据结构:栈

代码实现

voidQuickSortNonR(int* arr,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(arr[cur]< arr[keyi]&&++prev != cur)Swap(&arr[prev],&arr[cur]);++cur;}Swap(&arr[keyi],&arr[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);}

2.4 归并排序 (重要)

归并排序算法思想: 归并排序(MERGE-SORT)是建⽴在归并操作上的⼀种有效的排序算法,该算法是采⽤分治法(Divide andConquer)的⼀个⾮常典型的应⽤。将已有序的⼦序列合并,得到完全有序的序列;即先使每个⼦序列有序,再使⼦序列段间有序。若将两个有序表合并成⼀个有序表,称为⼆路归并。

归并递归排序核⼼步骤:

在这里插入图片描述

代码实现

void_MergeSort(int* arr,int left,int right,int* tmp){if(left >= right){return;}int mid =(right + left)/2;//[left,mid] [mid+1,right] _MergeSort(arr, left, mid, tmp);_MergeSort(arr, mid +1, right, tmp);int begin1 = left, end1 = mid;int begin2 = mid +1, end2 = right;int index = begin1;//合并两个有序数组为⼀个数组 while(begin1 <= end1 && begin2 <= end2){if(arr[begin1]< arr[begin2]){ tmp[index++]= arr[begin1++];}else{ tmp[index++]= arr[begin2++];}}while(begin1 <= end1){ tmp[index++]= arr[begin1++];}while(begin2 <= end2){ tmp[index++]= arr[begin2++];}for(int i = left; i <= right; i++){ arr[i]= tmp[i];}}voidMergeSort(int* arr,int n){int* tmp = new int[n];_MergeSort(a,0, n -1, tmp); delete[] tmp;}

归并排序特性总结: 1. 时间复杂度:O(nlogn) 2. 空间复杂度:O(n)

2.5 测试代码:排序性能对⽐

// 测试排序的性能对⽐voidTestOP(){srand(time(0));constint N =100000;int* a1 =(int*)malloc(sizeof(int)*N);int* a2 =(int*)malloc(sizeof(int)*N);int* a3 =(int*)malloc(sizeof(int)*N);int* a4 =(int*)malloc(sizeof(int)*N);int* a5 =(int*)malloc(sizeof(int)*N);int* a6 =(int*)malloc(sizeof(int)*N);int* a7 =(int*)malloc(sizeof(int)*N);for(int i =0; i < N;++i){ a1[i]=rand(); a2[i]= a1[i]; a3[i]= a1[i]; a4[i]= a1[i]; a5[i]= a1[i]; a6[i]= a1[i]; a7[i]= a1[i];}int begin1 =clock();InsertSort(a1, N);int end1 =clock();int begin2 =clock();ShellSort(a2, N);int end2 =clock();int begin3 =clock();SelectSort(a3, N);int end3 =clock();int begin4 =clock();HeapSort(a4, N);int end4 =clock();int begin5 =clock();QuickSort(a5,0, N-1);int end5 =clock();int begin6 =clock();MergeSort(a6, N);int end6 =clock();int begin7 =clock();BubbleSort(a7, N);int end7 =clock();printf("InsertSort:%d\n", end1 - begin1);printf("ShellSort:%d\n", end2 - begin2);printf("SelectSort:%d\n", end3 - begin3);printf("HeapSort:%d\n", end4 - begin4);printf("QuickSort:%d\n", end5 - begin5);printf("MergeSort:%d\n", end6 - begin6);printf("BubbleSort:%d\n", end7 - begin7);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);}

2.6 ⾮⽐较排序

2.6.1 计数排序

计数排序⼜称为鸽巢原理,是对哈希直接定址法的变形应⽤。

操作步骤: 1)统计相同元素出现次数

2)根据统计的结果将序列回收到原来的序列中代码实现

在这里插入图片描述


在这里插入图片描述
voidCountSort(int* arr,int n){//找min,maxint min = arr[0], max = arr[0];for(int i =1; i < n; i++){if(arr[i]> max) max = arr[i];if(arr[i]< min) min = arr[i];}//确定数组大小rangeint range = max - min +1;int* count =(int*)malloc(sizeof(int)* range);if(count ==NULL){perror("malloc fail");exit(1);}//对count初始化----calloc,memsetmemset(count,0,sizeof(int)* range);// 统计次数 //通过映射的方式将数组保存在count数组中//映射的方式==原数组的值-minfor(int i =0; i < n; i++){ count[arr[i]- min]++;}//将count数组中的数据还原到arr数组中 int index =0;for(int i =0; i < range; i++){while(count[i]--){ arr[index++]= i + min;}}free(count);}

计数排序的特性: 计数排序在数据范围集中时,效率很⾼,但是适⽤范围及场景有限。

时间复杂度:O(n + range)

空间复杂度:O(range)

稳定性:稳定

3.排序算法复杂度及稳定性分析

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的 相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,⽽在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

在这里插入图片描述

四种不稳定排序举例

在这里插入图片描述
在这里插入图片描述


在这里插入图片描述


本节全部代码汇总

//Sort.h#include"Sort.h"#include"Stack.h"//直接插入排序voidInsertSort(int* arr,int n){//i表示有序数组的最后一个位置//arr[n-1]后面没有待排序的数,所以i<n-1for(int i =0; i < n -1; i++){int end = i;int tmp = arr[end +1];while(end >=0){if(arr[end]> tmp)//降序用"<" { arr[end +1]= arr[end]; end--;}else{break;}} arr[end +1]= tmp;}}//希尔排序voidShellSort(int* arr,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 = arr[end + gap];while(end >=0){if(arr[end]> tmp){ arr[end + gap]= arr[end]; end -= gap;}else{break;}} arr[end + gap]= tmp;}}}voidSwap(int* x,int* y){int tmp =*x;*x =*y;*y = tmp;}//向下调整算法 lognvoidAdjustDown(int* arr,int parent,int n){int child = parent *2+1;while(child < n){//建大堆:<//建小堆: >if(child +1< n && arr[child]< arr[child +1]){ child++;}//孩子和父亲比较//建大堆:>//建小堆:<if(arr[child]> arr[parent]){Swap(&arr[child],&arr[parent]); parent = child; child = parent *2+1;}else{break;}}}//堆排序————使用的是堆结构的思想 n * lognvoidHeapSort(int* arr,int n){//向下调整算法——建堆nfor(int i =(n -1-1)/2; i >=0; i--){AdjustDown(arr, i, n);//调整堆,使其满足堆的性质}////向上调整算法建堆n*logn//for (int i = 0; i < n; i++)//{// AdjustUp(arr, i);//}//n*lognint end = n -1;while(end >0){Swap(&arr[0],&arr[end]);AdjustDown(arr,0, end);//logn end--;}}//1)直接选择排序//void SelectSort(int* arr, int n)//{// for (int i = 0; i < n-1; i++)// {// int min = i;// for (int j = i+1; j < n; j++)// {// if (arr[j] < arr[min])// min = j;// }// Swap(&arr[min], &arr[i]);// }//}voidSelectSort(int* arr,int n){int begin =0, end = n -1;while(begin < end){int mini = begin;int maxi = begin;//比较每个数for(int i = begin+1; i <= end; i++){if(arr[i]> arr[maxi]){ maxi = i;}if(arr[i]< arr[mini]){ mini = i;}}//当开头就是最大值时,最小值会和开头交换,&arr[maxi]此时存的是最小值if(begin == maxi){ maxi = mini;}Swap(&arr[mini],&arr[begin]);Swap(&arr[maxi],&arr[end]);++begin;--end;}}//冒泡排序 n^2voidBubbleSort(int* arr,int n){int exchange =0;for(int i =0; i < n-1; i++){for(int j =0; j < n - i -1; j++){if(arr[j]>arr[j+1]){ exchange =1;Swap(&arr[j],&arr[j +1]);}}if(exchange ==0)break;}}//找基准值hoareint_QuickSort1(int* arr,int left,int right){int keyi = left; left++;while(left <= right){//right:从右往左找小于基准值的while(left <= right && arr[right]> arr[keyi]){--right;}//left:从左往右找大于基准值的while(left <= right && arr[left]< arr[keyi]){++left;}//这里找到left比基准值大,right比基准值小if(left <= right)Swap(&arr[left++],&arr[right--]);}//right的位置就是基准值的位置Swap(&arr[keyi],&arr[right]);return right;}//挖坑法int_QuickSort2(int* arr,int left,int right){int hole = left;int key = arr[hole];while(left < right){while(left < right && arr[right]> key){--right;} arr[hole]= arr[right]; hole = right;while(left < right && arr[left]< key){++left;} arr[hole]= arr[left]; hole = left;} arr[hole]= key;return hole;}//lomutoint_QuickSort(int* arr,int left,int right){int prev = left, cur = left +1;int key = left;while(cur <= right){if(arr[cur]< arr[key]&&++prev != cur){Swap(&arr[cur],&arr[prev]);}++cur;}Swap(&arr[key],&arr[prev]);return prev;}//快速排序voidQuickSort(int* arr,int left,int right){if(left >= right){return;}//找基准值keyiint keyi =_QuickSort(arr, left, right);//左右序列递归继续排序QuickSort(arr, left, keyi -1);QuickSort(arr, keyi +1, right);}//非递归快速排序voidQuickSortNonR(int* arr,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);//[begin,end]---在这个范围里找基准值// 使用lomuto法int keyi = begin;int prev = begin;int cur = begin +1;while(cur <= end){if(arr[cur]< arr[keyi]&&++prev != cur)Swap(&arr[prev],&arr[cur]);++cur;}Swap(&arr[keyi],&arr[prev]); keyi = prev;//此时keyi又成为了基准值的位置//左序列[begin, keyi-1] //右序列[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_MergeSort(int* arr,int left,int right,int* tmp){//分解if(left >= right)//虽然没有>的可能,但是这样代码健壮性更好,只写==也可以{return;}int mid =(right + left)/2;//左序列:[left,mid] //右序列:[mid+1,right]// 左序列继续二分_MergeSort(arr, left, mid, tmp);//右序列二分_MergeSort(arr, mid +1, right, tmp);//合并两个有序数组为⼀个数组 int begin1 = left, end1 = mid;int begin2 = mid +1, end2 = right;int index = begin1;while(begin1 <= end1 && begin2 <= end2){if(arr[begin1]< arr[begin2]){ tmp[index++]= arr[begin1++];}else{ tmp[index++]= arr[begin2++];}}while(begin1 <= end1){ tmp[index++]= arr[begin1++];}while(begin2 <= end2){ tmp[index++]= arr[begin2++];}//拷贝回arrfor(int i = left; i <= right; i++){ arr[i]= tmp[i];}}voidMergeSort(int* arr,int n){int* tmp =(int*)malloc(n*sizeof(int));//[0,n-1]//二分_MergeSort(arr,0, n -1, tmp);free(tmp);}//非比较排序voidCountSort(int* arr,int n){//找min,maxint min = arr[0], max = arr[0];for(int i =1; i < n; i++){if(arr[i]> max) max = arr[i];if(arr[i]< min) min = arr[i];}//确定数组大小rangeint range = max - min +1;int* count =(int*)malloc(sizeof(int)* range);if(count ==NULL){perror("malloc fail");exit(1);}//对count初始化----calloc,memsetmemset(count,0,sizeof(int)* range);// 统计次数 //通过映射的方式将数组保存在count数组中//映射的方式==原数组的值-minfor(int i =0; i < n; i++){ count[arr[i]- min]++;}//将count数组中的数据还原到arr数组中 int index =0;for(int i =0; i < range; i++){while(count[i]--){ arr[index++]= i + min;}}}
//test.c#include"Sort.h"voidprintArr(int* arr,int n){for(int i =0; i < n; i++){printf("%d ", arr[i]);}printf("\n");}voidtest01(){//int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };//int a[] = { 5, 3, 9, 6, 2, 4 };int a[]={6,1,2,7,9,3};int n =sizeof(a)/sizeof(a[0]);printf("排序之前:");printArr(a, n);//InsertSort(a,n);//ShellSort(a, n);//SelectSort(a, n);//QuickSort(a, 0, n - 1);//QuickSortNonR(a, 0, n - 1);//MergeSort(a, n);CountSort(a, n);printf("排序之后:");printArr(a, n);}// 测试排序的性能对⽐voidTestOP(){srand(time(0));constint N =100000;int* a1 =(int*)malloc(sizeof(int)* N);int* a2 =(int*)malloc(sizeof(int)* N);int* a3 =(int*)malloc(sizeof(int)* N);int* a4 =(int*)malloc(sizeof(int)* N);int* a5 =(int*)malloc(sizeof(int)* N);int* a6 =(int*)malloc(sizeof(int)* N);int* a7 =(int*)malloc(sizeof(int)* N);for(int i =0; i < N;++i){ a1[i]=rand(); a2[i]= a1[i]; a3[i]= a1[i]; a4[i]= a1[i]; a5[i]= a1[i]; a6[i]= a1[i]; a7[i]= a1[i];}int begin1 =clock();InsertSort(a1, N);int end1 =clock();int begin2 =clock();ShellSort(a2, N);int end2 =clock();int begin3 =clock();SelectSort(a3, N);int end3 =clock();int begin4 =clock();HeapSort(a4, N);int end4 =clock();int begin5 =clock();QuickSort(a5,0, N -1);int end5 =clock();int begin6 =clock();MergeSort(a6, N);int end6 =clock();int begin7 =clock();BubbleSort(a7, N);int end7 =clock();printf("InsertSort:%d\n", end1 - begin1);printf("ShellSort:%d\n", end2 - begin2);printf("SelectSort:%d\n", end3 - begin3);printf("HeapSort:%d\n", end4 - begin4);printf("QuickSort:%d\n", end5 - begin5);printf("MergeSort:%d\n", end6 - begin6);printf("BubbleSort:%d\n", end7 - begin7);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);}intmain(){test01();//TestOP();return0;}
//Sort.h#pragmaonce#include<stdio.h>#include<memory.h>#include<stdlib.h>//插入排序//1)直接插入排序 <n^2voidInsertSort(int* arr,int n);//2)希尔排序 n^1.3voidShellSort(int* arr,int n);//选择排序//1)直接选择排序 n^2voidSelectSort(int* arr,int n);//2)堆排序 nlognvoidHeapSort(int* arr,int n);//交换排序//1)冒泡排序 n^2voidBubbleSort(int* arr,int n);//2)快速排序 nlognvoidQuickSort(int* arr,int left,int right);//3)非递归版本的快速排序voidQuickSortNonR(int* arr,int left,int right);//归并排序 nlognvoidMergeSort(int* arr,int n);//非比较排序voidCountSort(int* arr,int n);
//Stack.h#pragmaonce#include<stdio.h>#include<assert.h>#include<stdbool.h>#include<stdlib.h>typedefint STDataType;typedefstructStack{ STDataType* arr;//数组 int top;//有效数据的个数,也是栈顶 int capacity;//栈的空间大小}ST;// 初始化栈 voidSTInit(ST* ps);// 销毁栈 voidSTDestroy(ST* ps);// ⼊栈 voidSTPush(ST* ps, STDataType x);//出栈 voidSTPop(ST* ps);//取栈顶元素  STDataType STTop(ST* ps);//获取栈中有效元素个数 intSTSize(ST* ps);//栈是否为空  bool STEmpty(ST* ps);
//Stack.c#include"Stack.h"// 初始化栈 voidSTInit(ST* ps){ ps->arr =NULL; ps->top = ps->capacity =0;}// 销毁栈 voidSTDestroy(ST* ps){if(ps->arr)free(ps->arr); ps->arr =NULL; ps->top = ps->capacity =0;}// ⼊栈 voidSTPush(ST* ps, STDataType x){assert(ps);//先判断空间是否足够if(ps->top == ps->capacity){//如果capacity==0;不能直接*2int newCapacity = ps->capacity ==0?4:2* ps->capacity; STDataType* tmp =(STDataType*)realloc(ps->arr, newCapacity *sizeof(STDataType));if(tmp ==NULL){perror("realloc");exit(1);} ps->arr = tmp; ps->capacity = newCapacity;}//空间足够 ps->arr[ps->top++]= x;}//出栈 voidSTPop(ST* ps){assert(!STEmpty(ps)); ps->top--;}//取栈顶元素  STDataType STTop(ST* ps){assert(!STEmpty(ps));return ps->arr[ps->top -1];}//获取栈中有效元素个数 intSTSize(ST* ps){assert(ps);//传的参数指针不为空就行,有效数据个数可以为0return ps->top;}//栈是否为空  bool STEmpty(ST* ps){assert(ps);return ps->top ==0;//如果top==0就直接返回}
在这里插入图片描述

Read more

Flutter for OpenHarmony: Flutter 三方库 flutter_cors 应对鸿蒙 Web 与混合开发中的跨域挑战(网络兼容方案)

Flutter for OpenHarmony: Flutter 三方库 flutter_cors 应对鸿蒙 Web 与混合开发中的跨域挑战(网络兼容方案)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 的跨平台开发时,我们不仅开发原生 HAP,有时也会涉及 Flutter Web 或是在鸿蒙端侧运行 Webview 混合应用。这时,一个经典的“拦路虎”就会出现:CORS (跨源资源共享) 限制。当你的 Web 端尝试访问一个未配置跨域头部的后端 API 时,请求会被浏览器拦截,报错信息极其晦涩。 虽然 CORS 主要是后端的工作,但 flutter_cors 提供了一种客户端视角的辅助工具。它通过工具化手段帮助开发者分析、绕过或生成跨域适配规则,是保证鸿蒙跨平台 Web 项目顺利运行的调试利器。 一、跨域访问逻辑模型 CORS 是一种浏览器的安全保护机制,它在请求发出前先进行“预检(Preflight)

By Ne0inhk
[前后端系统开发教程]第四节-前端多平台部署的终极解决方案

[前后端系统开发教程]第四节-前端多平台部署的终极解决方案

在上一节中我们已经制作了一个简单的用户管理后端系统,我们这节就来尝试制作一个对应的前端系统。那么,我们是要使用安卓开发者工具制作一个安卓app,或者部署为微信小程序,亦或部署为传统的html网页? 答案是我全都要!通过DCloud生态,我们可以实现一份代码,多端部署。 第一部分:什么是DCloud生态? 众将士多端露难色,新面孔竟生好胆识 注:本节开始,教程的节奏会适当加快,希望各位可以跟上。 简单来说,DCloud生态的核心功能是,通过将项目按照不同的目标部署平台,二次编译为对应平台的代码,以实现“一份代码,多端部署”,以提高开发效率。详细介绍请参考uniapp官方文档:简介 - HBuilderX 文档。DCloud还提供云函数、云对象等工具,我们将在教程的后面去学习。 在这节教程中我们先学习如何在HBuilderX中调用上节中后端系统的API(即后端服务接口),编写一份前端代码,再将其打包为微信小程序、html网页和安卓app。 第二部分:怎么调用后端API接口? 接口表叫那前端瞧,服务器知晓谁来还 我们先回顾一下上节教程中的接口类,将其整理为一份API接口说明

By Ne0inhk
AI编程实战 : 使用 TRAE CN 将 MasterGo 设计稿转化为前端代码

AI编程实战 : 使用 TRAE CN 将 MasterGo 设计稿转化为前端代码

文章目录 * 什么是 MCP * 前置条件 * 1. 账号权限 * 2. 环境要求 * 3. 设计稿准备 * MasterGo AI Bridge 支持的能力 * 操作步骤 * 第一步: 安装/升级 TRAE CN IDE * 第二步: 获取 MasterGo 的 Personal Access Token * 第三步: 添加 MCP Server * 第四步: 创建自定义智能体(可选) * 第五步: 调用 MCP 生成前端代码 * 5.1 复制 MasterGo 设计稿链接 * 5.2 在 TRAE CN IDE

By Ne0inhk
AI 生成的 UI 太丑?3 步让你的前端秒变高级感

AI 生成的 UI 太丑?3 步让你的前端秒变高级感

🚀 AI 生成的 UI 太丑?3 步让你的前端秒变高级感 你是不是也遇到过这种情况:满心期待地用 AI 生成一个前端页面,结果得到的是一个土到掉渣的蓝紫色界面,丑到自己都看不下去?🤦‍♂️ 别担心,你不是一个人!这是目前 90% 开发者使用 AI 写前端时都会遇到的痛点。 好消息是,经过一番研究和实践,我们发现了一些有效的方法!通过几个简单的技巧,不需要手写任何 CSS,就能让 AI 帮你生成媲美专业设计师的 UI 界面。 今天就手把手教你 3 步搞定,让 AI 彻底告别 “AI 味”! 🧪 实验准备 工具准备 想要跟着实验,你需要准备: 1. Claude Code (2.0.55) 底层模型是 Minimax-M2

By Ne0inhk