内排序算法
排序算法是面试中常见的问题,不同算法的时间复杂度、稳定性和适用场景各不相同。按照数据量和存储方式可以将排序算法分为 内排序(Internal Sorting)和 外排序(External Sorting)。
内排序是指对所有待排序的数据都可以一次性加载到内存中进行排序。这意味着所有的数据都可以直接在计算机的内存中操作,无需借助外部存储设备(如硬盘)。内排序算法的设计可以更加灵活,因为内存操作速度远远高于磁盘操作速度。常见的内排序算法包括快速排序、归并排序、插入排序、冒泡排序等。
外排序是指对大量数据进行排序时,由于数据无法一次性加载到内存中,需要借助外部存储设备进行排序。通常在外排序中,数据会被分成若干个小块,每次只处理其中一部分数据,然后将部分排序好的数据存储回外部存储设备,再进行合并等操作。外排序算法需要考虑数据的分块、读写外部存储的效率,以及合并有序数据等问题。常见的外排序算法有多路归并排序、置换选择排序等。
本文介绍的都是常见的内排序算法:插入排序、冒泡排序、选择排序、希尔排序、归并排序、快速排序、堆排序、桶排序。
目录
- 1. 插入排序
- 2. 冒泡排序
- 3. 选择排序
- 4. 希尔排序
- 5. 归并排序
- 6. 快速排序
- 7. 堆排序
- 8. 桶排序
1. 插入排序
插入排序的核心是将待排序的元素逐个插入到已经排好序的序列中,以构建有序的输出序列:
void inssort(int A[],int n){for(int i=1;i<n;i++){for(int j=i;j>0;j--){if(A[j]<A[j-1]){ //A[j]<A[j-1](此处小于即排在前面)swap(A,j,j-1);}else{break; //A[j]已达到正确位置(时间优化)}}}
}
插入排序的平均时间复杂度为 O(n2),最好情况为 O(n),最坏情况为 O(n2),适用于小规模的数据集或者已经基本有序的数据。插入排序的空间复杂度为 O(1),是一种稳定排序算法。
2. 冒泡排序
冒泡排序的核心思想是通过比较相邻的元素并交换位置,逐渐将最小的元素 “冒泡” 到正确的位置。冒泡排序的过程类似于冒泡泡沫在水中升起的过程,较大的元素会逐渐向序列的开头 “冒泡”:
void bubsort(int A[],int n){for(int i=0;i<n-1;i++){for(int j=n-1;j>i;j--){if(A[j]<A[j-1]){swap(A,j,j-1);}}}
}
冒泡排序的平均时间复杂度为 O(n2),最好情况和最坏情况都为 O(n2)。冒泡排序的空间复杂度为 O(1),是一种稳定排序算法。
3. 选择排序
选择排序的核心思想是在未排序序列中选择最小的元素,然后将它与未排序序列的起始位置交换,使得已排序序列逐步增长:
void selsort(int A[],int n){for(int i=0;i<n-1;i++){int index=i;for(int j=n-1;j>i;j--){if(A[j]<A[index]){index=j;}}swap(A,i,index);}
}
选择排序的平均时间复杂度为 O(n2),最好情况和最坏情况都为 O(n2)。选择排序的空间复杂度为 O(1),是一种稳定排序算法。
4. 希尔排序
希尔排序利用了插入排序最佳时间代价特性,在不相邻的记录之间进行交换和比较。核心思想是将整个序列分成多个较小的子序列来逐步减少序列的无序度,从而在最后一步进行一次插入排序,达到提高排序速度的效果。过程如图:

void inssort2(int A[],int n,int incr){ //n为传入数组长度,incr为数组内元素间隔for(int i=incr;i<n;i+=incr){ //incr相当于第1位元素,+incr相当于间隔for(int j=i;(j>=incr)&&(A[j]<A[j-incr]);j-=incr){int tmp=A[j];A[j]=A[j-incr];A[j-incr]=tmp;cnt1++;}}
}
void shellsort(int A[],int n){for(int i=n/2;i>2;i/=2){ //i表示组数或者间隔for(int j=0;j<i;j++){ //j表示有i组数据时从0到i遍历inssort2(&A[j],n-j,i); //对第j组数据进行插入排序(起始位置,长度,间隔)//第j组数据在A中下标:j,j+i,j+2i...用inssort2中传入参数表示为:&A[j],&A[j]+i,&A[j]+2i...}}inssort2(A,n,1); //对整个数组插入排序
}
希尔排序在现实中没有对应的直观解释,也无法证明其时间复杂度,一般认为平均时间复杂度为 O(n1.5),最好情况为 O(nlogn),最坏情况为 O(n2)。希尔排序的空间复杂度为 O(1),是一种不稳定排序算法。
5. 归并排序
归并排序的核心思想是 “二分+合并”,即将一个未排序的数组分割成两个子数组,递归地对这两个子数组排好序后再合并为一整个有序的数组:
void mergesort(int A[],int tmp[],int left,int right){ //left表示A[0]下标,right表示A[n-1]下标if(left==right) return;int mid=(left+right)/2;mergesort(A,tmp,left,mid); //向下递归,二分集合并排序mergesort(A,tmp,mid+1,right);for(int i=left;i<=right;i++){tmp[i]=A[i];}int i1=left,i2=mid+1; //i1,i2表示两个待合并数组(各自都已排序)首个未排序元素下标for(int curr=left;curr<=right;curr++){if(curr==mid+1){ //left数组已完成合并A[curr]=tmp[i2++];}else if(i2>right){ //right数组已完成合并A[curr]=tmp[i1++];}else if(tmp[i1]<tmp[i2]){A[curr]=tmp[i1++];}else{A[curr]=tmp[i2++];}}
}
归并排序的平均时间复杂度为 O(nlogn),最好情况和最坏情况也都为 O(nlogn)。归并排序的空间复杂度为 O(n),是一种稳定排序算法。
6. 快速排序
快速排序的核心思想是选择一个轴值 pivot,将数组分为小于轴值的部分和大于轴值的部分,然后递归地对这两部分进行排序。轴值的选择会影响算法的效率,一般选取数组的中间点作为轴值。为了设计方便,会将轴值置于数组末端,待排好序后再移动到合适位置:
//partition函数将数组的l+1~r-1划分为两个以轴值为分界的部分,并返回轴值的下标(实际上该位元素与最后一位交换后才是真正的轴值下标)
int partition(int A[],int l,int r,int& pivot){ //l,r为A[]的左右范围,pivot为轴值do{while(A[++l]<pivot); //跳过满足小于轴值的元素,终止在大于轴值的地方while((l<r)&&(A[--r]>pivot));int tmp=A[r];A[r]=A[l];A[l]=tmp;}while(l<r);return l;
}void qsort(int A[],int i,int j){if(i>=j) return;int pivotindex=(i+j)/2; //选取轴值int pivot=A[pivotindex];A[pivotindex]=A[j]; //将轴值与末尾元素互换A[j]=pivot;pivotindex=partition(A,i-1,j,A[j]); //将l+1~r-1按轴值分部,并返回待与轴值交换元素下标pivot=A[j]; //轴值还在末尾等待交换A[j]=A[pivotindex];A[pivotindex]=pivot;qsort(A,i,pivotindex-1);qsort(A,pivotindex+1,j);
}
快速排序是迄今为止所有内排序算法中在平均情况下最快的一种,当每个轴值都把数组分成相等的两个部分时,整个算法的时间代价是 Θ \Theta Θ(nlogn)。快速排序的平均时间复杂度为 O(nlogn),最好情况为 O(nlogn),最坏情况为 O(n2)。快速排序的空间复杂度为 O(logn),是一种不稳定排序算法。
7. 堆排序
堆排序是基于堆数据结构的排序算法,它利用最小堆的性质来实现对数组的排序:
class heap{
private:int* Heap;int maxsize;int n;void siftdown(int pos){while(!isLeaf(pos)){int j=leftchild(pos); //下面j表示的是lc和rc中较小值的下标int rc=rightchild(pos);if((rc<n)&&(Heap[rc]<Heap[j])){j=rc;}if(Heap[pos]<Heap[j]){return;}int tmp=Heap[pos];Heap[pos]=Heap[j];Heap[j]=tmp;pos=j;}}
public:heap(int* h,int num,int max){Heap=h;n=num;maxsize=max;buildHeap();}int size(){return n;}bool isLeaf(int pos){return (pos>=n/2)&&(pos<n);}int leftchild(int pos){return 2*pos+1;}int rightchild(int pos){return 2*pos+2;}int parent(int pos){return (pos-1)/2;}void buildHeap(){for(int i=n/2-1;i>=0;i--){siftdown(i);}}void insert(const int& it){int curr=n++;Heap[curr]=it;while((curr!=0)&&(Heap[curr]<Heap[parent(curr)])){int tmp=Heap[curr];Heap[curr]=Heap[parent(curr)];Heap[parent(curr)]=tmp;curr=parent(curr);}}int removefirst(){n--;int tmp=Heap[0];Heap[0]=Heap[n];Heap[n]=tmp;if(n!=0){siftdown(0);}return Heap[n];}int remove(int pos){if(pos==(n-1)){n--;}else{n--;int tmp=Heap[pos];Heap[pos]=Heap[n];Heap[n]=tmp;while((pos!=0)&&(Heap[pos]<Heap[parent(pos)])){int tmp=Heap[pos];Heap[pos]=Heap[parent(pos)];Heap[parent(pos)]=tmp;pos=parent(pos);}if(n!=0){siftdown(pos);}}return Heap[n];}
};
void heapsort(int A[],int n){int minval;heap H(A,n,n);for(int i=0;i<n;i++){minval=H.removefirst();}
}
堆排序的平均时间复杂度为 O(nlogn),最好情况和最坏情况也都为 O(nlogn),但比快速排序要慢一个常数因子。堆排序在实际使用时适用于查找第 k 大小的元素,或者用于外排序。堆排序的空间复杂度为 O(1),是一种不稳定排序算法。
8. 桶排序
桶排序的核心思想是将数组按数值范围放入若干个桶,每个桶对应一定范围的数据,然后对每个桶中的数据使用其他排序算法或递归方式进行排序,最后将所有桶中的数据按顺序合并得到排序结果:
void inssort(int A[],int n){for(int i=1;i<n;i++){for(int j=i;j>0;j--){if(A[j]<A[j-1]){int tmp=A[j];A[j]=A[j-1];A[j-1]=tmp;}else{break;}}}
}void bucketSort(int A[],int n,int numBuckets){//创建桶int buckets[numBuckets][n]; //二维数组,每行表示一个桶int bucketSizes[numBuckets]; //每个桶中元素的数量//初始化桶的大小for(int i=0;i<numBuckets;i++){bucketSizes[i]=0;}//确定数组元素范围int maxA=-999,minA=999;for(int i=0;i<n;i++){if(A[i]>maxA) maxA=A[i];if(A[i]<minA) minA=A[i];} //将元素放入桶中for(int i=0;i<n;i++){int bucketGap=ceil((maxA-minA)/(float)numBuckets);int bucketIndex=(A[i]-minA)/bucketGap;buckets[bucketIndex][bucketSizes[bucketIndex]++]=A[i];}//对每个桶中的元素进行插入排序for(int i=0;i<numBuckets;i++){inssort(buckets[i],bucketSizes[i]);}// 合并桶中的元素int index=0;for(int i=0;i<numBuckets;i++){for(int j=0;j<bucketSizes[i];j++){A[index++]=buckets[i][j];}}
}
桶排序的平均时间复杂度为 O(n2),最好情况和最坏情况也都为 O(n2),但比插入排序要快一个常数因子。桶排序适用于在已知输入数据的范围内进行排序,若数据分布不均匀也会影响效率。桶排序的空间复杂度为 O(n2),是一种稳定排序算法。
相关文章:
内排序算法
排序算法是面试中常见的问题,不同算法的时间复杂度、稳定性和适用场景各不相同。按照数据量和存储方式可以将排序算法分为 内排序(Internal Sorting)和 外排序(External Sorting)。 内排序是指对所有待排序的数据都可…...
options.html 页面设计成聊天框,左侧是功能列表,右侧是根据左侧的功能切换成不同的内容。--chatGpt
gpt: 要将 options.html 页面设计成一个聊天框式的界面,其中左侧是功能列表,右侧根据左侧的功能切换成不同的内容,你可以按照以下步骤进行设计和实现: 1. 首先,创建 options.html 文件,并在其中定义基本的…...
排序算法-选择排序法(SelectionSort)
排序算法-选择排序法(SelectionSort) 1、说明 选择排序法也是枚举法的应用,就是反复从未排序的数列中取出最小的元素,加入另一个数列中,最后的结果即为已排序的数列。选择排序法可使用两种方式排序,即在所…...
Java-集合框架
文章目录 摘要CollectionCollection集合遍历Iterator迭代器增强for循环 排序 ListArrayListLinkedListVector SetHashSet Map小结 摘要 Java的集合框架提供了一组用于存储、管理和操作数据的类和接口。这个框架提供了各种数据结构,如列表、集合、队列和映射&#x…...
联想携中国移动打造车路协同方案 助力重庆实现32类车联网场景
10月11日,联想集团在中国移动全球合作伙伴大会上首次分享了与中国移动等合作伙伴共同打造的5G车路协同案例——重庆两江协同创新区车路协同应用。联想利用基于5G智能算力技术,在总里程55公里路段实现了32类车联网场景。 据了解,重庆两江协同创…...
Rust入门基础
文章目录 Rust相关介绍为什么要用Rust?Rust的用户和案例 开发环境准备安装Rust更新与卸载Rust开发工具 Hello World程序编写Rust程序编译与运行Rust程序 Cargo工具Cargo创建项目Cargo构建项目Cargo构建并运行项目Cargo检查项目Cargo为发布构建项目 Rust相关介绍 为…...
民族民俗景区3d智慧旅游系统提升游客旅游体验和质量
随着科技的不断发展,传统的旅游方式正在逐渐被新的技术和系统所取代。网上3D沉浸式旅游体验凭借其身临其境的沉浸式体验优势,正成为旅游业的新宠。 网上3D沉浸式旅游体验是将旅游景区、度假区、休闲街区、科博馆等场所空间,利用VR全景制作、w…...
Webpack 解决:Error: error:0308010C:digital envelope routines::unsupported 的问题
1、问题描述: 其一、报错为: Error: error:0308010C:digital envelope routines::unsupported 中文为: 错误:错误:0308010C:数字信封例程::不支持 其二、问题描述为: 在项目打包的时候 np…...
JAVA操作Json的ObjectMapper类
JAVA操作Json的ObjectMapper类 市面上用于在 Java 中解析 Json 的第三方库,随便一搜不下几十种,其中的佼佼者有 Google 的 Gson以及本文的 jackson。 三者不相伯仲,随着掌握一个都能满足项目中的 json 解析操作,因为 Spring Boot…...
Docker--harbor
一,registry registry是私有仓库的核心,只有字符终端。 二,registry部署 #首先下载 registry 镜像 docker pull registry#在 daemon.json 文件中添加私有镜像仓库地址 vim /etc/docker/daemon.json {"insecure-registries": [&q…...
Flink中的时间和窗口
1.Flink的时间和窗口 在传统的批处理系统中,我们可以等到一批数据全部都到齐了之后,对其做相关的计算;但是在实时处理系统中,数据是源源不断的,正常情况下,我们就得来一条处理一条。那么,我们应…...
Ultra-Fast-Lane-Detection 车道线学习资料整理
目录 官方版本 两个优化 数据标注,降低参数量 1 数据标注 2降低参数量...
【Ubuntu】Ubuntu18.04终端卡顿问题
博主您好,我也遇到了类似的问题,但我找到了问题的原因: 在gnome-terminal中,按tab补全是默认开启了“咚咚咚”音效的,在gnome-terminal里把音效关掉就好了,主要是因为按tab时,NVIDIA的视频信号和…...
k8s强制删除pod、svc、namespace(Terminating)
如果名称空间、pod、pv、pvc全部处于“Terminating”状态时,此时的该名称空间下的所有控制器都已经被删除了,之所以出现pod、pvc、pv、ns无法删除,那是因为kubelet 阻塞,有其他的资源在使用该namespace,比如CRD等&…...
froeach迭代删除和List迭代删除问题
场景:我有一个 List<ISSLogMessage> records 数据,需要从里面删除指定内容数据 第一次写成 foreach(var item in records) {if (item.logMessage.Contains("上传通行记录"))records.Remove(item); } 直接报错,因为foreach 是个迭代器 直接移除它的对象会报…...
chromedriver下载地址
ChromeDriver下载地址: 淘宝镜像:https://registry.npmmirror.com/binary.html?pathchromedriver/ 官方镜像:https://sites.google.com/a/chromium.org/chromedriver/downloads在下载页面上,将看到一列Chrome浏览器的版本号和相…...
2ED2410-EM:12v / 24v智能模拟高侧MOSFET栅极驱动器
概述 12v / 24v智能模拟高侧MOSFET栅极驱动器。 特性 PRO-SIL ISO 26262-准备根据ISO 26262:2018条款8-13支持硬件元件评估的集成商。一个通道器件具有两个高侧栅极驱动器输出。3 Ω下拉,50 Ω上拉,用于快速开关开/关。支持背靠背MOSFET拓扑(共漏极和共源)。两个双向高侧模拟…...
什么是Fetch API?与传统的AJAX相比,有什么优势?
聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...
43.241.18.123哪些问题会导致服务器里面时间错误
我们在使用服务器的过程中,有时候可能会发现,服务器里面时间跟标准的时间对不上,那服务器里面时间错误可能由哪些问题引起: 硬件问题:服务器硬件中的时钟或电池可能损坏或失效,导致时间不准确或重置为默认…...
【ElasticSearch】更新es索引生命周期策略,策略何时对索引生效
大家好,我是好学的小师弟,今天和大家讨论下更新es索引生命周期策略后,策略何时对索引生效 结论: 若当前索引已应用策略A(旧),更新完策略A后,新的策略A会立即对原来的已经应用该策略的索引生效;若当前索引…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
【JavaSE】多线程基础学习笔记
多线程基础 -线程相关概念 程序(Program) 是为完成特定任务、用某种语言编写的一组指令的集合简单的说:就是我们写的代码 进程 进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
