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

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

文章目录

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 组件 dart_json_mapper_mobx 适配鸿蒙 HarmonyOS 实战:响应式 JSON 映射,构建非侵入式状态绑定与高性能序列化架构

Flutter 组件 dart_json_mapper_mobx 适配鸿蒙 HarmonyOS 实战:响应式 JSON 映射,构建非侵入式状态绑定与高性能序列化架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 dart_json_mapper_mobx 适配鸿蒙 HarmonyOS 实战:响应式 JSON 映射,构建非侵入式状态绑定与高性能序列化架构 前言 在鸿蒙(OpenHarmony)生态迈向全场景分布式联动、涉及复杂业务状态云端同步、大型本地配置反序列化及严苛 UI 刷新性能要求的背景下,如何实现一套既能保障业务模型(Model)的纯净性、又能与响应式状态管理(MobX)深度无缝融合的数据映射架构,已成为决定应用开发敏捷度与运行效能感的关键。在鸿蒙设备这类强调 AOT 极致性能与低堆内存占用的环境下,如果应用依然采用侵入式的 factory ToJson 或冗余的手写解析代码,由于由于业务逻辑与映射逻辑的重度耦合,极易由于由于“代码量激增”或“状态丢失”导致鸿蒙应用在处理高频数据流时发生状态不稳。 我们需要一种能够基于注解(Annotations)自动完成映射、支持

By Ne0inhk
基于Rust实现爬取 GitHub Trending 热门仓库

基于Rust实现爬取 GitHub Trending 热门仓库

基于Rust实现爬取 GitHub Trending 热门仓库 这个实战项目将使用 Rust 实现一个爬虫,目标是爬取 GitHub Trending 页面的热门 Rust 仓库信息(仓库名、描述、星标数、作者等),并将结果输出为 JSON 文件。本次更新基于优化后的代码,重点提升了错误处理容错性和 CSS 选择器稳定性。 技术栈 * HTTP 请求:reqwest( Rust 最流行的 HTTP 客户端,支持异步) * HTML 解析:scraper(基于 selectors 库,支持 CSS 选择器,轻量高效) * JSON 序列化:serde + serde_json( Rust 标准的序列化

By Ne0inhk
Spring Boot 日志实战:级别、持久化与 SLF4J 配置全指南

Spring Boot 日志实战:级别、持久化与 SLF4J 配置全指南

个人主页:♡喜欢做梦 欢迎  👍点赞  ➕关注  ❤️收藏  💬评论 目录 🍉日志的定义 🍉日志的使用 🍉日志的级别分类 🍑日志级别的使用 🍑日志级别的配置 🍉日志持久化 🍑什么是日志持久化? 🍑日志持久化的配置 🍉配置日志文件的文件 🍉更简单的日志输出 🍑添加依赖 🍑@Slf4j 🍉日志的定义 日志本质上是系统、软件或设备按时间顺序记录操作、事件或状态的文件文本,用于最终历史、排查问题和审计。 Spring Boot项目在启动时就有默认的日志输出: 核心作用 * 问题排查:当软件崩溃、系统出错、时,日志会记录错误代码、发生时间和上下文,帮助技术人员定位原因。 * 行为审计:记录用户的关键操作,比如谁登录了系统、谁修改了文件,用于追溯责任或合规检查。 * 状态监控:实是记录系统资源使用情况,如CPU占用率、内存使用量、帮助发现性能瓶颈。 🍉日志的使用 import org.slf4j.

By Ne0inhk