常见排序算法,快排,希尔,归并,堆排

后面的排序中都要用到的函数

//交换
void Swap(int* p1, int* p2)
{int* tmp = *p1;*p1 = *p2;*p2 = tmp;
}

包含的头文件 "Sort.h"

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<time.h>
#include<string.h>//交换
void Swap(int* p1, int* p2);
//向下调整,parent和child都是指下标,n指要调整的所有数据的上限下标
void AdjustDown(int* arr, int n, int parent);
//堆排序
void HeapSort(int* a, int n);// 插入排序
void InsertSort(int* a, int n);// 希尔排序
void ShellSort(int* a, int n);// 选择排序
void SelectSort(int* a, int n);// 冒泡排序
void BubbleSort(int* a, int n);// 快速排序递归实现
// 快速排序hoare版本
//left:区间的左端点,right:区间的右端点(都是指下标)
int QuickSort(int* a, int left, int right);// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right);//归并排序,n是数组长度
void MergeSort(int* a, int n);
//归并排序非递归实现
void MergeSortNonR(int* a, int n);

一.冒泡排序

// 冒泡排序
void BubbleSort(int* a, int n)
{//单趟:把最大的数换到最后for (int i = 0; i < n; i++){//如果一趟走完没有发生交换,说明已经有序int flag = 1;for (int j = 0; j < n - i - 1; j++){if (a[j] > a[j + 1]){Swap(&a[j], &a[j + 1]);flag = 0;}}if (flag == 1)return;}
}

二.选择排序

// 选择排序
void SelectSort(int* a, int n)
{int begin = 0;int end = n-1;while(begin < end){int mini = begin;int maxi = end;//二者都是下标//[i,n]中选出最大,和最小的数,分别放到begin,end的位置for (int i = begin; i <= end; i++){if (a[i] < a[mini]){mini = i;}if (a[i] > a[maxi]){maxi = i;}}Swap(&a[begin], &a[mini]);//maxi可能和begin重叠,此时maxi换到了mini位置if (maxi == begin)maxi = mini;Swap(&a[end], &a[maxi]);begin++;end--;}
}

三.插入排序

// 插入排序,O(N^2)
void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){//把a[end+1]插入到[0,end]的位置int end = i;int tmp = a[end + 1];while (end >= 0){if (a[end] > tmp){a[end + 1] = a[end];//大的数往后挪end--;}else{break;}//出循环有两种情况://1.end = -1,即tmp比[0,end]之间的数都要小//2.a[end]<tmpa[end + 1] = tmp;}}
}

四.堆排

//向下调整,parent和child都是指下标,n指要调整的所有数据的上限下标
void AdjustDown(int* arr, int n, int parent)
{int child = parent * 2 + 1;while (child < n)//child >= n,说明孩子不存在{//建小堆(建大堆:表示比较关系的"<",">"全部取反)//假设法,假设左孩子小if (arr[child + 1] < arr[child] && (child + 1) < n)//防止child+1越界{child++;}if (arr[parent] > arr[child]){Swap(&arr[parent], &arr[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(int* a, int n)
{assert(a);//1.向下调整建堆(小堆)int i = 0;for (i = (n - 1 - 1) / 2; i >= 0; i--)//n-1是最后一个数据的下标,(n-1-1)/2是最后一个非叶子节点{AdjustDown(a, n, i);}//2.堆顶的数据和最后一个数据进行交换,调整完的数据向下调整,再将钱n-1个数据看做一个新堆//堆顶(最小的)数据放到数据末尾,第二小的放到倒数第二位,依次进行int end = n - 1;while (end > 0){//交换Swap(&a[0], &a[end]);//调整AdjustDown(a, end, 0);end--;}
}

五.希尔排序

// 希尔排序,时间复杂度O(N^1.3)
void ShellSort(int* a, int n)
{//1.分gap组int gap = n;while (gap > 1){// +1保证最后一个gap一定是1// gap > 1时是预排序// gap == 1时是插入排序gap = gap / 3 + 1;//2.多个gap组并行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;//一次跳gap步}else{break;}a[end + gap] = tmp;}}}
}

六.快速排序(快排)

三数取中

int Getmidi(int*a,int left,int right)
{int midi = (right + left) / 2;//左<中if (a[left] < a[midi]){//中>右,中是最大的if (a[midi] > a[right]){//左<右<中if (a[left] < a[right]){return right;}else//右<左<中{return left;}}else//左<中<右{return midi;}}else//左>中{//中<左,中<右if (a[midi] < a[right]){//中<左<右if (a[left] < a[right]){return left;}else//中<右<左{return right;}}else//右<中<左{return midi;}}
}

1.快排单趟的不同版本

1.1 hoare版本

//单趟(hoare版本)
int partsort1(int*a,int left,int right)
{//优化——keyi的选择:// 如果数据是有序的,此时再选left位置的数当作keyi,则分割的区间就会变成0+n-1,时间复杂度退化为O(N^2)//解决方法——三数取中:选left,right,midi(区间中间位置的数)中,中间大小的数)	int midi = Getmidi(a, left, right);Swap(&a[midi], &a[left]);int keyi = left;int begin = left;int end = right;while (begin < end){//左边找大,右边找小//begin<end 这个条件要加上,否则在找大(或找小)的过程中,begin可能越过了endwhile (begin < end && a[begin] < a[keyi]){++begin;}while (begin<end && a[end]>a[keyi]){--end;}//begin和end停下后,交换,把比a[keyi]大的数放右边,小的数放左边Swap(&a[begin], &a[end]);}//begin和end相遇的位置(记为meet)一定比a[keyi]小(该结论可证明),二者交换Swap(&a[keyi], &a[begin]);return begin;
}

1.2前后指针法

//前后指针法
int partsort2(int* a, int left, int right)
{//优化——keyi的选择:// 如果数据是有序的,此时再选left位置的数当作keyi,则分割的区间就会变成0+n-1,时间复杂度退化为O(N^2)//解决方法——三数取中:选left,right,midi(区间中间位置的数)中,中间大小的数)	int midi = Getmidi(a, left, right);Swap(&a[midi], &a[left]);int prev = left;int cur = prev + 1;//a[cur]<a[left],二者同时++//a[cur]<a[left],cur++,prev不动while (cur <= right){//a[cur]<a[left]时,有两种情况//prev++后,1.a[prev]<a[left] 2.a[prev]>a[left]//若是第一种情况,则二者不需要交换,且此时prev == cur//第二种情况,二者需要交换if (a[cur] < a[left] && ++prev != cur)//这里的++是前置++,能保证在第一个条件满足的前提下,prev一定向前移动{Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[prev], &a[left]);return prev;
}

2.快排的不同版本

2.1 递归版

int QuickSort(int* a, int left, int right)
{//结束条件:1.区间内只剩一个数;2.区间不存在if (left >= right)return;//优化——小区间优化:当区间<10时,再使用函数递归调用很不划算,所以改为使用插入排序if (right - left <= 10){//区间左端:a+left,大小:right - left + 1  ([0,9]是十个数)InsertSort(a + left, right - left + 1);}else{//单趟int keyi = partsort1(a, left, right);//meet将数据分割为左区间和右区间//根据二叉树的思想,使用递归,对左右区间进行同样的操作//[left,keyi-1] keyi [keyi+1,right]QuickSort(a, left, keyi - 1);QuickSort(a, keyi + 1, right);}
}

2.2 非递归版

(栈实现的所有接口代码我会放在后面,也可看我之前的博客) 

#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 = partsort1(a, begin, end);//[begin,keyi-1] keyi [keyi+1,end]//先入右区间,再入左区间,先取出的就是左区间,和递归的逻辑顺序相一致//区间只有一个值或区间不存在时,不需要入栈if (keyi + 1 < end){STPush(&st, end);STPush(&st, keyi + 1);}if (begin < keyi - 1){STPush(&st, keyi - 1);STPush(&st, begin);}}STDestroy(&st);//栈的销毁
}

七.归并排序

1.递归版

//归并排序
void _MergeSort(int* a, int* tmp, int begin, int end)(子函数)
{//当数据有序时,可以进行归并//当区间内只有一个数时,可看做有序,然后两两归并//函数返回后,进行四四归并if (begin >= end)return;int mid = (begin + end) / 2;_MergeSort(a, tmp, begin, mid);_MergeSort(a, tmp, mid+1,end);//归并int begin1 = begin;int begin2 = mid + 1;int i = begin;//i是tmp中的元素下标//对进行归并的两个区间进行排序,并将排序后的结果放到tmp数组中while (begin1 <= mid && begin2 <= end){if (a[begin1] < a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}while (begin1 <= mid){tmp[i++] = a[begin1++];}while (begin2 <= end){tmp[i++] = a[begin2++];}//将tmp数组中的数拷贝回a数组//注意拷贝范围是归并后的两个区间和memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));//[0,9]是十个数
}
//n是数组长度
void MergeSort(int* a, int n)//主函数
{//在主函数内创建数组,在子函数内实现排序算法//这样可避免在递归时,子函数重复动态申请内存的过程int* tmp = (int*)malloc(sizeof(int)*n);if (tmp == NULL){perror("malloc");return;}_MergeSort(a, tmp, 0, n - 1);free(tmp);tmp = NULL;
}

2.非递归版

//归并排序非递归实现
void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc");return;}int gap = 1;while (gap < n){//单趟归并//11归并,22归并,44归并//每次归并两组,一组含有gap个数,gap是2的次方倍for (int i = 0; i < n; i += 2*gap){//[begin1,end1] [begin2,end2]int begin1 = i;int end1 = begin1 + gap - 1;int begin2 = end1 + 1;int end2 = begin2 + gap - 1;//对进行归并的两个区间进行排序,并将排序后的结果放到tmp数组中//在这样的区间划分下,end1,begin2,end2都有可能会越界//修改优化:if (begin2 >= n)//begin2越界,说明end2肯定也越界了,此时不再需要归并{break;}if (end2 >= n)//走到这里说明begin2,end1都没有越界,修改end2后继续归并{end2 = n - 1;}int j = begin1;//j是tmp中的元素下标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));//为防止越界,不能直接乘以2倍的gap}gap *= 2;}free(tmp);tmp = NULL;
}

附:栈的接口代码

Stack.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>typedef int STDataType;typedef struct Stack
{STDataType* arr;//用数组来实现栈int top;int capacity;
}ST;// 初始化和销毁
void STInit(ST* pst);
void STDestroy(ST* pst);// 入栈  出栈
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);// 取栈顶数据
STDataType STTop(ST* pst);// 判空
bool STEmpty(ST* pst);
// 获取数据个数
int STSize(ST* pst);

Stack.c

#include"Stack.h"// 初始化和销毁
void STInit(ST* pst)
{assert(pst);pst->arr = NULL;pst->capacity = 0;pst->top = 0;//top指向栈顶元素的下一位,数组下标从0开始//top = 数据个数
}
void STDestroy(ST* pst)
{assert(pst);free(pst->arr);pst->arr = NULL;pst->capacity = 0;pst->top = 0;
}// 入栈  出栈
void STPush(ST* pst, STDataType x)
{assert(pst);if (pst->capacity == pst->top)//扩容{int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;STDataType* tmp = (STDataType*)realloc(pst->arr, newcapacity*sizeof(STDataType));if (tmp == NULL){perror("realloc");exit(1);}pst->arr = tmp;pst->capacity = newcapacity;}pst->arr[pst->top++] = x;
}
void STPop(ST* pst)
{assert(pst);assert(pst->top > 0);//栈的数据个数大于0pst->top--;
}// 取栈顶数据
STDataType STTop(ST* pst)
{assert(pst);assert(pst->top > 0);//栈的数据个数大于0return pst->arr[(pst->top - 1)];
}// 判空
bool STEmpty(ST* pst)
{assert(pst);return (pst->top == 0);
}
// 获取数据个数
int STSize(ST* pst)
{assert(pst);return pst->top ;
}

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

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

相关文章

70 Realistic Mountain Environment Textures Cliff(70+张真实的山地环境纹理)

大量适合山区和其他岩石环境的纹理--悬崖、岩石、砾石等等 每个纹理都是可贴的/无缝的,并且完全兼容各种不同的场景--标准Unity地形、Unity标准着色器、URP、HDRP等等都兼容。 所有的纹理都是4096x4096,并包括一个HDRP掩码,以完全支持HDRP。 特点。 70种质地 70种材料 70个地…

C#——固态数组

固态数组 概念: 1.数组 是存储一组相同数据类型的集合 2.数组分为 一线数组 多维数组 交错数组 3.一般情况下 一维数组 就称之为数组 声明: 方法1: 数据类型[ ] 数组名 new 数据类型[ 长度 ] 实例: int[] arr new int[10] 方法2: 数据类型[ ] 数组名 new 数据类型[ 长度…

AI视频教程下载:用LangChain开发 ChatGPT和 LLMs 应用

在这个快速变化的人工智能时代&#xff0c;我们为您带来了一场关于语言模型和生成式人工智能的革命性课程。这不仅仅是一个课程&#xff0c;而是一次探险&#xff0c;一次深入人工智能核心的奇妙之旅。 在这里&#xff0c;您将开启一段激动人心的旅程&#xff0c;探索语言模型…

利用架构挖掘增强云管理

管理当今复杂的云环境比以往任何时候都更加重要。 大多数企业依赖 AWS、Azure、Kubernetes 和 Microsoft Entra ID 等各种平台来支持其运营&#xff0c;但管理这些平台可能会带来重大挑战。 云优化的最大挑战涉及安全性、成本管理和了解云基础设施内错综复杂的相互依赖关系。…

【数据库】255对比256的优势

文章目录 2552^8-1 &#xff0c;很多时候用到255却步凑整到256&#xff0c;这是为啥呢?一番了解总结 2552^8-1 &#xff0c;很多时候用到255却步凑整到256&#xff0c;这是为啥呢? 比如下面的两种情况: RabbitMQ的routing_key 和 binding_key 的最大长度255 字节。Navicat添…

基于springboot实现农产品直卖平台系统项目【项目源码+论文说明】

基于springboot实现农产品直卖平台系统的设计演示 摘要 计算机网络发展到现在已经好几十年了&#xff0c;在理论上面已经有了很丰富的基础&#xff0c;并且在现实生活中也到处都在使用&#xff0c;可以说&#xff0c;经过几十年的发展&#xff0c;互联网技术已经把地域信息的隔…

常见的Web漏洞——CORS

渗透做了多年的朋友都知道&#xff0c;大洞小洞都是漏洞。因此也学习、沉淀一下以前没重视的漏洞。 简介 CORS&#xff08;Cross-Origin Resource Sharing&#xff0c;跨源资源共享&#xff09;是一种由Web浏览器实现的安全策略&#xff0c;用于控制一个Web页面&#xff08;服…

finereport 9.0 Tomcat 集群-来自帆软

Tomcat服务器集群 目录: 1. 描述3. 配置过程4. 结果测试 1. 描述 在单一的服务器上执行WEB应用程序有一些问题&#xff0c;当网站成功建成并开始接受大量请求时&#xff0c;单一服务器无法满足需要处理的负荷量。 另外一个常见的问题是会产生单点故障&#xff0c;如果该服务器坏…

AppML 下载

要下载 AppML&#xff0c;您可以访问菜鸟教程网站或自强学堂网站。AppML 不是一个产品&#xff0c;而是一个基于浏览器和服务器脚本的想法。您可以从这些网站下载适用于 PHP 或 ASP.NET 的 AppML 版本。下载文件将包含服务端脚本、样式文件、浏览器脚本和本地配置文件&#xff…

Java桥接模式

桥接模式 最重要的是 将 抽象 与 实现 解耦 , 通过组合 在 抽象 与 实现 之间搭建桥梁 ; 【设计模式】桥接模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )-CSDN博客 桥接模式&#xff08;Bridge Pattern&#xff09;-&#xff08;最通俗易懂的案例&#xff09;_桥接模式 例子-…

力扣 242题 有效的字母异位 记录

题目描述 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t 互为字母异位词。示例 1: 输入: s "anagram", t "nagaram" 输出: true示例 …

高效学习LabVIEW的方法

学习LabVIEW可以通过系统化课程、在线资源、自学实验、参与论坛、结合实际项目等多角度进行。系统课程提供全面基础&#xff0c;在线资源便于查漏补缺&#xff0c;自学实验强化理解&#xff0c;论坛互动解决疑难&#xff0c;结合实际项目应用提高实践技能。结合项目学习是最高效…

有趣的C语言——我的C语言之路

记录一下关于C语言竞赛重刷的例题&#xff0c;有些真的很有意思&#xff0c;如果你觉得特别简单&#xff0c;那说明你很牛&#xff0c;小弟我甘拜下风&#xff01;! 勿喷 勿喷&#xff0c;仅记录学习历程&#xff08;有些特别基础的就不弄了&#xff0c;弄一些我觉得处理方法很…

Docker 中运行的 MySQL 数据库与 Docker 外部的管理系统连接

步骤 1&#xff1a;运行 MySQL 容器 首先&#xff0c;确保你的 Docker 容器中运行了 MySQL 数据库。 docker run --name mysql-container -e MYSQL_ROOT_PASSWORDmy-secret-pw -d -p 3306:3306 mysql:latest--name mysql-container 为容器命名。-e MYSQL_ROOT_PASSWORDmy-sec…

python3 -m http.server 检查打包前端的项目

python3 -m http.server这是 Python 提供的一个内置的简单 HTTP 服务器。当你在终端中运行 python3 -m http.server 命令时(在对应的打包目录比如dist目录)&#xff0c;Python 会启动一个 HTTP 服务器&#xff0c;它会将当前工作目录下的文件作为静态文件提供给浏览器。这个服务…

LabVIEW软件开发人员的核心能力是什么

LabVIEW软件开发人员的核心能力包括以下几个方面&#xff1a; 1. LabVIEW编程技能 熟练掌握LabVIEW编程语言&#xff1a;包括虚拟仪器&#xff08;VI&#xff09;的创建、数据流编程、图形化编程技巧等。 模块化编程&#xff1a;能够设计和实现模块化的代码结构&#xff0c;便…

python数据分析——正则化

参考资料&#xff1a;活用pandas库 正则化旨在解决模型的过拟合问题。 在实际中&#xff0c;可能存在模型的训练分数很好&#xff0c;而测试分数却很低&#xff0c;这说明模型出现了过拟合问题。正则化通过对系数和变量施加约束来解决模型过拟合的问题。这会导致数据的系数变小…

数据结构与算法之计数排序

目录 前言 计数排序 定义 优缺点 策略 图解 代码实现 结束语 前言 今天是坚持写博客的第20天&#xff0c;时光飞逝&#xff0c;第二个10天即将过去&#xff0c;希望可以继续坚持&#xff0c;光明的未来也在未来等着我们。今天也恰逢高考&#xff0c;祝所有学子一帆风顺…

【DevOps】掌握 Helm:Kubernetes 应用程序部署指南

目录 一、前言 二、什么是 Helm&#xff1f; 三、Helm 的优势 四、Helm 组件 五、安装和配置 Helm 1、先决条件 2、安装 Helm 客户端 3、初始化 Helm 4、添加 Helm Charts 存储库 5、搜索和安装应用程序 5.1、搜索 Helm Charts 5.2、安装应用程序 5.3、自定义应用程…

echarts的toolbox自定义feature标签及事件

1. 需求 在使用echarts图时希望toolbox扩展一些自定义icon和点击事件&#xff0c;而不只是图中这些echarts提供的事件。 2. 文档 属性名类型描述toolbox.featureObject各工具配置项。 feature中除了echarts提供的各个内置的工具按钮外&#xff0c;可以自定义工具按钮。 除…