暴力数据结构之排序大杂烩

1. 冒泡排序:O(N^2)

逻辑解析: 冒泡排序并没有什么实际意义,但是有教学意义,相信大部分小白在学习的初期第一个接触的排序就是冒泡排序。那么接下来我们了解一下他的底层逻辑:

冒泡排序顾名思义就是将最大(小)的值像一个一个小气泡一样逐渐运动到最上层,也就是将一串数字从头开始两两比较,逐步将最值移动到最尾端,最终实现排序。其时间复杂度为O(N^2)。

 

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}//冒泡排序O(N^2)
void BubbleSort(int* a, int n)
{for (int i = 0; i < n; i++){int flag = 0;for (int j = 1; j < n - i; j++){if (a[i] > a[j]){Swap(&a[i], &a[j]);flag = 1;}}//已经排好序,所以第一次遍历没有调整,直接退出if (flag == 0){break;}}
}

2. 插入排序:O(N^2)

逻辑解析: 插入排序就是设置一个end,假设区间[0,end]都是有序的,此时取出第end+1个数字插入到前面的有序数列,以此类推,最值end到尾端,此时整个数列就是一个有序数列。时间复杂度为:O(N^2)

void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}//插入排序O(N^2)
void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){//默认[0,end]的区间是有序的,取a[end+1]插入前面的区间int end = i;int tmp = a[end + 1];while (end >= 0){if (a[end] > tmp){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}

3. 希尔排序:O(N^1.3)

逻辑解析:希尔排序与插入排序的共同点是,希尔排序共进行多次,最后一次时的gap=1,也就是插入排序,在前面的时候先将gap置为n,也就是数组元素个数,这时每次对gap/3后+1,使gap/3递减的同时始终满足gap最小值为1,即保证最后一趟一定是插入排序。在插入排序之前要保证数组分为不相邻的gap组,保证每个组都是有序的,然后在最后一次插入排序使整个数组有序即可。时间复杂度为:O(N^1.3)

//希尔排序O(N^1.3)
void ShellSort(int* a, int n)
{int gap = n;while (gap > 1){// +1保证最后一个gap一定是1// gap > 1时是预排序// gap == 1时是插入排序gap = gap / 3 + 1;for (int i = 0; i < n - gap; i++){int end = i;int tmp = a[end + gap];while (end >= 0){if (a[end] > tmp){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}

4. 堆排序:O(N*logN)

逻辑解析:堆排序是基于堆的逻辑结构设计的,所以了解堆排序首先要知道堆的相关知识,详情移步堆的相关知识 ,关于堆排序实质就是对数组的一些操作,即首先根据升(降)序来决定创建大(小)堆,然后生成一个堆。这时就直接将根节点与最后的叶子结点交换,再向下调整即可。即将数组的首尾交换后进行操作。时间复杂度为:O(N*logN)

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)  // child >= n说明孩子不存在,调整到叶子了{// 找出小的那个孩子if (child + 1 < n && a[child + 1] < a[child]){++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}//堆排序O(N*logN)
void HeapSort(int* a, int n)
{// 降序,建小堆// 升序,建大堆// 向上调整建堆 O(N*logN)// 向下调整建堆 O(N)//从离叶子结点最近的上一层根节点开始//n-1代表最末尾的叶子结点,那么((n - 1) - 1) / 2就是该叶子结点的根结点for (int i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);}// O(N*logN)int end = n - 1;while (end > 0){//交换根叶结点后向下调整Swap(&a[0], &a[end]);AdjustDown(a, end, 0);--end;}
}

5. 选择排序: O(N^2)

逻辑解析: 选择排序和冒泡排序一样都没有什么实践意义,同样也没有什么教学意义,所以一般只做了解即可。关于选择排序,大体思路十分简单,就是将最大值与最小值下标都初始置为第一个元素的下标,然后从数组首尾分别向中间遍历,将最大值放在末尾,最小值放在首位,然后缩小遍历区间,再寻找该区间的最大值与最小值,放在两端,以此类推,直到遍历完整个数组即可。

//选择排序
//在[begin,end]范围内选择最值放到两边
void SelectSort(int* a, int n)
{int begin = 0;int end = n - 1;while (begin < end){int min = begin;int max = begin;for (int i = begin + 1; i <= end; i++){if (a[i] > a[max]){max = i;}if (a[i] < a[min]){min = i;}}if (begin == max){max = min;}Swap(&a[begin], &a[min]);Swap(&a[end], &a[max]);begin++;end--;}
}

6. 快速排序(递归实现):O(N*logN)

  1.霍尔方法实现

逻辑解析: 首先讲解一下霍尔发明的排序方法,就是初始时刻将数组最左边的元素置为 key 元素,然后设置两个标识 begin 与 end ,起初 begin 在最左边,end 在最右边,然后向中间遍历,如果 a[begin] 遇到比 a[key] 小的值就向后遍历,同理 a[end] 遇到比 a[key] 大的值就向前遍历,直到遇见不符合的情况,然后将 a[begin] 与 a[end] 交换,直到 begin == end ,这时将 a[key] 与 a[begin] 交换(与 a[end] 也可以) ,目的就是保证 key 元素的左边元素均小于它本身,右边元素都大于它本身,然后递归实现第一次排序后 key 元素的左右部分即可。

//快排的注意一点就是以一个端点为 key 开始端,那么另一个端点就先开始动
//即上述以左边为 key ,就是右边先开始动,直到 begin 和 end 遇见,此时的
//相遇点一定要小于 a[key] 的值//快速排序优化
void QuickSort(int* a, int left, int  right)
{//当只有一个数字就返回if (left >= right){return;}// 小区间优化,不再递归分割排序,减少递归的次数if ((right - left + 1) < 10){//这里 a+left = a[left],即从a[left]开始插入排序InsertSort(a + left, right - left + 1);}else{// 三数取中int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int begin = left;int end = right;int key = left;while (begin < end){//右找小//从最右边开始找出比a[key]小的值while (begin < end && a[key] <= a[end]){end--;}//左找大//从最左边开始找出比a[key]大的值while (begin < end && a[key] >= a[begin]){begin++;}//将左边比a[key]大的与右边比a[key]小的交换//保证begin == end 的位置左边的数字全部比a[key]小//同理,右边的数字全部比a[key]大Swap(&a[begin], &a[end]);}//当begin == end ,即两个目标相遇时就交换相遇的位置与a[key]的位置//然后以新的 key 为割点分为左部分与右部分,再对两部分依次递归Swap(&a[begin], &a[key]);key = begin;//递归实现,使第一次遍历后 key 位置的左右部分有序即可QuickSort(a, left, key - 1);QuickSort(a, key + 1, right);}
}

 2. 双指针方法实现 

逻辑解析:双指针就是初始化两个指针 cur  与 prev ,初始时刻 prev 指向开头,cur 指向 prev 的下一个位置,设置 key 元素为开头元素。然后 cur 向后遍历,遇到比 key 小的就让 prev++,然后交换 cur 与 prev 元素,遍历到 cur 指向元素末端之后结束,然后交换 prev 与 key 元素,然后递归对 key 元素的左右部分进行排序即可。

//快速排序优化
void QuickSort(int* a, int left, int  right)
{//当只有一个数字就返回if (left >= right){return;}// 小区间优化,不再递归分割排序,减少递归的次数if ((right - left + 1) < 10){//这里 a+left = a[left],即从a[left]开始插入排序InsertSort(a + left, right - left + 1);}else{// 三数取中int midi = GetMidi(a, left, right);Swap(&a[left], &a[midi]);int begin = left;int end = right;int key = left;while (begin < end){//右找小//从最右边开始找出比a[key]小的值while (begin < end && a[key] <= a[end]){end--;}//左找大//从最左边开始找出比a[key]大的值while (begin < end && a[key] >= a[begin]){begin++;}//将左边比a[key]大的与右边比a[key]小的交换//保证begin == end 的位置左边的数字全部比a[key]小//同理,右边的数字全部比a[key]大Swap(&a[begin], &a[end]);}//当begin == end ,即两个目标相遇时就交换相遇的位置与a[key]的位置//然后以新的 key 为割点分为左部分与右部分,再对两部分依次递归Swap(&a[begin], &a[key]);key = begin;//递归实现,使第一次遍历后 key 位置的左右部分有序即可QuickSort(a, left, key - 1);QuickSort(a, key + 1, right);}
}//单趟排序
int QSort2(int* a, int left, int right)
{int mid = GetMid(a, left, right);Swap(&a[left], &a[mid]);int key = left;int prev = left;int cur = prev + 1;while (cur <= right){if (a[cur] < a[key] && ++prev != cur){Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[prev], &a[key]);return prev;
}//快速排序双指针实现形式
void QuickSortDouble(int* a, int left, int right)
{if (left >= right){return;}int key = QSort2(a, left, right);QuickSort(a, left, key - 1);QuickSort(a, key, right);
}

7. 快速排序(非递归实现):O(N*logN)

逻辑解析: 虽然是非递归实现,但是其思路还是递归的思路,我们在递归版本的代码实质就是在对待排序数组的某些区间进行操作,所以非递归版本就沿用这个思路,依旧是对待排序数组的区间进行一系列操作,这里是通过栈来实现,即在栈中依次存入 begin 与 end 两个值,这两个值就是初始时刻待排序数组的全部区间,然后进行一趟排序,找到 key 元素,此时要排序的区间就分为了左区间 [begin,key - 1] 与右区间 [key + 1,end],这时就依次先对右区间再对左区间进行入栈操作,主要是因为栈遵循先进后出的原则,相关文章:暴力数据结构之栈与队列(栈的相关操作)

然后利用 while 循环实现不断分区间的操作即可,大体思路与递归相似。

//快速排序非递归实现(借助栈实现)
//主要是对区间进行操作,逐步二分,相当于深度优先遍历
void QuickSortNR(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);//取完栈为空,跳出while循环//单次排序//其中包含取中操作int key = QSort2(a, begin, end);if (key + 1 < end){STPush(&st, end);STPush(&st, key + 1);}if (key - 1 > begin){STPush(&st, key - 1);STPush(&st, begin);}}STDestroy(&st);
}

8. 归并排序(递归实现):O(N*logN)

 

逻辑解析:归并排序就是首先两两比较再四四比较以此类推,直到排序完成,代码实现的思路是,创建一个新的数组,然后将原数组分为两个区间,左区间与右区间,然后分别对左右区间不断细分直到左右区间都只有一个数据,这时就对他们比较,将较小的值放在新数组,以此类推。概括起来就是不断二分直到不可再分为止,然后利用新开的数组再进行归并,最终实现有序。递归版本是先实现单趟排序的逻辑,再进行递归实现整个数组排序的实现。

void _MergeSort(int* a, int* tmp, int begin, int end)
{if (begin == end){return;}//如果[begin,mid][mid+1,end]有序就直接合并即可int mid = (begin + end) / 2;_MergeSort(a, tmp, begin, mid);_MergeSort(a, tmp, mid + 1, end);//归并int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}//归并排序(递归实现)时间复杂度:O(N*logN)
//分为左右区间,分别排序再合并到新数组即可
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc error");return;}_MergeSort(a, tmp, 0, n - 1);free(tmp);tmp = NULL;
}

9. 归并排序(非递归实现): O(N*logN)

逻辑解析: 非递归版本就是首先进行一一比较,即第一个与第二个比较,第二个与第三个比较,以此类推,直到遇见末尾,然后再两两比较,即第一与第二个为一组,第二与第三个为一组,两组之间比较,然后存入新数组中,以此类推直到原数组分组到不可再分,代码实现就是使用 gap 进行分组,然后循环进行比较,注意不能越界,所以在越界时就要进行改正操作,最后排序完毕将新数组中已排序好的数据存入原数组即可。

//归并排序(非递归实现)
void MergeSortNR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc error");return;}//gap表示每一组归并的数据个数int gap = 1;while (gap < n){//i表示每次归并的起始位置for (int i = 0; i < n; i += gap * 2){//[begin1,end1][begin2,end2]int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;// 第二组都越界不存在,这一组就不需要归并if (begin2 >= n)break;// 第二的组begin2没越界,end2越界了,需要修正一下,继续归并if (end2 >= n)end2 = n - 1;//归并左右区间int j = i;while (begin1 < end1 && begin2 < end2){//取出较小的值存入新建的数组if (a[begin1] < a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}//如果原区间剩余部分数据则直接尾插入新建数组while (begin1 < end1){tmp[j++] = a[begin1];}while (begin2 < end2){tmp[j++] = a[begin2];}//每次排序后都要拷贝一遍memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;}free(tmp);tmp = NULL;
}

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

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

相关文章

idea项目maven下载依赖报错

报错&#xff1a; 1、Failure to find bad.robot:simple-excel:jar:1.0 in https://maven.aliyun.com/repository/public was cached in the local repository, resolution will not be reattempted until the update interval of aliyunmaven has elapsed or updates are forc…

python的while循环与for循环总结

前两章中&#xff0c;我们跟着海绵宝宝的故事&#xff0c;掌握了 while 循环和 for 循环&#xff0c;这两种不同的循环模式。while 循环和 for 循环都需要有 循环体 和 缩进&#xff0c;我们来复习一下它俩的语法规则&#xff1a; while 循环与 for 循环辨析 学到这里&#x…

在鲲鹏服务器搭建k8s高可用集群分享

高可用架构 本文采用kubeadm方式搭建k8s高可用集群&#xff0c;k8s高可用集群主要是对apiserver、etcd、controller-manager、scheduler做的高可用&#xff1b;高可用形式只要是为&#xff1a; 1. apiserver利用haproxykeepalived做的负载&#xff0c;多apiserver节点同时工作…

易联众智慧云胶片平台,助推医学影像服务“向云端”

在门诊室里,张女士焦急地告诉主治医师,自己忘了带CT胶片。“您别急,我用系统查询一下。”医生轻点几下鼠标进入云胶片平台,只用不到10秒就顺利完成了影像调取。“不仅我可以看到,您在手机上也能随时随地查阅。”张女士根据提示操作,不仅能调阅自己的影像档案,连抽血化验结果都可…

[GeoServer系列]Shapefile数据发布

【GeoServer系列】——安装与发布shapefile数据-CSDN博客 将待发布数据放置指定目录下 webapps\geoserver\data\data 创建存储仓库 新建矢量数据源 发布图层 设置边框 设置样式 使用 方式1 let highRoad new Cesium.WebMapServiceImageryProvider({url: http://local…

Spring Cloud学习笔记(Nacos):Nacos持久化(未完成)

这是本人学习的总结&#xff0c;主要学习资料如下 - 马士兵教育 1、Overview2、单机使用MySQL 1、Overview 我们关闭单机下的Nacos后&#xff0c;再重新启动会发现之前配置的内容没有被删除。这时因为Nacos有内嵌的数据库derby&#xff0c;会自己持久化。 但是在集群的情况下…

KAN(Kolmogorov-Arnold Network)的理解 3

系列文章目录 第一部分 KAN的理解——数学背景 第二部分 KAN的理解——网络结构 第三部分 KAN的实践——第一个例程 文章目录 系列文章目录前言KAN 的第一个例程 get started 前言 这里记录我对于KAN的探索过程&#xff0c;每次会尝试理解解释一部分问题。欢迎大家和我一起讨…

百度/迅雷/夸克,网盘免费加速,已破!

哈喽&#xff0c;各位小伙伴们好&#xff0c;我是给大家带来各类黑科技与前沿资讯的小武。 之前给大家安利了百度网盘及迅雷的加速方法&#xff0c;详细方法及获取参考之前文章&#xff1a; 刚刚&#xff01;度盘、某雷已破&#xff01;速度50M/s&#xff01; 本次主要介绍夸…

simulink基础学习笔记

写在前面 这个笔记是看B站UP 快乐的宇航boy 所出的simulink基础教程系列视频过程中记下来的&#xff0c;写的很粗糙不完整&#xff0c;也不会补。视频教程很细跟着做就行。 lesson1-7节的笔记up有&#xff0c;可以加up的群&#xff0c;里面大佬挺活跃的。 lesson8 for循环 For …

【C++初阶学习】第十二弹——stack和queue的介绍和使用

C语言栈&#xff1a;数据结构——栈(C语言版)-CSDN博客 C语言队列&#xff1a;数据结构——队列&#xff08;C语言版&#xff09;-CSDN博客 前言&#xff1a; 在之前学习C语言的时候&#xff0c;我们已经学习过栈与队列&#xff0c;并学习过如何使用C语言来实现栈与队列&…

OCR图片转Excel表格:没结构化的弊端

随着OCR技术的不断发展&#xff0c;将表格图片转为excel已不再是难题&#xff0c;但是&#xff0c;目前市面上的程序还大多处于仅能将图片表格转为普通的excel格式阶段&#xff0c;而不能将其结构化&#xff0c;这样就会产生许多的弊端&#xff0c;具体弊端如下&#xff1a; &l…

数据容器的通用操作、字符串大小比较 总结完毕!

1.数据容器的通用操作 1&#xff09;五类数据容器是否都支持while循环/for循环 五类数据容器都支持for循环遍历 列表、元组、字符串都支持while循环&#xff0c;集合、字典不支持&#xff08;无法下标索引&#xff09; 尽管遍历的形式不同&#xff0c;但都支持遍历操作 2&a…

办公软件 Office 安装教程(亲测有效)

Office 现已更名为 Microsoft 365。习惯还是称作 Office。 1、Office 套装下载 Windows 的样子 这里下载的是最新版本的 O365ProPlus 安装完成后&#xff0c;点击关闭&#xff08;请先不要打开&#xff09;。 Mac 的样子 这里下载的是Office for Mac 2019&#xff08;更多版…

速递FineWeb:一个拥有无限潜力的15T Tokens的开源数据集

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

内核宕机自救

【问题】在测试内核级防篡改时&#xff0c;偶尔会遇到内核宕机的问题 【结论】进入紧急救援模式&#xff0c;将服务进程文件的start注释掉&#xff0c;即可 在Linux系统启动时&#xff0c;内核启动顺序选择界面&#xff0c;进入系统欢迎界面按上下左右键进入GRUB界面&#xff…

欧佩克+同意集体性减产延长,油价能否稳住?

KlipC报道&#xff1a;欧佩克组织同意将延长目前部分减产协议至2025年&#xff0c;以支撑油价。主要成员国把2023年11月宣布的日均220万桶的自愿减产措施延长至今年9月底&#xff0c;将在10月份根据市场情况开始缩减自愿减产规模。 高盛分析师表示&#xff0c;“我们认为这次欧…

python常见数据分析函数

apply DataFrame.apply(func, axis0, broadcastFalse, rawFalse, reduceNone, args(), **kwds) 第一个参数是函数 可以在Series或DataFrame上执行一个函数 支持对行、列或单个值进行处理 import numpy as np import pandas as pdf lambda x: x.max()-x.min()df pd.DataFrame(…

高端、大气、很牛B的免费wordpress模板主题

这是一款专为WordPress打造的极简主义风格主题&#xff0c;以白色和黑色为主色调&#xff0c;搭配红色点缀&#xff0c;营造出一种简洁、专业且具有视觉冲击力的效果。 该主题的设计理念是“简单即美”&#xff0c;旨在帮助用户快速搭建一个美观、易用的网站。它提供了丰富的自…

动态sql set标签 , trim标签

set标签 来看例子 set标案解决了逗号问题(当if条件不满足时,逗号无处安放的问题),我认为set标签可以识别这个问题,并自动忽略这个问题 <update id"update">update employee<set><if test"name!null">name#{name},</if><if te…

HTML基本元素包含HTML表单验证

可将以下代码复制另存为一个HTML文件浏览器打开自己去看看实际使用效果 <!DOCTYPE html> <html> <head> <meta charset"utf-8"><title>测试</title> </head> <body> <h1>很多事</h1> <h1><b&…