当前位置: 首页 > news >正文

基本排序算法

排序

  • 插入排序
  • 希尔排序
  • 选择排序
  • 堆排序
  • 冒泡排序
  • 计数排序
  • 快排
    • 实现快排的几种方式:
      • 1.hoare版本
      • 2.前后指针法
      • 3.挖坑法
      • 4.快排非递归
  • 归并排序

插入排序

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

void InsertSort(int* a, int n)
{for (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;}}

插入排序的时间复杂度:O(N^2);

希尔排序

        希尔排序也是插入排序,它是插入排序的优化,插入排序有一个非常明显的缺点,如果插入排序数据是逆序的话,它的效率就下来了。

希尔排序大致分为两个步骤:

1.预排序(让数组接近有序)。
2.插入排序。
1.预排序。
设置一个gap(gap为间隔),间隔gap为一组,进行插入排序。
例如:假设gap = 3
在这里插入图片描述

先尝试一下对每个gap组进行排序:

void ShellSort(int* a, int n)
{int gap = 3;for(int j = 0;j<gap;j++)//gap组依次排序{for (int i = j; i < n-gap; i + gap)//每组排序{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 ShellSort(int* a, int n)
{int gap = 3;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;}
}

关于gap:
gap越大,大的可以越快跳到后面,小的数可以越快跳到前面,越不接近有序
gap越小,跳得越慢,但是越接近有序。当gap==1相当于插入排序就有序了
所以gap到底取多少呢?
有人算出:gap = gap/3+1比较合理。

希尔排序代码:

void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){//gap>1就是预排序//gap==1就是插入排序gap = gap / 3 + 1;//+1保证最后一个gap一定等于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;}}}

希尔排序的时间复杂度:O(N^1.3).

选择排序

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

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[maxi]){maxi = i;}if (a[i] < a[mini]){mini = i;}}Swap(&a[begin], &a[mini]);if (a[begin] == a[maxi])Swap(&a[end], &a[mini]);elseSwap(&a[end], &a[maxi]);++begin;--end;}
}

时间复杂多:O(N^2);(不推荐)

堆排序

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void AdjustDown(int* a, int n, int parent)
{//要对比左,右孩子的大小//假设左孩子小int child = parent * 2 + 1;//找出小的那个孩子while (child < n)//chile>=n,越界了{if (child + 1 < n && a[child + 1]> a[child])//右孩子也要n{++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 = (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;}
}

具体见:堆的基本认识
时间复杂度:O(N*logN);

冒泡排序

在这里插入图片描述

void BubbleSort(int* a, int n)
{for (int j = 0; j < n ; j++){for (int i = 0; i < n -j; i++){if (a[i] > a[i + 1]){Swap(&a[i], &a[i + 1]);}}}}

计数排序

思想:计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。 操作步骤:

  1. 统计相同元素出现次数
  2. 根据统计的结果将序列回收到原来的序列中。
    如:对数组a进行计数排序
    在这里插入图片描述

相对映射:在以上a数组中,它的范围是0~10,那如果出现这样的一组数组呢?a[]={100,105,107,104,101,109} ,count数组如果从头开到109就太浪费空间了,就想要进行相对映射。
在这里插入图片描述
什么是相对映射?简单来说,就是count数组按范围开空间,其下标就对应代表a数组其中的某一个值。
在这里插入图片描述

计数排序的代码:

void CountSort(int* a, int n)
{
//求范围int min = a[0];int max = a[0];for (int i = 0; i < n; i++){if (a[i] < min)min = a[i];if (a[i] > max)max = a[i];}int range = max - min + 1;//开辟count数组int* count = (int*)calloc(range, sizeof(int));//要判空if (count == NULL){perror("calloc fail");return;}//计数for (int i = 0; i < range; i++){count[a[i] - min]++;}//排序int j = 0;for (int i = 0; i < range; i++){while (count[i]--){a[j++] = min + i;}}free(count);
}

时间复杂度:O(MAX(N,范围))
空间复杂度:O(范围)

快排

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

实现快排的几种方式:

void QuickSort(int* a, int left, int right)
{if (left >= right)return;//结束条件int keyi = PartSort1(a, left, right);//hoare版本int keyi = PartSort2(a, left, right);//前后指针法// [left, keyi-1] keyi [keyi+1, right]QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);
}

关于优化:
1.三数取中
2.小区间优化

1.hoare版本

在这里插入图片描述

//小区间优化if ((right - left + 1) < 10){InsertSort(a, right - left + 1);}else{// 三数取中int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int keyi = left;int begin = left, end = right;while (begin < end){// 右边找小while (begin < end && a[end] >= a[keyi]){--end;}// 左边找大while (begin < end && a[begin] <= a[keyi]){++begin;}Swap(&a[begin], &a[end]);}Swap(&a[keyi], &a[begin]);return begin;}}

2.前后指针法

在这里插入图片描述

int PartSort2(int* a, int left, int right)
{// 三数取中int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int keyi = left;int prev = left;int cur = prev + 1;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur)Swap(&a[prev], &a[cur]);cur++;}Swap(&a[prev], &a[keyi]);return prev;
}

3.挖坑法

在这里插入图片描述

4.快排非递归

为避免快排的深度太深,可以采用非递归。

#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 = PartSort2(a, begin, end);if (keyi + 1 < end){STPush(&st, end);STPush(&st, keyi+1);}if (begin < keyi - 1){STPush(&st, keyi-1);STPush(&st, begin);}}}

为什么左边做key,右边先走,可以保证相遇位置比key小?
相遇的场景分析:
L遇R:R先走,停下来,R停下条件是遇到比key小的值,R停的位置一定比key小L没有找大的,遇到R停下了
在这里插入图片描述

R遇L:
R先走,找小,没有找到比key小的,
直接跟L相遇了。L停留的位置是上一轮交换的位置,上一轮交换,把比key小的值,换到L的位置了
在这里插入图片描述

归并排序

没写,不想写了

http://www.dtcms.com/a/392314.html

相关文章:

  • 学习Python中Selenium模块的基本用法(15:窗口操作)
  • 能力(1)
  • UE4/UE5 如何迁移HotPatcher插件
  • SQL从入门到起飞:完整数据库操作练习
  • MyBatis 从入门到进阶:数据库操作全指南
  • spring cloud 同一服务多实例 websocket跨实例无法共享Session 的解决
  • 如何通过pycharm使用AutoDL服务器
  • 【Linux】4G网卡-AT命令
  • 新版本附近停车场推荐系统demo,基于python+flask+协同推荐+空车位识别+yolov人工智能开发,开发语言python,数据库mysql
  • 《UE5_C++多人TPS完整教程》学习笔记55 ——《P56 网络更新频率(Net Update Frequency)》
  • 华为鸿蒙 ArkTS 实战:基于 RelationalStore 的 SQLite 实现本地数据持久化
  • 流行的 3D 文件格式及其用途指南
  • 腾讯发布一站式工作平台“混元3D Studio
  • TGRS2025 | 视觉语言模型 | 文本驱动自适应网络实现高光谱跨场景零样本分类
  • PyQt6之选项卡示例
  • 研学小程序前端平台开发项目需求规格说明书
  • 【Linux】netplan配置网络;ntp搭建时间服务器;shc将脚本转二进制;ty0tty创建虚拟串口
  • C# ADO.NET 操作学习记录
  • PIT 定时器寄存器配置
  • 算法代码讲座6:最小二乘法理论原理、典型案例与MATLAB实现
  • 【深入浅出】交叉熵损失函数——原理、公式与代码示例
  • Vue实现路由守卫
  • Coze源码分析-资源库-删除工作流-前端源码-核心接口
  • 安踏集团 X OB Cloud:新零售创新如何有“底”和有“数”
  • Web3艺术品交易应用方案
  • Spring 事务管理详解:保障数据一致性的实践指南
  • 软考中级-软件设计师 答题解题思路
  • Java IDEA学习之路:第二周课程笔记归纳
  • SQL语句一文通
  • Ubuntu22.04 双显卡系统使用集显 DRM 渲染的完整流程记录