数据结构排序小结

排序类型小结

  • 💦 插入排序
    • 直接插入排序
    • 希尔排序
  • 💦 选择排序
    • 直接选择排序
    • 堆排序
  • 💦 交换排序
    • 冒泡排序
    • 快速排序🐾
      • ==霍尔版本==
      • ==补坑位版本==
      • ==前后指针版本==
      • ==非递归版本==
  • 💦 归并排序
      • ==递归版本==
      • ==非递归版本==
  • 💦 性能测试

💦 插入排序

直接插入排序

🔑 核心思想 🔑
请添加图片描述

🐳 把待排序的记录按关键码的大小逐个插入到一个已经排好的序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

❗ 过程:❕

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

❗ 直接插入排序的特性总结:❕

1️⃣ 元素集合越接近有序,直接插入排序算法的时间效率越高

2️⃣ 时间复杂度:O(N^2)

3️⃣ 空间复杂度:O(1),它是一种稳定的排序算法

4️⃣ 稳定性:稳定
  
🐾代码实现:

void InsertSort(int* a, int n)
{//注意这里的n-1for (int i = 0; i < n-1; 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;}
}

希尔排序

希尔排序 (缩小增量排序)

🔑 核心思想 🔑

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

在这里插入图片描述

🤖希尔排序的时间复杂度并不好计算,因为 gap 的取值方法很多,导致很难去计算,因此在好些数中给出的希尔排序的时间复杂度都不固定,官方给出的时间复杂度是 O(N1.3)

以下是其粗略的计算方法,可见非常复杂

在这里插入图片描述
🐾代码实现

//希尔排序
void ShellSort(int* a, int n)
{int gap = n;//gap>1时是预排,目的是让他接近有序//gap==1是直接插入排序,目的是让他有序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;}elsebreak;}a[end + gap] = tmp;}}
}

❗ 希尔排序特性总结 ❕

1️⃣ 希尔排序是对直接插入排序的优化

2️⃣ 当 gap > 1 时都是预排序,目的是让数组更接近于有序。当 gap == 1 时,其实就是直接插入排序,且数组已经接近有序的了。整体而言,可以达到优化的效果,我们实现后可以进行性能测试的对比

3️⃣ 希尔排序的时间复杂度并不好计算,因为 gap 的取值方法很多,导致很难去计算,因此在好些数中给出的希尔排序的时间复杂度都不固定,官方给出的时间复杂度是 O(N1.3)

4️⃣ 稳定性:不稳定

💦 选择排序

直接选择排序

🔑 核心思想 🔑

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

❗ 过程:❕

1️⃣ 在元素集合 array[i] - array[n-1] 中选择关键码最大 (小) 的数据元素

2️⃣ 若它不是这组元素中的最后一个(或第一个)元素,则将它与这组元素中的最后一个(或第一个)元素交换

3️⃣ 在剩余的 array[i] - array[n-2] (array[i+1]–array[n-1]) 集合中,重复上述步骤,直到集合剩余 1 个元素

❗ 直接选择排序的特性总结:❕

1️⃣ 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

2️⃣ 时间复杂度:O(N^2) - 最好 / 最坏都是如此,当数据极其无序时,比冒泡排序还要拉跨

3️⃣ 空间复杂度:O(1)

4️⃣ 稳定性:不稳定

❗ 动图演示:❕
请添加图片描述
🐾代码实现


//与上述动图一样的代码
void Swap(int* px, int* py)
{int temp = *px;*px = *py;*py = temp;
}
void SelectSort(int* a, int n)
{int i = 0;int begin = 0;while (begin < n){int mini = begin;//选最小for (i = begin; i < n; i++){if (a[i] < a[mini]){mini = i;}}//交换Swap(&a[begin], &a[mini]);//迭代begin++;}
}//优化版本,最大值最小值同时找
void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end){//一趟排序,将最大数最小数的下标都初始化成beginint 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判断,最大值的下标指向会随着begin的mini的交换使得max的值丢失//当maxi==begin时,maxi指向min,下一次交换就会出问题!!!if (maxi == begin){maxi = mini;}Swap(&a[end], &a[maxi]);//迭代++begin;--end;}
}

堆排序

🔑 核心思想 🔑

堆排序 (Heapsort) 是指利用堆积树 (堆) 这种数据结构所设计的一种排序算法,它是选择排序的一种。它是通过堆来进行选择数据。需要注意的是排升序要建大堆,排降序建小堆。

❗ 堆排序的特性总结:❕

1 堆排序使用堆来选数,效率就高了很多。

2. 时间复杂度:O(N*logN)

3.空间复杂度:O(1)

4.稳定性:不稳定

void Swap(int* px, int* py)
{int temp = *px;*px = *py;*py = temp;
}
//向上调整算法
void AdjustUp(int*a,int child)
{int parent=(child-1)/2;while(child > 0){//这里的符号决定调整为大堆还是小堆,这里以大堆为例if(a[child]>a[parent]){Swap(&a[child],&a[parent]);child=parent;parent=(child-1)/2;}else{break;}}
}
//向下调整算法
void AdjustDown(int* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){//假设左孩子小,如果假设错了,更新一下//这样操作后,child指向的就是两个孩子中较小的那一个if (a[child+1] < a[child] && child + 1 < size){  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)
{//建大堆for(int i=0;i<n;i++){AdjustUp(a,i); }int end=n-1;while(end>0){//交换后最大的数就排好了Swap(&a[0],&a[end]);//将前面的数再调整,选出次小的数AdjustDown(a,end,0);end--;}
}

方法二:用向下调整算法建堆

void Swap(int* px, int* py)
{int temp = *px;*px = *py;*py = temp;
}//排升序
void HeapSort(int* a, int n)
{//建大堆int i = 0;//n-1就是最后一个元素下标,再-1,除以二就是其父节点,也就是倒数第一个非叶子节点//从这开始从下往上向下建堆for (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--;}
}
  • 倒着调整叶子节点不需要处理(因为叶子节点没有子节点无法向下比较),从倒数第一个非叶子节点开始,即最后一个节点的父节点开始调整,从下往上向下建堆。

  • 在HeapSort函数中,第一个循环调用了AdjustDown函数,将待排序数组构建成了一个大堆。但是,这个大堆并不是完全有序的,只是满足了大堆的性质,即每个节点的值都大于或等于其左右子节点的值。因此,需要进行第二个while循环,将大堆根中的元素依次取出,交换堆顶元素和数组末尾元素,并重新调整大堆,直到整个数组有序。

  • 第二个while循环中,将堆顶元素与数组末尾元素交换,然后将剩余元素重新调整为大根堆。这样,每次交换后,数组末尾的元素就是当前大根堆中的最大值,而剩余元素仍然满足大根堆的性质。重复以上步骤,直到整个数组有序。

💦 交换排序

冒泡排序

请添加图片描述

// 时间复杂度:O(N^2)
// 最好情况是多少:O(N)
void BubbleSort(int* a, int n)
{for (int j = 0; j < n; j++){//定义exchange可以优化冒泡排序,当数据已经有序时,可以提前结束排序bool exchange = false;for (int i = 1; i < n-j; i++){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);exchange = true;}}if (exchange == false)break;}

快速排序🐾

🔑 核心思想 🔑

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

❗ 过程:❕

霍尔版本

请添加图片描述

1️⃣ 选出一个关键字 key,一般是头或者尾

2️⃣ 经过一次单趟后,key 放到了正确的位置,key 左边的值比 key 小,key 右边的值比 key 大

3️⃣ 再让 key 的左边区间有序、key 的右边区间有序

为什么相遇的位置的数不会比key大呢?
因为右边先走🐳🐳🐳🐳!!!
我们来分析一波,首先要明确R的任务是找小数,L的任务是找大数,相遇无非就分两种情况
1.R遇到L:这种情况下又分两种
第一种情况:L指向begin开始的位置,压根没动,这样当R走来是相遇于key这个位置,二者相等并不影响排序结果
第二种情况:R成功找到一个比key小的数,L也成功找到一个比key大的数,按照快排规则,此时L和R指向的数应该交换了,那么在交换后,L位置的数就比key要小了(这个过程可能不止一趟),最终R会与L相遇在L位置,这个位置的数必定是比key要小的
2.L遇到R:因为刚开始是R先走的,那么R停下一定是遇到了比key要小的数,(这个过程也可能不止一次)然后L开始向右走,与R在R位置相遇,这个位置的值一定是比key要小的

//简易版本
QuickSort(int* a,int begin,int end)
{if(begin>=end)return;//注意left的起始位置,如果这里left=begin+1,当数组有序时会出Bugint keyi=begin,left=begin,right=end;while(left<right){//右边找小//注意循环条件的控制while(left<right&&a[right]>=a[keyi]){right--;} //左边找大while(left<right&&a[left]<=a[keyi]){left++;}Swap(&a[left],&a[right]);}Swap(&a[left],&a[keyi]);//更新下一次递归时区间边界keyi=left;//与二叉树的先序遍历有异曲同工之妙QuickSort(a,begin,keyi-1);QuickSort(a,keyi+1,end);
}

在这里插入图片描述

上述代码每次递归都将keyi的值给成begin左边,在最坏情况下,时间复杂度会成为O(N^2),并且调用函数次数与数据个数密切相关,当待排序的数组数据过多时会出现栈溢出,导致被覆盖的内存区域中的数据损坏,从而导致程序崩溃或运行不正常。

要想优化上述情况,就要使得每次选取的key尽量在中间,我们可以选begin,end,midi三个下表对应数组中的值的中位数,用GetMidi函数分装,返回中间值的下标,然后在主函数中交换a[begin]和a[midi]的值,这样操作后a[begin]的值在整个数组中处于更居中的位置,再递归子区间就会缩短递归的区间长度和次数,大大优化代码。

//优化代码
//三数取中函数
int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;// begin end midi三个数选中位数if (a[begin] < a[midi]){if (a[midi] < a[end])return midi;else if (a[begin] > a[end])return begin;elsereturn end;}else//a[begin] > a[midi]{if (a[midi] > a[end])return midi;else if (a[begin] > a[end])return end;elsereturn begin;}
}void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int left = begin, right = end;int keyi = begin;while (left < right){// 右边找小while (left < right && a[right] >= a[keyi]){--right;}// 左边找大while (left < right && a[left] <= a[keyi]){++left;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;// [begin, keyi-1] keyi [keyi+1, end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

对于优化后的代码,还存在小区间占用过多栈空间的问题,如下图,假定在理想递归情况下,我们每次寻找的key都排到了数组中间,那么递归的函数栈空间展开就是一颗二叉树,当我们递归到二叉树后三层之后,开辟的栈空间居然占了整棵树的87.5%!!
然而后三层需要排的数据个数并不多,根本不需要浪费这么多空间,我们可以针对后三层单独采用插入排序进行再优化。
在这里插入图片描述

//对小区间处理的插入排序函数
void InsertSort(int* a, int n)
{//注意这里的n-1for (int i = 0; i < n-1; 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;}
}
//找中位数函数
int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;// begin end midi三个数选中位数if (a[begin] < a[midi]){if (a[midi] < a[end])return midi;else if (a[begin] > a[end])return begin;elsereturn end;}else//a[begin] > a[midi]{if (a[midi] > a[end])return midi;else if (a[begin] > a[end])return end;elsereturn begin;}
}
//快排函数
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;//end-begin+1是数据个数,规定其小于10时采用插入排序if(end-begin+1<10)//注意这里的递归起始区间位置是a+beginInsertSort(a+begin,end-begin+1);else{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int left = begin, right = end;int keyi = begin;while (left < right){// 右边找小while (left < right && a[right] >= a[keyi]){--right;}// 左边找大while (left < right && a[left] <= a[keyi]){++left;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;// [begin, keyi-1] keyi [keyi+1, end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);}
}
  • 下图是测试优化小区间后的快排效果,数字代表跑完程序所用的毫秒数,测试方法文末附有,我们对由10万个随机数构成的数组进行排序,为了使得对比更明显,我们在debug(调试版本)下进行对比,debug下,每个函数栈帧添加了许多调试信息,占用的空间更大,小区间优化的效果更加明显。而release(发布版本)对调试信息进行了优化,函数栈帧过多影响不大,多敲的代码可能还会降低排序速度,起到适得其反的效果。

在这里插入图片描述
可以看到效果是有的,但是不是非常明显,想要大幅度改进,还需要从思想上改变的新的方法

为了方便和后续新的方法对比,我们把霍尔版本的单趟排序抽离出来,定义为PartSort1函数

int PartSort1(int* a, int begin, int end)
{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int left = begin, right = end;int keyi = begin;while (left < right){// 右边找小while (left < right && a[right] >= a[keyi]){--right;}// 左边找大while (left < right && a[left] <= a[keyi]){++left;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);return left;
}
//函数 PartSort1 返回了 left 指针指向的值
//这个值表示基准值在分区后的位置。在快速排序算法中,
//这个值会被用来确定下一次分区的区间范围。
void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;//end-begin+1是数据个数,规定其小于10时采用插入排序if(end-begin+1<10)//注意这里的递归起始区间位置是a+beginInsertSort(a+begin,end-begin+1);else{int keyi=PartSort1(a,begin,end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);}
}

补坑位版本

动图展示💦
请添加图片描述

注意,不同方法的快速排序第一趟排序的结果可能不同
在这里插入图片描述

//补坑位版本
int PartSort2(int* a,int begin,int end)
{int midi=GetMidi(a, begin, end);Swap(&a[midi],&a[begin]);int key=a[begin];int hole=begin;while(begin<end){//右边找小,找到后填到左边坑位while(begin<end && key>=a[end]){end--;}  a[hole]=a[end];hole=end;//左边找大,找到后填到右边坑位while(begin<end&&key<=a[begin]){begin++;}a[hole]=a[begin];hole=begin;}a[hole]=key;return hole;
}

前后指针版本

请添加图片描述
第一趟交换后的结果
在这里插入图片描述

  • 最开始prev和cur相邻的
  • 当cur遇到比key小的值,++perv,交换prev和cur位置的值
  • 当cur遇到比key的大的值以后,++cur
  • prev和cur之间的值都是比key大的值,prev之前的包括交换后prev上的值都比key小
  • 相当于把大的翻滚式往右边推,同时把小的换到左边
int PartSort3(int*a,int begin,int end)
{int midi=GetMidi(a,begin,end);Swap(&a[begin],&a[midi]);int prev=begin;int cur=prev+1;int key=a[begin];while(cur<=end){if(key<=a[cur]){cur++;}else{prev++;Swap(&a[prev],&a[cur]); cur++;}}Swap(&a[prev],&key);return prev;
}

ok了这个测试后也没啥问题
在这里插入图片描述
在这里插入图片描述

int PartSort3(int* a, int begin, int end)
{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int keyi = begin;int prev = begin;int cur = prev + 1;while (cur <= end){//循环内部其实还可以进一步简化代码if (a[cur] < a[keyi] && ++prev != cur)Swap(&a[prev], &a[cur]);++cur;}Swap(&a[prev], &a[keyi]);keyi = prev;return keyi;
}

非递归版本

通常我们将递归代码改为非递归有两种方式

  • 写成循环版本
  • 借助数据结构----栈

如果想将快排仅仅利用循环是不现实的,因为递归的子区间左右区间不好控制,这里我们需要借助栈的结构

1️⃣将begin和end压入栈中,利用单趟快排找到keyi并且pop掉这两个元素
2️⃣将由keyi分出的子区间压入栈中,并且让每次将出栈的左右区间的子区间进栈
3️⃣循环多次
4️⃣当分出的子区间左区间大于等于右区间不需要入栈在这里插入图片描述
在这里插入图片描述

void QuickSortNonR(int* a, int begin, int end)
{ST s;STInit(&s);//第一次入栈要将整个数组的左右区间进栈STPush(&s, end);STPush(&s, begin);while(!STEmpty(&s)){//记录左右区间后出栈int left = STTop(&s);STPop(&s);int right = STTop(&s);STPop(&s);//利用存好的左右区间进行一趟快排,找到keyiint keyi = PartSort3(a, left, right);// [left, keyi-1] keyi [keyi+1, right]//当子区间合法时,继续入栈if (left < keyi - 1){STPush(&s, keyi - 1);STPush(&s, left);}if (keyi + 1 < right){STPush(&s, right);STPush(&s, keyi+1);}}STDestroy(&s);
}

💦 归并排序

基本思想: 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

动图展示
请添加图片描述

在这里插入图片描述

递归版本

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);// [begin, mid][mid+1, end]归并int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;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++];}//将tmp数组中的值拷贝回去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);
}

小结

  • 主函数接受两个参数,一个整数数组a和一个整数n,n 表示数组的长度。

  • MergeSort 函数首先为tmp数组开辟待空间。

  • 调用_MergeSort函数进行排序。

  • 释放tmp的空间。

  • 在子函数中,首先计算中间位置mid,并递归地对数组的两部分进行排序。这是分治的思想,将大问题分解成小问题,使用四个指针begin1和begin2、end1和end2,分别指向两个部分的开始位置和结束位置,

  • 然后看三个while循环的比较插入过程,每次分割后两部分分别从头开始比较,把较小的插入tmp数组,某一部分的数全部插入数组后,结束第一个while循环。继续检查哪个数组还有剩余元素,剩下的都是较大的,直接插入tmp数组中。

  • 接下来,我们需要从最小的子序列到最大依次往上进行排序插入,所以这里引用递归的思想完成排序:

  • 在函数_MergeSort中,首先判断begin是否等于end,如果相等,则当前子序列只有一个元素,不需要排序,直接返回。

  • 如果不相等,则计算中间位置mid,然后递归调用_MergeSort函数对左半部分和右半部分进行排序。在排序完成后,将左半部分和右半部分合并成一个有序数组tmp。

  • 每层递归排序后,使用memcpy函数将临时数组tmp中的元素复制回原数组a中。

非递归版本

  • 归并排序不适合用栈来改造,归并排序类似于树的后序遍历,在返回时还需要合并这样的复杂操作,栈模拟递归是不能实现的,对比之前的快速排序的非递归版本,快速排序是前序遍历,即使返回时不做任何操作也可以达到排序目的。
  • 我们可以先将gap初始化为1,然后每次将gap乘以2,直到gap大于等于数组的长度为止。在每次循环中,我们将数组分成若干个大小为gap的子数组,然后对每个子数组进行排序和合并。这样,我们就可以通过循环来实现归并排序,而不需要使用递归。在这里插入图片描述
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}//通过gap控制归并的子数组大小实现非递归的归并排序int gap = 1;while (gap < n){printf("gap:%2d->", gap);for (size_t i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;// 边界的处理if (end1 >= n || begin2 >= n){break;}if (end2 >= n){end2 = n - 1;}//开始归并int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}memcpy(a + i, tmp + i, sizeof(int) * (end2-i+1));}printf("\n");gap *= 2;}free(tmp);
}

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

💦 性能测试

void TestInsertSort()
{int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };InsertSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestBubbleSort()
{int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };BubbleSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestShellSort()
{//int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };int a[] = { 13, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };ShellSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestSelectSort()
{//int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };int a[] = { 13, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1};SelectSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestHeapSort()
{//int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };int a[] = { 13, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };HeapSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestQuickSort()
{//int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };//int a[] = {6,1,2,7,9,3,4,5,10,8};int a[] = { 6,1,2,6,7,9,3,4,6,10,8 };PrintArray(a, sizeof(a) / sizeof(int));//QuickSort(a, 0, sizeof(a) / sizeof(int)-1);QuickSortNonR(a, 0, sizeof(a) / sizeof(int) - 1);PrintArray(a, sizeof(a) / sizeof(int));
}void TestMergeSort()
{//int a[] = { 3, 2, 6, 8, 4, 6, 0, 9, 5, 7, 1 };//int a[] = {6,1,2,7,9,3,4,5,10,8};//int a[] = { 6,1,2,6,7,9,3,4,6,10,8 };int a[] = { 10,8,7,1,3,9,4,2,9,10,1,1,2,3};PrintArray(a, sizeof(a) / sizeof(int));MergeSortNonR(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}void TestCountSort()
{int a[] = {1,3,9,1,5,1,2,3,-5,-5,-2 };PrintArray(a, sizeof(a) / sizeof(int));CountSort(a, sizeof(a) / sizeof(int));PrintArray(a, sizeof(a) / sizeof(int));
}// 测试排序的性能对比
void TestOP()
{srand(time(0));const int N = 10000000;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);int* a8 = (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];a8[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();int begin8 = clock();CountSort(a8, N);int end8 = 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);printf("CountSort:%d\n", end8 - begin8);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);free(a8);
}int main()
{//TestInsertSort();//TestBubbleSort();//TestShellSort();//TestSelectSort();//TestHeapSort();//TestQuickSort();//TestMergeSort();//TestCountSort();TestOP();return 0;
}

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

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

相关文章

Kotlin 教程(环境搭建)

Kotlin IntelliJ IDEA环境搭建 IntelliJ IDEA 免费的社区版下载地址&#xff1a;Download IntelliJ IDEA – The Leading Java and Kotlin IDE 下载安装后&#xff0c;我们就可以使用该工具来创建项目&#xff0c;创建过程需要选择 SDK&#xff0c; Kotlin 与 JDK 1.6 一起使…

【数据结构与算法】6.栈

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…

Qt编写手机端视频播放器/推流工具/Onvif工具

一、视频播放器 同时支持多种解码内核&#xff0c;包括qmedia内核&#xff08;Qt4/Qt5/Qt6&#xff09;、ffmpeg内核&#xff08;ffmpeg2/ffmpeg3/ffmpeg4/ffmpeg5/ffmpeg6&#xff09;、vlc内核&#xff08;vlc2/vlc3&#xff09;、mpv内核&#xff08;mpv1/mp2&#xff09;、…

《HTML 简易速速上手小册》第1章:HTML 入门(2024 最新版)

文章目录 1.1 HTML 简介与历史&#xff08;&#x1f609;&#x1f310;&#x1f47d;踏上神奇的网页编程之旅&#xff09;1.1.1 从过去到现在的华丽蜕变1.1.2 市场需求 —— HTML的黄金时代1.1.3 企业中的实际应用 —— 不只是个网页1.1.4 职业前景 —— 未来属于你 1.2 基本 H…

第八篇【传奇开心果系列】beeware的toga开发移动应用示例:实现消消乐安卓手机小游戏

传奇开心果博文系列 系列博文目录beeware的toga开发移动应用示例系列博文目录一、项目目标二、安装依赖三、初步实现四、扩展思路五、实现游戏逻辑示例代码六、实现界面设计示例代码七、实现增加关卡和难度示例代码八、实现存档和排行榜示例代码九、实现添加特殊方块和道具示例…

C++提取ICO图标(PE文件资源提取)

最近需要写一个提取EXE或者DLL图标资源的功能, 网上找了很久, 要么功能不好用, 最后结果如下: 1.很多是加载为HICON句柄后转换为图片保存, 全损画质..., 2.后来找了个还能用的, 详见 https://github.com/TortoiseGit/TortoiseGit/blob/master/src/Utils/IconExtractor.cpp …

[设计模式Java实现附plantuml源码~结构型]树形结构的处理——组合模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…

网络会话管理

文章目录 一 什么是会话1 生活中会话2 Web中会话3 会话技术 二 Cookie1 原理2 使用2.1 添加cookie2.2 遍历cookie2.3 设置cookie的过期时间 3 特点 三 Session1 原理2 使用2.1 创建session对象2.2 不同的servlet间共享session对象中数据2.3 web.xml中设置session的超时时间 3 禁…

Keil软件某些汉字输出乱码,0xFD问题,51单片机

1. 问题 keil软件输入某些汉字的时候会输出乱码&#xff0c;例如&#xff1a;升、 数 2. 原因 keil软件会忽略0xFD。 升的GB2312编码为 0xc9fd&#xff0c;keil解析为0xc9数的GB2312编码为 0xcafd&#xff0c;keil解析为0xca 关于Keil软件中0xFD问题的说明 3. 解决方案1 …

E5071C 是德科技网络分析仪

181/2461/8938产品概述&#xff1a; E5071C ENA 矢量网络分析仪&#xff0c;9 kHz 至 20 GHz&#xff0c;配有增强型 TDR 测量选件。 E5071C 是大规模无源元器件测试的理想解决方案。 它具有出色的测量性能&#xff0c;有助于提高测试吞吐量&#xff0c;尤其是与 E5092A 多端…

力扣题目训练(3)

2024年1月27日力扣题目训练 2024年1月27日力扣题目训练290. 单词规律292. Nim 游戏303. 区域和检索 - 数组不可变91. 解码方法92. 反转链表 II41. 缺失的第一个正数 2024年1月27日力扣题目训练 2024年1月27日第三天编程训练&#xff0c;今天主要是进行一些题训练&#xff0c;包…

SQL注入:盲注

SQL注入系列文章&#xff1a; 初识SQL注入-CSDN博客 SQL注入&#xff1a;联合查询的三个绕过技巧-CSDN博客 SQL注入&#xff1a;报错注入-CSDN博客 目录 什么是盲注&#xff1f; 布尔盲注 手工注入 使用python脚本 使用sqlmap 时间盲注 手工注入 使用python脚本 使…

聊聊鸿蒙HarmonyOS NEXT 的技术细节

上周&#xff0c;华为在深圳举办了“鸿蒙生态千帆启航仪式”&#xff0c;这也是华为鸿蒙开启生态进阶的信号。在政策的叠加下&#xff0c;鸿蒙未来必定是势不可挡的。我们这些程序员也得与时俱进&#xff0c;熟悉鸿蒙的技术和细节&#xff0c;别在经济寒冬里被淘汰了。 官方称…

scrapy框架核心知识Spider,Middleware,Item Pipeline,scrapy项目创建与启动,Scrapy-redis与分布式

scrapy项目创建与启动 创建项目 在你的工作目录下直接使用命令: scrapy startproject scrapytutorial运行后创建了一个名为scrapytutorial的爬虫工程 创建spider 在爬虫工程文件内&#xff0c;运行以下命令&#xff1a; scrapy genspider quotes创建了名为quotes的爬虫 …

【分治专题】详解快排类型4道题

本文讲解分治下的快排类型的4道题&#xff0c;在讲解题目的同时提供AC代码&#xff0c;点击题目即可打开对应链接 目录 1、颜色分类 2、排序数组 3、数组中的第K个最大元素 4、库存管理 III 1、颜色分类 解法&#xff08;快排思想--三指针法使数组分三块&#xff09;&…

专业建设数字平台

专业建设数字平台&#xff0c;遵循以学生发展为中心、以产出为导向的OBE理念&#xff0c;大数据赋能人才培养全过程管理&#xff0c;支撑专业数字化建设与专业认证等专项工作&#xff0c;平台围绕学院与专业建设中人才培养方案制定的顶层设计工作及全流程、全场景业务&#xff…

ARM常用汇编指令

文章目录 前言一、处理器内部数据传输指令MOV&#xff1a; 将数据从一个寄存器复制到另一个寄存器。MRS&#xff1a; 将特殊寄存器(CPSR,SPSR)中的数据传给通用寄存器。MSR&#xff1a; 将通用寄存器中的数据传给特殊寄存器(CPSR,SPSR)。 二、存储器访问指令LDR:用于从内存中加…

力扣面试题 16.06. 最小差

Problem: 面试题 16.06. 最小差 文章目录 题目描述思路即解法复杂度Code 题目描述 思路即解法 注意本题目的数据范围!!! 1.对数组a与数组b进行排序;获取a与b的数组长度aLen,bLen&#xff0c;定义一个long类型的变量min&#xff1b; 2.分别让两个指针i&#xff0c;j指向数组的开…

56. 合并区间 - 力扣(LeetCode)

题目描述 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 题目示例 输入&#xff1a;intervals [[1,3…

LeNet跟LeNet5详解

1 LeNet结构 主要是为了手写数字识别 具体结构讲解&#xff1a;从图中例子可得 1 先传入一个灰度图像尺寸为1x28x28&#xff0c;通道数为1&#xff0c;尺寸为28x28的灰度图像 2 第一层5x5卷积&#xff0c;经过公式 输入图像尺寸-卷积核尺寸2padding/步长1&#xff0c;&#…