企业网站建设参考资料/东莞网站定制开发

企业网站建设参考资料,东莞网站定制开发,在excel中怎么做邮箱网站,网站开发上证k线文章目录 前言一、排序概念及常见排序算法框图1.排序概念2.常见排序算法框图 二、实现比较排序算法1.插入排序1.1 直接插入排序1.2 希尔排序 2.选择排序2.1 直接选择排序2.2 堆排序 3.交换排序3.1 冒泡排序3.2 快速排序3.2.1 hoare版本3.2.2 挖坑法3.2.3 lomuto前后指针 3.3 快…

文章目录

  • 前言
  • 一、排序概念及常见排序算法框图
    • 1.排序概念
    • 2.常见排序算法框图
  • 二、实现比较排序算法
    • 1.插入排序
      • 1.1 直接插入排序
      • 1.2 希尔排序
    • 2.选择排序
      • 2.1 直接选择排序
      • 2.2 堆排序
    • 3.交换排序
      • 3.1 冒泡排序
      • 3.2 快速排序
        • 3.2.1 hoare版本
        • 3.2.2 挖坑法
        • 3.2.3 lomuto前后指针
      • 3.3 快速排序的复杂度讨论及其优化
        • 3.3.1 快速排序的复杂度
        • 3.3.2 随机选取基准值 和 三数取中选取基准值
        • 3.3.3 三路划分
    • 4.归并排序
      • 4.1 归并排序的思想及实现
  • 三、比较排序算法总结
    • 1.复杂度及稳定性分析
    • 2.测试代码:比较排序性能对比
  • 四、实现非比较排序算法
    • 1.计数排序


前言

1.插入排序(直接插入排序 和 希尔排序)、选择排序(直接选择排序 和 堆排序)、交换排序(冒泡排序 和 快速排序)和归并排序
2.重点内容:快速排序(三个版本的算法思路及代码实现 以及 快速排序的复杂度讨论及其优化方法:随机选取基准值 、三数取中选取基准值 和 三路划分)


一、排序概念及常见排序算法框图

1.排序概念

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

2.常见排序算法框图

在这里插入图片描述
非比较排序中暂只介绍计数排序

二、实现比较排序算法

1.插入排序

1.1 直接插入排序

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

void direct_insert_sort(int* arr, int n)
{for (int i = 0; i < n - 1; i++){int end = i;int tmp = arr[end + 1];while (end >= 0){if (tmp < arr[end]){arr[end + 1] = arr[end];end--;}elsebreak;}arr[end + 1] = tmp;}
}

在这里插入图片描述
直接插入排序的特性总结:

  1. 元素集合越接近有序,直接插入排序算法的时间效率越高
  2. 时间复杂度(按最坏情况):O(N^2)
  3. 空间复杂度:O(1)

1.2 希尔排序

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数gap(通常 gap = n/2 或 n/3+1,n为数组元素个数),把待排序数据分成gap组,所有的距离相等的数据分在同⼀组内,并对每⼀组内的数据进行插入排序,然后 gap = n/2(或 n/3+1)得到下一个整数,再将数组分成gap组,对每⼀组内的数据排序,当gap=1时,就相当于直接插入排序。
直接插入排序算法的缺陷是它的时间复杂度在最优情况和最差情况下相差极大。希尔排序在直接插入排序算法的基础上进行改进,通过几轮分组排序(轮次越后面的排序,数组会越有序,效率会更高)使数组在最后一轮直接插入排序时接近有序,最后一轮直接插入排序效率会接近O(N)。
综合来说,希尔排序的效率肯定是要高于直接插入排序算法的。

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

void shell_sort(int* arr, int n)
{int gap = n;while(gap>1){ gap = gap / 2;for (int i = 0; i < n - gap; i++){int end = i;int tmp = arr[end + gap];while (end >= 0){if (tmp < arr[end]){arr[end + gap] = arr[end];end -= gap;}elsebreak;}arr[end + gap] = tmp;}}
}

希尔排序的时间复杂度取决于增量序列的选择(gap划分方法的选择),不同增量序列会导致不同的性能表现:

(1)原始希尔序列 n / 2 , n / 4 , … , 1 n/2, n/4, \dots, 1 n/2,n/4,,1

  • 最坏情况 O ( n 2 ) O(n^2) O(n2)(与直接插入排序相同)
  • 平均情况:实际应用中约为 O ( n 1.5 ) O(n^{1.5}) O(n1.5),但缺乏严格证明。

(2)Hibbard 序列 1 , 3 , 7 , 15 , … , 2 k − 1 1, 3, 7, 15, \dots, 2^k-1 1,3,7,15,,2k1

  • 最坏情况 O ( n 3 / 2 ) O(n^{3/2}) O(n3/2)
  • 平均情况:接近 O ( n 5 / 4 ) ) O(n^{5/4})) O(n5/4))

(3)Sedgewick 序列 1 , 5 , 19 , 41 , … 1, 5, 19, 41, \dots 1,5,19,41,

  • 最坏情况 O ( n 4 / 3 ) O(n^{4/3}) O(n4/3)
  • 平均情况:接近 O ( n log ⁡ 2 n ) O(n \log^2 n) O(nlog2n),是已知最优增量序列之一。
增量序列最坏时间复杂度平均时间复杂度空间复杂度
原始序列 O ( n 2 ) O(n^2) O(n2) O ( n 1.5 ) O(n^{1.5}) O(n1.5)(经验) O ( 1 ) O(1) O(1)
Hibbard 序列 O ( n 3 / 2 ) O(n^{3/2}) O(n3/2) O ( n 5 / 4 ) O(n^{5/4}) O(n5/4) O ( 1 ) O(1) O(1)
Sedgewick 序列 O ( n 4 / 3 ) O(n^{4/3}) O(n4/3) O ( n log ⁡ 2 n ) O(n \log^2 n) O(nlog2n) O ( 1 ) O(1) O(1)

实际应用中,希尔排序通常比 O ( n 2 ) O(n^2) O(n2) 的算法(如冒泡排序)快得多,但不如 O ( n log ⁡ n ) O(n \log n) O(nlogn) 的算法(如快速排序、归并排序)。

2.选择排序

2.1 直接选择排序

每⼀次从待排序的数据元素中选出最小(或最大)的⼀个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
在这里插入图片描述
优化版本:每⼀次从待排序的数据元素中选出一组最小值和最大值,分别存放在序列的起始位置和末尾位置,一次可以确定两个元素的位置,直到全部待排序的数据元素排完 。
在这里插入图片描述
在这里插入图片描述

void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}void direct_select_sort(int* arr, int n)
{int left = 0;int right = n - 1;while (left < right){int min = left;int max = left;int cur = left + 1;while (cur <= right){if (arr[cur] > arr[max])max = cur;if (arr[cur] < arr[min])min = cur;cur++;}swap(&arr[left], &arr[min]);if (left == max)max = min;swap(&arr[right], &arr[max]);left++;right--;}
}

时间复杂度计算:
第一次遍历n个元素的数组,确认一组最大最小值;
第二次遍历n-2个元素的数组,确认一组最大最小值;
……
最后一次遍历2(或3)个元素的数组。
所以计算式子为:2+4+……+(n-2)+ n = n ^ 2 + n
最终得到时间复杂度为:O(N^2)

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

  1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
  2. 时间复杂度: O(N^2)
  3. 空间复杂度: O(1)

2.2 堆排序

堆是一棵完全二叉树,使用顺序结构的数组来存储数据。堆中某个结点的值总是不大于(或不小于)其父结点的值,将根结点最大的堆叫做大堆或大根堆,根结点最小的堆叫做小堆或小根堆。
在这里插入图片描述
在这里插入图片描述

核心思想总结
利用堆的性质高效地找到最大值(或最小值)并逐步完成排序。 其核心在于:

  1. 构建堆:将无序数组转化为堆结构。
  2. 反复提取极值:每次提取堆顶元素后调整堆,最终得到有序序列。
#include <queue>
#include <vector>
using namespace std;void heap_sort(int* arr, int n)
{priority_queue<int,vector<int>,greater<int>> hp; // priority_queue是C++的STL库中自带的堆结构,直接调用priority_queue生成一个小堆。// C语言要使用堆排序还需要自己先实现一个堆结构for (int i = 0; i < n; i++){hp.push(arr[i]); // 先把数组中所有元素插入小堆中(将无序数组转化为堆结构)} int i = 0;while (!hp.empty()){arr[i++] = hp.top(); // 每次提取堆顶元素后调整堆,最终得到有序序列hp.pop();} 
}

堆排序的特性总结:

  1. 时间复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn)
  2. 空间复杂度: O ( 1 ) O(1) O(1)

3.交换排序

3.1 冒泡排序

冒泡排序的核心思想就是:两两相邻的元素进行比较,满足条件进行交换;一趟冒泡排序确定一个数的位置。
在这里插入图片描述
在这里插入图片描述

冒泡排序改良:
(1)设置一个变量,让它可以判断一趟冒泡排序中有无进行数组元素交换。
(2)当一趟冒泡排序中无任何一次数组元素交换,说明数组中元素已经有序,这时可提前结束循环。

void bubble_sort(int* arr, int n)
{for (int i = 0; i < n; i++){int exchange = 0; // 设置变量exchange,用来判断一趟冒泡排序中有无进行数组元素交换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;}}
}

时间复杂度计算(按最坏情况):
每趟冒泡排序确定一个元素的位置,所以n个元素要进行n-1次冒泡排序。
第一趟冒泡排序要进行n-1次比较,确认一个元素的位置;
第二趟冒泡排序要进行n-2次比较(比上一趟少进行一次比较,因为确定了位置的元素无需再进行比较),再确认一个元素的位置;
……
依此类推,第n-1趟冒泡排序只需要进行1次比较
所以计算式子为:1+2+……+(n-2)+(n-1)=(n ^ 2)/ 2 - n/2
最终得到时间复杂度为:O(N^2)

冒泡排序的特性总结:

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

3.2 快速排序

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

3.2.1 hoare版本

算法思路 :

  1. 确定基准值(此处直接选择最左元素为基准值),用key标记其下标;创建left和right标记,left起始指向key+1位置,right起始指向末尾位置
  2. 从右向左找出比基准值小的数据,用下标right标记;从左向右找出比基准值大的数据,用下标left标记;然后right和left指向的数据互换,进入下次循环;期间一旦left>right,循环直接终止
  3. 结束循环后,right所在的位置就是基准值的正确位置,把基准值交换到right位置
  4. 以基准值位置划分数组,左边的数组<=基准值,右边的数组>=基准值
  5. 接着对左边和右边数组进行以上同样的操作,不断划分数组,直到无法再进行划分
    在这里插入图片描述
void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}void hoare_quicksort(int* arr, int begin, int end)
{int key = begin;int left = begin + 1;int right = end;while (left <= right){while (left <= right && arr[right] >= arr[key])right--;while (left <= right && arr[left] <= arr[key])left++;if (left <= right){swap(&arr[left], &arr[right]);right--;left++;}}swap(&arr[key], &arr[right]);key = right; // [begin,key-1] key [key+1,end]if (begin < key - 1)hoare_quicksort(arr, begin, key - 1);if (key + 1 < end)hoare_quicksort(arr, key + 1, end);
}
3.2.2 挖坑法

算法思路 :

  1. 确定基准值(此处直接选择最左元素为基准值),创建变量key保存基准值;创建left和right标记,left起始指向开头位置(起始以left指向位置为"坑"位),right起始指向末尾位置
  2. 首先移动right标记从右向左找出比基准值小的数据,找到后立即放入左边"坑"位中,当前位置变为新的"坑"位,然后移动left从左向右找出比基准值大的数据,找到后立即放入右边"坑"位中,当前位置变为新的"坑",循环进行此过程,过程中一旦left和right相遇,循环立即终止。
  3. 结束循环后,当前的"坑"位就是基准值的正确位置,将最开始用变量key存储的基准值放入当前的"坑"位中
  4. 以基准值位置划分数组,左边的数组<=基准值,右边的数组>=基准值
  5. 接着对左边和右边数组进行以上同样的操作,不断划分数组,直到无法再进行划分
    在这里插入图片描述
    在这里插入图片描述
void digholes_quicksort(int* arr, int begin, int end)
{int key = arr[begin];int left = begin;int right = end;while (left < right){while (left < right && arr[right] >= key)right--;if (left == right)break;elsearr[left] = arr[right];while (left < right && arr[left] <= key)left++;if (left == right)break;elsearr[right] = arr[left];}arr[right] = key; // [begin,right-1] right [right+1,end]if (begin < right - 1)digholes_quicksort(arr, begin, right - 1);if (right + 1 < end)digholes_quicksort(arr, right + 1, end);
}
3.2.3 lomuto前后指针

算法思路 :

  1. 确定基准值(此处直接选择最左元素为基准值),用key标记其下标;创建prev和cur标记,prev起始指向开头位置,cur起始指向prev+1位置
  2. 首先移动cur标记从左向右找出比基准值小的数据,找到之后,prev先后移一位,然后将cur和prev指向的数据交换,紧接着cur后移一位,继续循环进行此过程,直到cur越界,循环才终止。
  3. 结束循环后,prev所在的位置就是基准值的正确位置,把基准值交换到prev位置
  4. 以基准值位置划分数组,左边的数组<=基准值,右边的数组>=基准值
  5. 接着对左边和右边数组进行以上同样的操作,不断划分数组,直到无法再进行划分
    在这里插入图片描述
    在这里插入图片描述
void lomuto_quicksort(int* arr, int begin, int end)
{int key = begin;int prev = begin;int cur = prev + 1;while (cur <= end){while (cur <= end && arr[cur] >= arr[key])cur++;if (cur <= end){prev++;swap(&arr[cur], &arr[prev]);cur++;}}swap(&arr[key], &arr[prev]);key = prev; // [begin,key-1] key [key+1,end]if (begin < key - 1)lomuto_quicksort(arr, begin, key - 1);if (key + 1 < end)lomuto_quicksort(arr, key + 1, end);
}

3.3 快速排序的复杂度讨论及其优化

3.3.1 快速排序的复杂度

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

时间复杂度分析:

  1. 最好情况

    • 每次划分都能将数组均匀分成两部分(每次选的基准值都接近中位数)。
    • 递归深度为 log ⁡ n \log n logn,每层需遍历 n n n 个元素(实际就第一层需遍历n个元素,每层递归都会确定一些元素的位置,递归层数越深,需遍历的元素越少,但统一按每层遍历n个元素计算比较方便)。
    • 时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn)
  2. 平均情况

    • 假设基准值通过随机选择或三数取中法选出,划分的均衡性服从概率分布。
    • 数学期望计算后仍为 O ( n log ⁡ n ) O(n \log n) O(nlogn)
    • 时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn)
  3. 最坏情况

    • 每次划分极不均衡(如数组已有序且固定选第一个元素为基准值)。
    • 递归深度为 n n n,总操作次数为 n + ( n − 1 ) + ⋯ + 1 = n ( n + 1 ) 2 n + (n-1) + \dots + 1 = \frac{n(n+1)}{2} n+(n1)++1=2n(n+1)
    • 时间复杂度 O ( n 2 ) O(n^2) O(n2)

空间复杂度分析:

  1. 最好情况

    • 递归深度为 log ⁡ n \log n logn,栈空间占用与递归深度成正比。
    • 空间复杂度 O ( log ⁡ n ) O(\log n) O(logn)
  2. 平均情况

    • 递归深度仍为 log ⁡ n \log n logn(通过随机化选择基准值减少最坏概率)。
    • 空间复杂度 O ( log ⁡ n ) O(\log n) O(logn)
  3. 最坏情况

    • 递归深度为 n n n(如每次仅处理一个元素)。
    • 空间复杂度 O ( n ) O(n) O(n)
  • 优化策略随机选择或三数取中法选择基准值,可有效降低与最坏情况1类似场景(数组有序或部分有序)的时间和空间复杂度 ;三路划分法 可有效降低与最坏情况2类似场景(数组存在大量重复数据)的时间和空间复杂度。
  • 空间占用虽为原地排序,但需注意递归栈的隐式空间消耗。
3.3.2 随机选取基准值 和 三数取中选取基准值

决定快排性能的关键点是每次单趟排序后,基准值对数组的分割,如果每次选的基准值都接近数组中的中位数,那么快排的递归树就是颗均匀的满二叉树,性能最佳。但是实践中虽然不可能每次都是二分居中,但是性能也还是可控的。但是如果出现每次选到最小值/最大值,划分为0个和N-1的子问题时,时间复杂度为O(N^2),数组有序并且每次固定选第一个元素为基准值前不进行任何干预,就会出现这样的问题。
我们仍然可以采用第一个元素作为基准值的方法,不过在那之前进行随机选取 或 三数取中法选出基准值,再把选出的基准值换到第一个元素,这样可以有效减少因数组有序或部分有序而降低快排效率的情况。

  1. 随机选取基准值
    每次排序时,从当前数组中随机选择一个元素作为基准值,再通过交换操作将其移动到数组的起始位置,最后进行常规的划分操作。
#include <time.h>
#include <stdlib.h>
void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}void random_select(int* arr, int begin, int end)
{srand((unsigned)time(NULL));// 随机选择基准值的位置,选择区间在begin和end之间int key = rand() % (end - begin + 1) + begin;//把基准值位置的元素与begin位置元素互换,后续依然可以选第一个元素为基准值划分函数  swap(&arr[key], &arr[begin]);
}
  1. 三数取中选取基准值
    从当前子数组的首、中、尾三个位置取元素,选择三者的中位数作为基准值,通过交换操作将其移动到数组的起始位置,最后进行常规的划分操作。
void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}void median_select(int* arr, int begin, int end)
{int mid = begin + ((end - begin) >> 1);//计算数组中间的元素的下标  // 使用三数取中法选择三者的中位数作为基准值并将其换到begin位置 if (arr[mid] < arr[end])//目标: arr[mid] >= arr[end]  {swap(&arr[mid], &arr[end]);}if (arr[begin] > arr[mid])//目标: arr[begin] <= arr[mid]  {swap(&arr[begin], &arr[mid]);}if (arr[begin] < arr[end]) //目标: arr[begin] >= arr[end]  {swap(&arr[begin], &arr[end]);}//此时,arr[mid] >= arr[begin] >= arr[end]  //begin位置上保存这三个位置中间的值,后续依然可以选第一个元素为基准值划分函数  
}
3.3.3 三路划分

快速排序的三路划分是一种优化策略,用于处理数组中存在大量重复元素的情况。 它将数组划分为三个区域:

  • 小于基准值的部分(左段)
  • 等于基准值的部分(中段)
  • 大于基准值的部分(右段)

通过将重复元素集中到中间段,后续递归时只需处理左右两段,避免对重复元素重复操作,从而提升效率。

算法思路(类似hoare的左右指针和lomuto的前后指针的结合) :

  1. 确定基准值(直接选择最左元素为基准值),创建变量key保存基准值;创建 left、right 和 cur 标记,left起始指向开头位置,right起始指向末尾位置,cur起始指向left+1位置
  2. cur标记从左向右移动会遇到三种不同的情形,每种情形都会有对应的处理方式:
  • 情形一:cur遇到比基准值小的值后跟left位置交换,换到左边,left++,cur++;
  • 情形二:cur遇到比基准值大的值后跟right位置交换,换到右边,right–;
  • 情形三:cur遇到跟基准值相等的值后,cur++。
  1. 直到cur > right才结束以上过程
  2. 以 left 和 right位置划分数组,左边的数组<基准值,右边的数组>基准值
  3. 接着对左边和右边数组进行以上同样的操作,不断划分数组,直到无法再进行划分
    在这里插入图片描述

4.归并排序

4.1 归并排序的思想及实现

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

在这里插入图片描述

void merge_sort(int* arr, int begin, int end, int* tmp)
{int mid = begin + (end - begin) / 2;if (begin < mid)merge_sort(arr, begin, mid, tmp);if (mid + 1 < end)merge_sort(arr, mid + 1, end, tmp);int left1 = begin;int right1 = mid;int left2 = mid + 1;int right2 = end;int n = begin;while (left1 <= right1 && left2 <= right2){if (arr[left1] <= arr[left2])tmp[n++] = arr[left1++];elsetmp[n++] = arr[left2++];}while (left1 <= right1){tmp[n++] = arr[left1++];}while (left2 <= right2){tmp[n++] = arr[left2++];}while (begin <= end){arr[begin] = tmp[begin];begin++;}
}

三、比较排序算法总结

1.复杂度及稳定性分析

算法 最好 最坏 平均时间复杂度 空间 稳定 冒泡排序 O ( n ) O ( n 2 ) O ( n 2 ) O ( 1 ) ✓ 直接插入排序 O ( n ) O ( n 2 ) O ( n 2 ) O ( 1 ) ✓ 归并排序 O ( n log ⁡ n ) O ( n log ⁡ n ) O ( n log ⁡ n ) O ( n ) ✓ 直接选择排序 O ( n 2 ) O ( n 2 ) O ( n 2 ) O ( 1 ) ✗ 堆排序 O ( n log ⁡ n ) O ( n log ⁡ n ) O ( n log ⁡ n ) O ( 1 ) ✗ 希尔排序 O ( n log ⁡ n ) O ( n 2 ) O ( n 1.3 ) O ( 1 ) ✗ 快速排序 O ( n log ⁡ n ) O ( n 2 ) O ( n log ⁡ n ) O ( log ⁡ n ) ✗ \begin{array}{|l|c|c|c|c|c|} \hline \text{算法} & \text{最好} & \text{最坏} & \text{平均时间复杂度} & \text{空间} & \text{稳定} \\ \hline 冒泡排序 & O(n) & O(n^2) & O(n^2) & O(1) & ✓ \\ 直接插入排序 & O(n) & O(n^2) & O(n^2) & O(1) & ✓ \\ 归并排序 & O(n \log n) & O(n \log n) & O(n \log n) & O(n) & ✓ \\ 直接选择排序 & O(n^2) & O(n^2) & O(n^2) & O(1) & ✗ \\ 堆排序 & O(n \log n) & O(n \log n) & O(n \log n) & O(1) & ✗ \\ 希尔排序 & O(n \log n) & O(n^2) & O(n^{1.3}) & O(1) & ✗ \\ 快速排序 & O(n \log n) & O(n^2) & O(n \log n) & O(\log n) & ✗ \\ \hline \end{array} 算法冒泡排序直接插入排序归并排序直接选择排序堆排序希尔排序快速排序最好O(n)O(n)O(nlogn)O(n2)O(nlogn)O(nlogn)O(nlogn)最坏O(n2)O(n2)O(nlogn)O(n2)O(nlogn)O(n2)O(n2)平均时间复杂度O(n2)O(n2)O(nlogn)O(n2)O(nlogn)O(n1.3)O(nlogn)空间O(1)O(1)O(n)O(1)O(1)O(1)O(logn)稳定

2.测试代码:比较排序性能对比

用以上各种比较排序算法排序同一组随机生成的数据,测试时间(单位:毫秒)消耗,要在Release版本运行这段代码(含递归的算法会在Debug版本生成更多调试数据,影响算法效率):

#include "sort.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>void TestOP()
{srand(time(0));const int N = 50000;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* tmp = (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();direct_insert_sort(a1, N);int end1 = clock();int begin2 = clock();shell_sort(a2, N);int end2 = clock();int begin3 = clock();direct_select_sort(a3, N);int end3 = clock();int begin4 = clock();heap_sort(a4, N);int end4 = clock();int begin5 = clock();hoare_quicksort(a5, 0, N - 1);int end5 = clock();int begin6 = clock();merge_sort(a6, 0, N - 1, tmp);int end6 = clock();int begin7 = clock();bubble_sort(a7, N);int end7 = clock();printf("direct_insert_sort:%d\n", end1 - begin1);printf("shell_sort:%d\n", end2 - begin2);printf("direct_select_sort:%d\n", end3 - begin3);printf("heap_sort:%d\n", end4 - begin4);printf("hoare_quicksort:%d\n", end5 - begin5);printf("merge_sort:%d\n", end6 - begin6);printf("bubble_sort:%d\n", end7 - begin7);free(a1);free(a2);free(a3);free(a4);free(a5);free(a6);free(a7);free(tmp);
}int main()
{TestOP();return 0;
}

在这里插入图片描述

四、实现非比较排序算法

1.计数排序

计数排序是对哈希直接定址法的变形应用。 操作步骤:
1)统计相同元素出现次数
2)根据统计的结果将序列回收到原来的序列中

在这里插入图片描述
"找出最大值k,创建长度为k+1的计数数组"的方式有些时候会造成很大空间浪费,可以找出待排序数组中的最大最小值,按范围开空间:
在这里插入图片描述

void count_sort(int* arr, int n)
{int 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];} int range = max - min + 1;int* count = (int*)calloc(range, sizeof(int));if (count == NULL){perror("calloc fail");return;}// 统计次数for (int i = 0; i < n; i++){count[arr[i] - min]++; } // 排序int j = 0;for (int i = 0; i < range; i++){while (count[i]--){arr[j++] = i + min;}}free(count);
}

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

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

相关文章

电动自行车/电动工具锂电池PCM方案--SH367003、SH367004、SH79F329

在消费电子系统中&#xff0c;如手机电池包&#xff0c;笔记本电脑电池包等&#xff0c;带有控制IC、功率MOSFETFE管以及其他电子元件的电路系统称为电池充放电保护板Protection Circuit Module &#xff08;PCM&#xff09;&#xff0c;而对于动力电池的电池管理系统&#xff…

【基于ROS的A*算法实现路径规划】A* | ROS | 路径规划 | Python

### 记录一下使用Python实现ROS平台A*算法路径规划 ### 代码可自取 &#xff1a;Xz/little_projecthttps://gitee.com/Xz_zh/little_project.git 目录 一、思路分析 二、算法实现 三、路径规划实现 一、思路分析 要求使用A*算法实现路径规划&#xff0c;可以将该任务分为三…

2025-03-23 吴恩达机器学习3——多维特征

文章目录 1 多元引入2 矢量化2.1 示例2.2 非矢量化实现2.3 矢量化实现2.4 应用 3 特征缩放3.1 举例3.2 必要性3.3 方法3.3.1 最大最小值缩放&#xff08;Min-Max Scaling&#xff09;3.3.2 均值归一化&#xff08;Mean Normalization&#xff09;3.3.3 Z 分数归一化&#xff08…

正点原子内存管理学习和修改

由于项目需要用到内存管理进行动态申请和释放&#xff0c;今天又重新学习了一下正点原子的内存管理实验&#xff0c;温习了一下内存管理的实质。首先先上正点原子内存管理的源代码&#xff1a; malloc.c文件&#xff1a; #include "./MALLOC/malloc.h"#if !(__ARMC…

【Centos7搭建Zabbix4.x监控HCL模拟网络设备:zabbix-server搭建及监控基础05

兰生幽谷&#xff0c;不为莫服而不芳&#xff1b; 君子行义&#xff0c;不为莫知而止休。 5.zabbix监控HCL模拟网络设备 在保证zabbix-server与HCL网络相通的情况下进行如下操作。 5.1创建主机群 配置-主机群-创建主机群 图 19 取名&#xff0c;添加。 图 20 5.2 创建监控…

趣味极简品牌海报艺术贴纸设计圆润边缘无衬线粗体装饰字体 Chunko Bold - Sans Serif Font

Chunko Bold 是一种功能强大的显示字体&#xff0c;体现了大胆极简主义的原则 – 当代设计的主流趋势。这种自信的字体将粗犷的几何形状与现代的趣味性相结合&#xff0c;具有圆润的边缘和强烈的存在感&#xff0c;与当今的极简主义设计方法完美契合。无论是用于鲜明的构图还是…

2025-03-21 Unity 序列化 —— 自定义2进制序列化

文章目录 前言1 项目结构1.1 整体1.2 代码 2 实现2.1 Processor2.1.1 BaseType2.1.2 CollectionType2.1.3 CustomType 2.2 ByteFormatter2.3 ByteHelper 3 使用 前言 ​ BinaryFormatter 类可以将 C# 类对象快速转换为字节数组数据。 ​ 在网络开发时&#xff0c;不会使用 Bi…

嵌入式项目:利用心知天气获取天气数据实验方案

【实验目的】 1、利用心知天气服务器获取指定位置天气数据 2、将天气数据解析并可视化显示到OLED屏幕 【实验原理】 【实验步骤】 官网注册

LabVIEW FPGA与Windows平台数据滤波处理对比

LabVIEW在FPGA和Windows平台均可实现数据滤波处理&#xff0c;但两者的底层架构、资源限制、实时性及应用场景差异显著。FPGA侧重硬件级并行处理&#xff0c;适用于高实时性场景&#xff1b;Windows依赖软件算法&#xff0c;适合复杂数据处理与可视化。本文结合具体案例&#x…

深度解析 Android Matrix 变换(二):组合变换 pre、post

前言 在上一篇文章中&#xff0c;我们讲解了 Canvas 中单个变换的原理和效果&#xff0c;即缩放、旋转和平移。但是单个旋转仅仅是基础&#xff0c;Canvas 变换最重要的是能够随意组合各种变换以实现想要的效果。在这种情况下&#xff0c;就需要了解如何组合变换&#xff0c;以…

FAQ - VMware vSphere Web 控制台中鼠标控制不了怎么办?

问题描述 在VMware vSphere vCenter Server 的 Web 控制台中新建了一台 Windows Server 2008 R2 虚拟机&#xff0c;但是鼠标进入控制台后&#xff0c;可以看见鼠标光标&#xff0c;但是移动却没有反应。 根因分析 暂无。 解决方案 选中虚拟机>操作>编辑设置>添加新…

关于极端场景下,数据库更新与 MQ 消息一致性保障方案的详细总结

目录 一、核心问题场景 二、RocketMQ 事务消息方案 1. 核心机制 2. 执行流程 3. 关键优势 4. 局限性 三、消息表方案 1. 核心机制 2. 执行流程 3. 关键优势 4. 局限性 四、方案对比与选择 五、实施建议 六、总结 一、核心问题场景 当数据库更新后,若 MQ 消息未…

【redis】主从复制:拓扑结构、原理和psync命令解析

文章目录 拓扑一主一从相关问题 一主多从相关问题 树形主从结构相关问题 主从复制原理复制流程 psync 命令命令解析replicatonidoffset总结 运行流程 拓扑 若干个节点之间按照什么样的方式来进行组织连接 一主一从 都可以读&#xff0c;从节点可以帮主节点分担一部分的压力只…

[RoarCTF 2019]Easy Calc-3.23BUUCTF练习day5(2)

[RoarCTF 2019]Easy Calc-3.23BUUCTF练习day5(2) 解题过程 查看源码 发现calc.php页面&#xff0c;访问一下 分析代码 首先获取$_GET[num]的值并赋给变量$str。然后定义了一个黑名单数组$blacklist&#xff0c;包含了一系列被禁止的字符或转义字符&#xff0c;如空格、制表…

阻塞队列:原理、应用及实现

阻塞队列&#xff1a;原理、应用及实现 什么是阻塞队列以生产消费者模型形象地理解阻塞队列阻塞队列实现生产消费者模型模拟实现阻塞队列实现生产消费者模型 什么是阻塞队列 阻塞队列是一种特殊且实用的队列数据结构&#xff0c;它同样遵循 “先进先出” 的原则。与普通队列不…

【开源宝藏】30天学会CSS - DAY5 第五课 脉冲动画

以下是一个完整的渐进式教程&#xff0c;拆解如何用 HTML CSS 构建“Pulsar”水波脉冲动画。通过阅读&#xff0c;你将理解每个核心属性与关键帧如何配合&#xff0c;让一个小圆不断散发动态波纹&#xff0c;并且文字始终停留在圆心。 第 0 步&#xff1a;项目概览 文件结构示…

2060 裁纸刀

2060 裁纸刀 ⭐️难度&#xff1a;简单 &#x1f31f;考点&#xff1a;2022、规律、思维 &#x1f4d6; &#x1f4da; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class Main {static int N 100010…

python学习笔记--实现简单的爬虫(一)

任务&#xff1a;爬取豆瓣最受欢迎的250个电影的资料 链接&#xff1a;豆瓣电影 Top 250 用浏览器打开后&#xff0c;使用F12或鼠标右键--检查&#xff0c;查看网页的源代码&#xff0c;分析网页结构&#xff0c;如下图所示&#xff1a; 分析后得知&#xff1a; 1.电影名位于…

Cursor+Claude-3.5生成Android app

一、Android Studio下载 https://developer.android.com/studio?hlzh-tw#get-android-studio 等待安装完成 二、新建工程 点击new project 选择Empty Activity 起一个工程名 当弹出这个框时 可以在settings里面选择No proxy 新建好后如下 点击右边模拟器&#xff0c…

Java EE(15)——网络原理——TCP协议解析一

一.确认应答/(确认)序列号 接收方接收到数据后&#xff0c;向发送方返回一个确认信号(ack)&#xff0c;告诉发送方数据被成功接收。ACK报文段只是作为确认使用的&#xff0c;一般来说不携带应用层数据&#xff08;载荷&#xff09;&#xff0c;也就是说只有报头部分。但有可能…