八大排序详解(默认升序)

一、直接插入排序

直接插入排序:直接插入排序就是像打扑克牌一样,每张牌依次与前面的牌比较,遇到比自己大的就将大的牌挪到后面,遇到比自己小的就把自己放在它后面(如果自己最小就放在第一位),所有牌排一遍后就完成了排序。

代码如下: 

// 插入排序
void InsertSort(int* a, int n)
{for (int i = 0; i < n; ++i){int tmp = a[i];int end = i - 1;while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}

总结:直接插入排序的时间复杂度为O(n^2)空间复杂度为O(1)具有稳定性(相对位置不变),在接近升序的情况下效果最好,接近O(n)。

二、希尔排序

希尔排序:直接插入排序在接近有序的情况下很快,所以就想到在直接插入排序之前先预排序,让数据接近有序,再用直接插入排序,效果就会提升,这就是希尔排序。

预排序: 先将数据分成 gap 组,每组里面使用直接插入排序。 

gap为1时,就是直接插入排序。

 代码如下: 

// 希尔排序
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){gap = gap / 3 + 1;//控制gap,最后一次是1for (int i = 0; i < n; i++){int tmp = a[i];int end = i - 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)空间复杂度为O(1),因为预排序,希尔排序不稳定,也因为预排序,希尔排序在接近有序(无论升序或降序)时,速度最快。

三、选择排序 

选择排序:类似打牌,选择排序就是在 所有牌中选出最小的放在最左边,选出最大的放在最右边,然后再从剩余的牌中重复此操作。

需要注意的是当最大的牌在最左边时,若先与最小的牌交换位置,则会造成错误,只要加个判断调整即可。

 代码如下:

// 选择排序
void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end){int min = begin, max = begin;for (int i = begin; i <= end; ++i){if (a[min] > a[i])min = i;if (a[max] < a[i])max = i;}swap(&a[min], &a[begin]);if (a[begin] == a[max])max = min;swap(&a[max], &a[end]);begin++;end--;}
}

总结时间复杂度为O(n^2),空间复杂度为O(n),不具有稳定性(因为与第一张牌交换时可能会改变相对位置)。

四、冒泡排序 

冒泡排序 遇到比自己小的就交换,每趟冒泡都可以把最大的值放到后面,次大的值放到倒数第二位.....然后结束。

代码如下: 

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

 总结时间复杂度为O(n^2),空间复杂度为O(n),具有稳定性。

五、堆排序

请看这里(づ ̄ 3 ̄)づ:堆排序与TopK问题-CSDN博客 

六、快速排序 

 快速排序:选一个key,将小于key的排在左边,大于key的排在右边,排完后key就在有序时的对应位置。

然后递归左区间和右区间,每个数都在对应位置就能使整体有序。

让 key 在有序时的对应位置,有三种方法。(小指的是比key小,大就是比key大)

hoare版本:最老的版本,右边找小,左边找大,找到交换,重复操作到 left >= right 再将 left 与 key 位置的值交换

 挖坑法key看作坑,右边找小,找到交换,右边看作坑,左边找大,找到交换,左边再看作坑,依次循环,最后坑就在相应位置。

双指针法:一个cur指针,一个prev指针,cur遇到小就与++prev位置交换,遇到大就cur++

使prev位置是小的最后一个

代码如下:

// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{int key = left;while (left < right){//必须先右再左,若先左再右则要交换left+1的位置//前面加一个left<right防止越界,后面不加等号可能会导致死循环while (left < right && a[right] >= a[key])right--;while (left < right && a[left] <= a[key])left++;swap(&a[left], &a[right]);}swap(&a[key], &a[left]);return left;
}

/ 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{int hole = left;while (left < right){while (left < right && a[right] >= a[hole])right--;swap(&a[hole], &a[right]);hole = right;while (left < right && a[left] <= a[hole])left++;swap(&a[hole], &a[left]);hole = left;}return hole;
}
// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{int key = left;int prev = left, cur = prev + 1;while (cur <= right){if (a[cur] < a[key] && ++prev != cur){swap(&a[cur], &a[prev]);}cur++;}swap(&a[key], &a[prev]);return prev;
}

排好一个位置,然后递归左右区间排剩余位置,最终使所有数据有序。

void QuickSort(int* a, int left, int right)
{if (left >= right)return;//int key = PartSort1(a, left, right);//int key = PartSort2(a, left, right);int key = PartSort3(a, left, right);QuickSort(a, left, key - 1);QuickSort(a, key + 1, right);
}

 快排在数据接近有序时会出现剩余数据全在右区间的情况,此时最慢,时间复杂度为O(n^2)

所以我们可以加一个优化:三数取中。就可以极大地规避这种可能性。

三数取中:加一个三数取中的函数,在left,right,(left+right)/2 这三个位置中选出中间那个位置做key.

以hoare版本为例

int GetMid(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] > a[right]){if (a[right] > a[mid])return right;else{if (a[left] > a[mid])return mid;elsereturn left;}}else{if (a[left] > a[mid])return left;else{if (a[mid] > a[right])return right;elsereturn mid;}}
}// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{int mid = GetMid(a, left, right);swap(&a[mid], &a[left]);int key = left;while (left < right){//必须先右再左,若先左再右则要交换left+1的位置//前面加一个left<right防止越界,后面不加等号可能会导致死循环while (left < right && a[right] >= a[key])right--;while (left < right && a[left] <= a[key])left++;swap(&a[left], &a[right]);}swap(&a[key], &a[left]);return left;
}

在递归左右区间时,根据我们学过的满二叉树知识,最后一层的结点几乎占全部的 50% ,倒数第二层大概占 25% 依次类推,而且最后的小区间是接近有序的,所以我们可以判断区间里的数据个数,若小于10,则用直接插入排序直接排完,可以减少80%多栈的调用。 

void QuickSort(int* a, int left, int right)
{if (left >= right)return;if (right - left + 1 <= 10){InsertSort(a + left, right - left + 1);return;}//int key = PartSort1(a, left, right);//int key = PartSort2(a, left, right);int key = PartSort3(a, left, right);QuickSort(a, left, key - 1);QuickSort(a, key + 1, right);
}

非递归版 :用栈模拟函数栈帧的调用过程

// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right)
{stack<int> st;st.push(right);st.push(left);while (!st.empty()){int begin = st.top();st.pop();int end = st.top();st.pop();int key = PartSort3(a, begin, end);if (key - 1 < begin)//区间有效入栈{st.push(key - 1);st.push(begin);}if (key + 1 < end)//区间有效入栈{st.push(end);st.push(key + 1);}}
}

 总结时间复杂度O(nlogn),空间复杂度O(logn),不稳定(找到key合适的位置后要交换)

七、归并排序

 归并排序:大家都做过两个有序数组归并到一起然后整体有序的题目吧,这就是归并排序的核心思路,它先将整个数组递归成一个一个的,然后再不断归并,最后整体有序。就相当于后序遍历的思路,左边有序,右边有序,归并自己,然后自己就有序

 

void _MergeSort(int* a, int* tmp, int left, int right)
{//只有一个数就返回if (left >= right)return;int mid = (left + right) / 2;_MergeSort(a, tmp, left, mid);_MergeSort(a, tmp, mid+1, right);//两个数组有序,归并到整体有序int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1];begin1++;}else{tmp[j++] = a[begin2];begin2++;}}while (begin1 <= end1){tmp[j++] = a[begin1];begin1++;}while (begin2 <= end2){tmp[j++] = a[begin2];begin2++;}//拷贝回对应位置memcpy(a+left, tmp+left, sizeof(int) * (right - left + 1));
}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);_MergeSort(a, tmp, 0, n - 1);
}

非递归版本: 这个思路就是两两归并,四四归并,八八归并....最后整体有序,所以就可以设置一个gap从1到n,让数据模拟上述过程,循环归并。注意的是要考虑数据不足越界情况

void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(n * sizeof(int));int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2*gap){//利用gap设置区间int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;//考虑越界情况if (begin2 >= n)//第二组越界就没必要归并了break;if (end2 >= n)//begin2没越,end2越界,修正end2end2 = n - 1;//归并int j = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[j++] = a[begin1];begin1++;}else{tmp[j++] = a[begin2];begin2++;}}while (begin1 <= end1){tmp[j++] = a[begin1];begin1++;}while (begin2 <= end2){tmp[j++] = a[begin2];begin2++;}memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;}
}

总结 时间复杂度O(nlogn),空间复杂度O(n),稳定。缺点是空间复杂度过高,优势是可以在磁盘中排序(外排序)

 八、计数排序

计数排序:计数排序是类似哈希表先开一个数组,让每个值都有一个对应位置,遍历数据,遇到就在对应位置++,最后遍历数组输出

 代码如下:

void CountSort(int* a, int n)
{//assert(n>0);//找到最大值与最小值,为映射做准备int max = a[0], min = a[0];for (int i = 1; i < n; ++i){if (a[i] < min)min = a[i];if (a[i] > max)max = a[i];}int range = max - min + 1;int* Count = (int*)calloc(range, sizeof(int));if (Count == NULL){perror("calloc fail");exit(-1);}//对应位置++for (int i = 0; i < n; ++i){Count[a[i] - min]++;}int j = 0;for (int i = 0; i < range; ++i){//把对应位置写回原数组while (Count[i]--){a[j++] = i + min;}}
}

总结 :计数排序的时间复杂度为O(max(range, n)),空间复杂度为O(range),稳定适用于数据集中在某一范围的时候

感谢大家观看,欢迎指出错误(๑•̀ㅂ•́)و✧ 

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

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

相关文章

GitHub工具之云资产管理

文章目录 0x01 介绍0x02 软件架构0x03 下载地址0x04 更新记录0x05 实现功能0x06 使用截图1、云存储工具-资产列表2、云存储工具-阿里云3、云存储工具-七牛云4、云存储工具-腾讯云5、云存储工具-亚马逊6、云存储工具-京东云7、云存储工具-金山云8、云存储工具-其他9、云存储工具…

【Kotlin精简】第1章 基础类型

1 Kotlin基础类型 Kotlin中&#xff0c;我们可以调用任何变量的成员函数和属性&#xff0c;从这个角度来说&#xff0c;一切皆对象。某些类型可以有特殊的内部表现。例如&#xff1a;数字、字符和布尔型在运行时可以表现为基础类型&#xff08;primitivetypes&#xff09;。 …

Stable diffusion的架构解读(本博客还是以unet架构为主)

博客只是简单的记录一下自己学的&#xff0c;基于自己的一些情况&#xff0c;所以简单了一些只是将来忘记&#xff0c;用来回顾用。 论文的大体框架 unet结构位于 unet会接受prompt特征、latent特征、和t时间步特征&#xff0c;最后生成新一轮的特征 可以参考知乎大佬htt…

Android之App跳转其他软件

文章目录 前言一、效果图二、实现步骤1.弹框xml(自己替换图标)2.弹框utils3.两个弹框动画4.封装方便调用5.调用6.长按事件方法7.跳转步骤8.复制utils 总结 前言 最近遇到一个需求&#xff0c;就是App内大面积需要长按复制并跳转指定App&#xff0c;没办法&#xff0c;只能埋头…

大语言模型之十六-基于LongLoRA的长文本上下文微调Llama-2

增加LLM上下文长度可以提升大语言模型在一些任务上的表现&#xff0c;这包括多轮长对话、长文本摘要、视觉-语言Transformer模型的高分辨4k模型的理解力以及代码生成、图像以及音频生成等。 对长上下文场景&#xff0c;在解码阶段&#xff0c;缓存先前token的Key和Value&#…

SpringCloud Alibaba - Sentinel 高级玩法,修改 Sentinel-dashboard 源码,实现 push 模式

目录 一、规则持久化 1.1、什么是规则持久化 1.1.1、使用背景 1.1.2、规则管理的三种模式 a&#xff09;原始模式 b&#xff09;pull 模式 c&#xff09;push 模式 1.2、实现 push 模式 1.2.1、修改 order-service 服务&#xff0c;使其监听 Nacos 配置中心 1.2.2、修…

RocketMQ 5.0 新版版本新特性总结

1 架构变化 RocketMQ 5.0 架构上的变化主要是为了更好的走向云原生 RocketMQ 4.x 架构如下&#xff1a; Broker 向 Name Server 注册 Topic 路由信息&#xff0c;Producer 和 Consumer 则从 Name Server 获取路由信息&#xff0c;然后 Producer 根据路由信息向 Broker 发送消…

2023年中国石化行业节能减排发展措施分析:用精细化生产提高生产效率,降低能耗[图]

2022年&#xff0c;我国石油和化工行业克服诸多挑战取得了极其不易的经营业绩&#xff0c;行业生产基本稳定&#xff0c;营业收入和进出口总额增长较快&#xff0c;效益比上年略有下降但总额仍处高位。2022年&#xff0c;我国石油化工行业市场规模为191761.2亿元&#xff0c;同…

macbook电脑磁盘满了怎么删东西?

macbook是苹果公司的一款高性能笔记本电脑&#xff0c;受到很多用户的喜爱。但是&#xff0c;如果macbook的磁盘空间不足&#xff0c;可能会导致一些问题&#xff0c;比如无法开机、运行缓慢、应用崩溃等。那么&#xff0c;macbook磁盘满了无法开机怎么办&#xff0c;macbook磁…

Qt实现 图片处理器PictureEdit

目录 图片处理器PictureEdit1 创建工具栏2 打开图片3 显示图片4 灰度处理5 颜色反转6 马赛克 图片处理器PictureEdit 创建工程&#xff0c;添加资源文件 1 创建工具栏 widget.h中 #include <QWidget> #include<QPixmap> #include<QFileDialog> #include&l…

【赠书活动】如何让AI在企业多快好省的落地

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【LeetCode: 2034. 股票价格波动 | 有序表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

[极客大挑战 2019]BabySQL 1

#做题方法# 进去之后做了简单的注入发现有错误回显&#xff0c;就进行注入发现过滤了sql语 后面进行了双写and payload&#xff1a; ?usernameadmin%27%20aandnd%20updatexml(1,concat(0x7e,dAtabase(),0x7e,version()),1)%20--&passwordadmi 接下来又 ?usernameadm…

yolov5及yolov7实战之剪枝

之前有讲过一次yolov5的剪枝&#xff1a;yolov5实战之模型剪枝_yolov5模型剪枝-CSDN博客 当时基于的是比较老的yolov5版本&#xff0c;剪枝对整个训练代码的改动也比较多。最近发现一个比较好用的剪枝库&#xff0c;可以在不怎么改动原有训练代码的情况下&#xff0c;实现剪枝的…

李沐深度学习记录5:13.Dropout

Dropout从零开始实现 import torch from torch import nn from d2l import torch as d2l# 定义Dropout函数 def dropout_layer(X, dropout):assert 0 < dropout < 1# 在本情况中&#xff0c;所有元素都被丢弃if dropout 1:return torch.zeros_like(X)# 在本情况中&…

超自动化加速落地,助力运营效率和用户体验显著提升|爱分析报告

RPA、iPaaS、AI、低代码、BPM、流程挖掘等在帮助企业实现自动化的同时&#xff0c;也在构建一座座“自动化烟囱”。自动化工具尚未融为一体&#xff0c;协同价值没有得到释放。Gartner于2019年提出超自动化&#xff08;Hyperautomation&#xff09;概念&#xff0c;主要从技术组…

【动手学深度学习】课程笔记 04 数据操作和数据预处理

目录 数据操作 N维数组样例 访问元素 数据操作实现 入门 运算符 广播机制 节省内存 转换为其他Python对象 数据预处理实现 数据操作 N维数组是机器学习和神经网路的主要数据结构。 N维数组样例 访问元素 数据操作实现 下面介绍一下本课程中需要用到的PyTorch相关操…

BJT晶体管

BJT晶体管也叫双极结型三极管&#xff0c;主要有PNP、NPN型两种&#xff0c;符号如下&#xff1a; 中间的是基极&#xff08;最薄&#xff0c;用于控制&#xff09;&#xff0c;带箭头的是发射极&#xff08;自由电子浓度高&#xff09;&#xff0c;剩下的就是集电极&#xff0…

no Go files in ...问题

golang项目&#xff0c;当我们微服务分模块开发时&#xff0c;习惯把main.go放在cmd目录下分模块放置&#xff0c;此时&#xff0c;我们在项目根目录下执行go test . 或go build . 时会报错“no Go files in ...”, 这是因为在.目录下找不到go程序&#xff0c;或者找不到程序入…

springboot整合pi支付开发

pi支付流程图&#xff1a; 使用Pi SDK功能发起支付由 Pi SDK 自动调用的回调函数&#xff08;让您的应用服务器知道它需要发出批准 API 请求&#xff09;从您的应用程序服务器到 Pi 服务器的 API 请求以批准付款&#xff08;让 Pi 服务器知道您知道此付款&#xff09;Pi浏览器向…