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

排序算法专题

文章目录

      • 一、排序的基本概念
          • 算法的稳定性
          • 内部排序与外部排序
      • 二、插入排序
          • 直接插入排序
          • 希尔排序
      • 三、交换排序
          • 冒泡排序
          • 快速排序
      • 四、选择排序
          • 简单选择排序
          • 堆排序
      • 五、归并排序
          • 二路归并排序
          • 归并排序
      • 六、基数排序
          • 多关键字排序
          • 链式基数排序
      • 七、内部排序算法的比较

一、排序的基本概念

算法的稳定性
  • 关键字相同的元素经过排序后相对顺序是否会改变
内部排序与外部排序
  • 内部排序:数据都在内存中----关注时间、空间复杂度、稳定性
  • 外部排序:数据太多,无法全部放入内存----还需关注磁盘读取次数

二、插入排序

直接插入排序
  • 每次将一个待排序的记录按其关键字大小插入到前面已排好序的子序列中
  • 平均时间复杂度:O(n^2)
  • 算法稳定性:稳定
void InsertSort(int A[],int n){int i,j,temp;for(i=1;i<n;i++){if(A[i]<A[i-1]){temp=A[i];for(j=i-1;j>=0&&A[j]>temp;--j){A[j+1]=A[j]; // 从后往前检查,比当前元素大的右移}A[j+1]=temp;}}
}
希尔排序
  • 先追求表中元素部分有序,再逐渐逼近全局有序
  • 仅适用于顺序表
  • 算法过程:
    • 相隔增量d的元素组成同一个子表,对各个子表分别进行直接插入排序,缩小增量d,直到d=1为止
  • 算法稳定性:不稳定
    • 相同关键字划分到不同子表,可能改变相对次序
void ShellSort(int A[],int n){int i,j,temp,d;for(d=n/2;d>=1;d=d/2){for(i=d;i<n;i++){if(A[i]<A[i-d]){temp=A[i];for(j=i-d;j>=0&&A[j]>temp;j-=d){A[j+d]=A[j]; // 检查前面比当前元素大的右移}A[j+d]=temp;}}}
}

三、交换排序

冒泡排序
  • 从后往前(或从前往后)两两比较相邻元素的值,若为逆序则交换
  • 平均时间复杂度:O(n^2)
  • 算法稳定性:稳定
void BubbleSort(int A[],int n){int i,j,temp,flag;for(i=0;i<n-1;i++){flag=false;for(j=n-1;j>i;j--){if(A[j]<A[j-1]){ //相等不交换保持稳定temp=A[j];A[j]=A[j-1];A[j-1]=temp;flag=true;}}if(flag==false)return; //本趟遍历后没有发生交换,说明已经有序}
}
快速排序
  • 用枢轴元素pivot划分待排序表
  • 算法过程
    • 两个指针lowhigh向中间移动,将小于pivot的元素放到左边,大于pivot的元素放到右边,pivot元素放到最终位置上,再对左右递归
  • 快速排序是所有内部排序算法中平均性能最优的排序算法
  • 空间复杂度:
    • 最好O(log_2n)
    • 最坏O(n)
    • 平均O(log_2n)
  • 平均时间复杂度:O(nlog_2n)
  • 稳定性:不稳定
int Partition(int A[],int low,int high){int pivot=A[low];while(low<high){while(low<high&&A[high]>=pivot){--high;}A[low]=A[high]; // 比枢轴小的元素移动到左边while(low<high&&A[low]<=pivot){++low;}A[high]=A[low]; // 比枢轴大的元素移动到右边}A[low]=pivot;return low;
}void Quicksort(int A[],int low,int high){if(low<high){ //递归跳出条件int pivotpos=Partition(A,low,high);Quicksort(A,low,pivotpos-1);Quicksort(A,pivotpos+1,high);}}

四、选择排序

简单选择排序
  • 每一趟在待排元素中选取关键字最小(或最大)的元素加入有序子序列
  • 平均时间复杂度:O(n^2)
  • 稳定性:不稳定
void Selectsort(int A[],int n){int i,j,temp,min;for(i=0;i<n-1;i++){min=i;for(j=i+1;j<n;j++){if(A[j]<A[min]){min=j; //得到最小关键字的索引}}if(min!=i){temp=A[min];A[min]=A[i];A[i]=temp;}}
}
堆排序
  • 将待排序列整理成大根堆(或小根堆)后进行选择排序
  • 大根堆:根>=左、右
  • 建立大根堆:
    • 在顺序存储的完全二叉树中,非终端节点编号i<=⌊n/2⌋
    • 由后向前检查所有非终端节点,若不满足根>=左、右,将当前节点与更大的一个孩子互换
    • 若元素互换破坏了下一级的堆,则采用相同方法继续向下调整
    • 建堆的时间复杂度:O(n)
  • 输出堆顶元素:最后一个元素替换堆顶
  • 算法过程
    • 首先建立大根堆,不断输出堆顶元素,并将剩余元素序列再次调整为大根堆
  • 堆的插入删除
    • 插入:插入到堆底后“上升”
    • 删除:用堆底元素替换后“下坠”
  • 平均时间复杂度:O(nlog_2n)
  • 稳定性:不稳定
//建立大根堆
void BuildMaxHeap(int A[],int len){int i;for(i=len/2;i>0;i--){ //从后往前调整所有非终端节点HeadAdjust(A,i,len);}
}//将以k为根的子树调整为大根堆
void HeadAdjust(int A[],int k,int len){int i;A[0]=A[k]; //A[0]暂存防止覆盖for(i=2*k;i<=len;i*=2){if(i<len&&A[i]<A[i+1]){i++; //获取左右孩子中更大的}if(A[0]>A[i])break;else{A[k]=A[i]; //A[i]调整到双亲结点上k=i; //向下继续筛选}}A[k]=A[0]; //注意此时k值已经改变,为该节点最终位置
}// 堆排序
void HeapSort(int A[],int len){BuildMaxHeap(A,len);int temp;for(int i=len;i>1;i--){temp=A[i];A[i]=A[1];A[1]=temp; //堆顶元素和堆底元素互换HeadAdjust(A,1,i-1); //把剩余待排元素整理为大根堆}
}

五、归并排序

二路归并排序
  • 将相邻的两个有序表合并成一个
归并排序
  • 将n个待排元素分成两个长度相等的子序列,分别对两个子序列递归调用归并方法,再将两有序子序列二路归并排序
  • 空间复杂度:O(n)
  • 平均时间复杂度:O(nlog_2n)
  • 稳定性:稳定
int *B=(int *)malloc((n+1)*sizeof(int)); // 辅助数组Bvoid Merge(int A[],int low,int mid,int high){// mid分割两个待归并序列for(int k=low;k<=high;k++){B[k]=A[k]; //复制到辅助数组中}int i,j,k;for(i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){if(B[i]<=B[j]){A[k]=B[i++];}else{A[k]=B[j++];}}while(i<=mid)A[k++]=B[i++];while(j<=high){A[k++]=B[j++];}
}void MergeSort(int A[],int low,int high){if(low<high){int mid=(low+high)/2;MergeSort(A,low,mid);MergeSort(A,mid+1,high);Merge(A,low,mid,high); //归并}
}

六、基数排序

多关键字排序
  • 按照关键字各位大小排序
  • n:待排元素个数
  • d:单个元素分量的个数
  • rd:基数,每个分量的取值范围
  • 单逻辑关键字:文件中任意一关键字k均由d个分量构成,且每个分量都是单独的关键字
  • 排序方法:
    • 最高位优先法MSD–逐层分割成若干子序列
    • 最低位优先法LSD–不必分成若干子实现排序序列,不通过关键字比较,通过“分配”+“收集”实现排序
链式基数排序
  • 算法过程:
    • 将关键字拆分为d位(或组)
    • 按照各个关键字位权重递增的次序(LSD方法),做d趟分配O(n)和收集O(rd)
  • 空间复杂度:O(rd)
  • 时间复杂度:O(d(n+rd))
  • 稳定性:稳定

七、内部排序算法的比较

  • 各算法性质比较表
排序算法最好时间复杂度平均时间复杂度最坏时间复杂度空间复杂度(平均)是否稳定
直接(折半)插入排序O(n)O(n2)O(n2)O(1)稳定
希尔排序---O(1)不稳定
冒泡排序O(n)O(n2)O(n2)O(1)稳定
快速排序O(nlog2n)O(nlog2n)O(n2)O(log2n)不稳定
简单选择排序O(n2)O(n2)O(n2)O(1)不稳定
堆排序O(nlog2n)O(nlog2n)O(nlog2n)O(1)不稳定
归并排序O(nlog2n)O(nlog2n)O(nlog2n)O(n)稳定
基数排序O(d(n+rd))O(d(n+rd))O(d(n+rd))O(rd)稳定
  • 规律总结

    • 每次排序确定一个最终位置:插入排序,冒泡排序,选择排序,快速排序
    • 算法复杂度与初始状态无关:选择排序、堆排序、归并排序、基数排序
    • 比较次数与初始状态无关:选择排序、基数排序
    • 移动次数与初始状态无关:归并排序、基数排序
    • 排序趟数与初始状态有关:快速排序、冒泡排序
  • 根据关键字分布情况选择排序方法

    • n较小,基本有序:直接插入排序
    • n较小,数据项较多,所占存储空间较大:简单选择排序
http://www.lryc.cn/news/571749.html

相关文章:

  • 【文本大模型】从0开始 - 本地部署一个ChatGLM对话模型(基于WebUI)
  • MySQL 索引和查询优化
  • linux虚拟机yum命令报错解决方案
  • 学习大模型---需要掌握的数学知识
  • 【Python编程】__all__ = [] 的作用
  • PROFIBUS转EtherCAT网关:市政再生水厂的智能连接枢纽
  • 二分查找算法题
  • 鸿蒙Next仓颉语言开发实战教程:懒加载
  • Neo4j常见语句-delete
  • 华为云Flexus+DeepSeek征文|一键部署华为云CCE容器高可用Dify平台的实践经验与思考
  • 部署并了解什么是openstack
  • 结合 STM32CubeMX 使用 FreeRTOS 实时操作系统
  • pyqt 简单条码系统
  • java充电桩源码获取,云快充源码、OCPP、互联互通协议源码实现SpringCloud+vue
  • 对抗性提示:进阶守护大语言模型
  • 使用 Elasticsearch 提升 Copilot 能力
  • Arduino入门教程:10、屏幕显示
  • aws各类服务器编号
  • 阿里云主机自动 HTTPS 证书部署踩坑实录
  • Day04_C语言基础数据结构重点复习笔记20250618
  • 28.行为型模式分析对比
  • linux 下 jenkins 构建 uniapp node-sass 报错
  • WPF学习(二)
  • 专题:2025信创产业新发展+AI趋势数字化研究报告|附30+份报告PDF汇总下载
  • 【OpenGL ES】不用GLSurfaceView,如何渲染图像
  • java学习笔记 IDEA的相关配置
  • 基于Android的打印系统的设计与实现
  • 深入解析 Java List 实现类的底层原理
  • 软件技术专业的出路在哪
  • 学习量子网络中的最佳路径