数据结构(初阶7)---七大排序法(堆排序,快速排序,归并排序,希尔排序,冒泡排序,选择排序,插入排序)(详解)

排序

  • 1.插入排序
  • 2.希尔排序
  • 3.冒泡排序
  • 4.选择排序(双头排序优化版)
  • 5.堆排序
  • 6.快速排序
    • 1). 双指针法
    • 2).前后指针法
    • 3).非递归法
  • 7.归并排序
    • 1).递归版本(递归的回退就是归并)
    • 2).非递归版本(迭代版本)

计算机执行的最多的操作之一就有排序,排序是一项极其重要的技能
接下来我们来详细介绍一下,我们遇到最多的排序方法

1.插入排序

首先我们介绍的是最基本的排序方式,插入排序

给定一个数组
[2,3,5,7,6,4,1]
请添加图片描述

代码实现:如何在数组中实现插入呢,
1.先将p+1的值记住。
2.从后向前遍历,找到比它小的前一个数
3.在遍历的时候,只需要将后面的值赋值给前面的值

void insertsort(int* a, int n)
{for (int count = 0; count < n; count++){int end = count;int tmp = a[count+1];while (end >= 0){if (a[end] > tmp){a[end+1] = a[end];end--;}else{break;}}a[end+1] = tmp;}}

这是最基本的排序方式
遍历了n+(n-1)+(n-2)+…+1
共计时间复杂度为O(n^2);

2.希尔排序

希尔排序是插入排序的升级版,是一种强大的排序方式。
我们发现如果我们正常使用选择排序,如果我的极值在数组的两头,我们就需要花费更多的时间将两头的元素一步一步的移到另一端,所以希尔大佬就发明了一种跳跃式移动的方法——希尔排序

给定一个数组:[5,3,2,4,1,2,2,3,4]需要排为升序
请添加图片描述
所以我们可以去迭代gap,让gap从sz/3开始逐渐向1迭代,
这样数列逐渐有序->最终变为有序

void ShellSort(int*a,int n)
{int gap=n;while (gap > 1){gap = gap / 3 + 1;for (int count = 0; count < n - gap; count++){int end = count;int tmp = a[end + gap];while (end >= 0){if (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}

在实际应用中,希尔排序的平均时间复杂度通常被认为是
O(n log n)到O(n^(1.2)) 之间

3.冒泡排序

冒泡排序应该是所有初学者第一次学习的排序算法,也是效率最低的一种算法

这里就不过多赘述
通过一次次的冒泡排序,每一次都将一个数字移到正确的位置

void swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void bubblesort(int *p,int sz)
{for (int count = 0; count < sz; count++){for (int cur = 0; cur < sz - count-1; cur++){if (p[cur] > p[cur+1]){swap(p + cur, p + cur + 1);}}}
}

4.选择排序(双头排序优化版)

选择排序应该也是一种十分常见的排序方式

这里我们就在原有基础上优化升级一下,双头选择排序优化版
从两头分别取数,左边取一个交换为较小,右边取一个交换为较大
给定一个数组:[5,3,2,4,1,2,2,3,4]需要排为升序请添加图片描述
时间复杂度还是O(n^2)但是效率要略高一些。

5.堆排序

堆排序我已经在这篇文章中仔细地阐述了:
可以点击以下链接去访问:

数据结构(初阶5)—堆与堆排序(详解)

6.快速排序

运用最最广泛的一种排序方式,以其独特的解决思想而闻名。
他的变种和相关推论有许多,这边着重将双指针法前后指针法
优化方式为三数取中法

快速排序的本质就是分治的思想,递归的思想
给定一个数组:[5,3,2,4,1,2,2,3,4]需要排为升序
请添加图片描述

1). 双指针法

请添加图片描述
为什么我们必须让p1先走呢,因为p1先走,最后相遇的时候,

相遇的值一定大于key,可以直接将key与相遇值交换
我们也许会想到,如果key的左边全部都大于key,这种极端的情况下,我们的快排就会变得十分的不稳定
所以我们有一个三数取中的优化方法
先将key与中间值与排头比较,最后将第二大的值与key的位置交换

void swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
int mid_inthree(int* arr, int begin, int end)
{if (arr[begin] > arr[end] && arr[begin] < arr[(begin + end) / 2])return begin;else if (arr[end] > arr[begin] && arr[end] < arr[(begin + end) / 2])return end;elsereturn (end + begin) / 2;
}
void my_qsort(int* arr, int begin, int end)//双指针快排
{if (end <= begin){return;}swap(arr+end, arr+mid_inthree(arr, begin, end));int left = begin, right = end - 1;while (left <= right){while (arr[left] <= arr[end]&&left<=right){left++;}while (arr[right] >= arr[end]&&right>=left){right--;}if (left < right){swap(arr + left, arr + right);}}swap(arr + end, arr + left);my_qsort(arr, begin, left - 1);my_qsort(arr, left + 1, end);
}

2).前后指针法

请添加图片描述

void swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
int mid_inthree(int* arr, int begin, int end)
{if (arr[begin] > arr[end] && arr[begin] < arr[(begin + end) / 2])return begin;else if (arr[end] > arr[begin] && arr[end] < arr[(begin + end) / 2])return end;elsereturn (end + begin) / 2;
}
void my_qsort2(int* arr, int begin, int end)
{if (end <= begin){return;}swap(arr + end, arr + mid_inthree(arr, begin, end));int cur = begin, prev = begin-1;while (cur<end){while (cur < end && arr[cur] >= arr[end]){cur++;}prev++;swap(arr + cur, arr + prev);}swap(arr + prev+1, arr + end);my_qsort(arr, begin, prev - 1);my_qsort(arr, prev + 1, end);
}

3).非递归法

递归的短板不言而喻,稍微大一点的数组就会导致栈溢出
所以我们这里可以在堆上面建立一个模拟栈,总所周知栈上空间只有M量级堆上空间却有G量级

大致思路与上面类似,只是多了一个模拟栈,所以选择上面任意一种排序法就行,这里选择双指针快排作为核心
我们的模拟栈的作用是什么:用来存放和调用下标
比如说给定一个数组:[5,3,2,4,1,2,2,3,4]需要排为升序
哪么我的模拟栈中存入 (0,8)这两个区间的脚标
每进行一次快排,去掉内部的两个脚标,再放入四个脚标;
如果区间只有一个数字,就不放入模拟栈
最后栈变为空。
所以代码实现如下:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// 定义栈节点
typedef int STDataType;typedef struct Stack {STDataType _data;struct Stack* _next;
} Stack;// 初始化栈
Stack* StackIni() {return NULL; // 空栈直接用 NULL 表示
}// 销毁栈
void Stackdestory(Stack* pst) {while (pst) {Stack* tmp = pst;pst = pst->_next;free(tmp);}
}// 入栈
void StackPush(Stack** ppst, STDataType x) {Stack* newNode = (Stack*)malloc(sizeof(Stack));newNode->_data = x;newNode->_next = *ppst; // 新节点指向当前栈顶*ppst = newNode;        // 更新栈顶为新节点
}// 出栈
void StackPop(Stack** ppst) {assert(*ppst); // 确保栈非空Stack* tmp = *ppst;*ppst = (*ppst)->_next;free(tmp);
}// 判断栈是否为空
int Stackempty(Stack* pst) {return pst == NULL; // 空栈返回 1,非空返回 0
}// 获取栈顶元素
STDataType Stacktop(Stack* pst) {assert(!Stackempty(pst)); // 确保栈非空return pst->_data;
}

前面是栈的源码操作,和建立
下面实现非递归快排

void swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
int partsort(int* arr, int begin, int end)//双指针核心
{swap(arr + end, arr + mid_inthree(arr, begin, end));int left = begin, right = end - 1;while (left <= right){while (left <= right&&arr[left] <= arr[end]){left++;}while (right >= left &&arr[right] >= arr[end] ){right--;}if (left < right){swap(arr + left, arr + right);}}swap(arr + end, arr + left);return left;
}
void my_qsort3(int* arr, int begin, int end)//非递归快排
{Stack* tmp=StackIni();StackPush(&tmp, end);StackPush(&tmp, begin);while (!Stackempty(tmp)){int begin = Stacktop(tmp);StackPop(&tmp);int end = Stacktop(tmp);StackPop(&tmp);int div = partsort(arr, begin, end);if (div + 1 < end){StackPush(&tmp, end);StackPush(&tmp, div+1);}if (div - 1 > begin){StackPush(&tmp, div - 1);StackPush(&tmp, begin);}}Stackdestory(tmp);
}

这种操作可以有效规避栈溢出,但是不会提高性能。
用了三数取中的方法,我们的快排可以近似为O(nlogn)

在这里插入图片描述

7.归并排序

归并排序也是一个十分重要的排序,在许多场景下都有运用,其主要思想是分治

给定一个数组:[5,3,2,4,1,2,2,3]需要排为升序
主要思想如下:
递归到底,再向上归并
请添加图片描述

1).递归版本(递归的回退就是归并)

代码实现如下,在经历了二叉树便利的学习后,归并排序的代码其实并不难理解

void _MergeSort(int* a, int left, int right, int* tmp)//递归回退就是归并
{if (left >=right){return;}int mid = (left+right) / 2;_MergeSort(a, left, mid,tmp);_MergeSort(a, mid + 1, left, tmp);int begin_1 = left, end_1 = mid;int begin_2 = mid + 1, end_2 = right;int begin_tmp = begin_1;while (begin_1 <= end_1 && begin_2 <= end_2){if (a[begin_1] >= a[begin_2]){tmp[begin_tmp++] = a[begin_2++];}else {tmp[begin_tmp++] = a[begin_1++];}}while (begin_1 <= end_1)//因为是不等长的,所以我们需要考虑不等长的情况{tmp[begin_tmp++] = a[begin_1++];}while (begin_2 <= end_2){tmp[begin_tmp++] = a[begin_2++];}while (left <= end_2)//复制过去{a[left] = tmp[left];left++;}
}
void MergeSort(int* arr, int n)
{assert(arr);int* tmp = (int*)malloc(sizeof(int) * n);_MergeSort(arr,0,n-1,tmp);
}

2).非递归版本(迭代版本)

这个版本有点抽象,这种递归改非递归的题都很让人摸不着头脑qwq

创建数组int arr[ ] = {1,3,5,7,9,2,4,6,8,0};
我们创造一个gap,gap从1开始,为 2^(n-1)+1,这样代表1,3,7,15…刚好表示
请添加图片描述
代码实现:(切记要控制边界,且要处理特殊情况!!!)

void MergeSortpart(int* a, int left,int mid,int right, int* tmp)
{int begin_1 = left, end_1 = mid;int begin_2 = mid + 1, end_2 = right;int begin_tmp = begin_1;while (begin_1 <= end_1 && begin_2 <= end_2){if (a[begin_1] >= a[begin_2]){tmp[begin_tmp++] = a[begin_2++];}else{tmp[begin_tmp++] = a[begin_1++];}}while (begin_1 <= end_1)//因为是不等长的,所以我们需要考虑不等长的情况{tmp[begin_tmp++] = a[begin_1++];}while (begin_2 <= end_2){tmp[begin_tmp++] = a[begin_2++];}while (left <= right)//复制过去{a[left] = tmp[left];left++;//printf("%d ", a[left]);}
}
void MergeSort(int* arr, int n)
{assert(arr);int* tmp = (int*)malloc(sizeof(int) * n);//_MergeSort(arr,0,n,tmp);int gap = 1;for (gap = 1; gap <= n-1; gap=gap*2+1){for (int x = 0; x+gap<=n; x += gap+1){int begin_1 = x, end_1 = x + gap;int mid = (begin_1 + end_1) / 2;MergeSortpart(arr, begin_1,mid,end_1,tmp);}for (int i = 0; i < n + 1; i++){printf(" %d", arr[i]);}printf("\n");}if (gap/2 != n-1)//处理后面多出来的一小块{MergeSortpart(arr, 0, gap/2, n,tmp);}//_MergeSort(arr,0,n-1,tmp);
}

(*´∀`)~♥

创作不易,恳请留一个赞吧qwq,有留言必回 (*´∀`)~♥
在这里插入图片描述

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

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

相关文章

DataWhale—PumpkinBook(TASK07支持向量机)

课程开源地址及相关视频链接&#xff1a;&#xff08;当然这里也希望大家支持一下正版西瓜书和南瓜书图书&#xff0c;支持文睿、秦州等等致力于开源生态建设的大佬✿✿ヽ(▽)ノ✿&#xff09; Datawhale-学用 AI,从此开始 【吃瓜教程】《机器学习公式详解》&#xff08;南瓜…

【Python数据分析五十个小案例】使用自然语言处理(NLP)技术分析 Twitter 情感

博客主页&#xff1a;小馒头学python 本文专栏: Python爬虫五十个小案例 专栏简介&#xff1a;分享五十个Python爬虫小案例 项目简介 什么是情感分析 情感分析&#xff08;Sentiment Analysis&#xff09;是文本分析的一部分&#xff0c;旨在识别文本中传递的情感信息&…

【数据结构与算法】排序算法(上)——插入排序与选择排序

文章目录 一、常见的排序算法二、插入排序2.1、直接插入排序2.2、希尔排序( 缩小增量排序 ) 三、选择排序3.1、直接选择排序3.2、堆排序3.2.1、堆排序的代码实现 一、常见的排序算法 常见排序算法中有四大排序算法&#xff0c;第一是插入排序&#xff0c;二是选择排序&#xff…

Educator头歌:离散数学 - 图论

第1关&#xff1a;图的概念 任务描述 本关任务&#xff1a;学习图的基本概念&#xff0c;完成相关练习。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;图的概念。 图的概念 1.一个图G是一个有序三元组G<V,R,ϕ>&#xff0c;其中V是非空顶点集合&am…

oracle RAC各版本集群总结和常用命令汇总

oracle RAC学习 RAC介绍 RAC&#xff1a;高可用集群&#xff0c;负载均衡集群&#xff0c;高性能计算集群 RAC是⼀种⾼可⽤&#xff0c;⾼性能&#xff0c;负载均衡的share-everything的集群 8i:内存融合雏形 内存融合雏形&#xff08;Oracle Parallel Server&#xff09;…

数据资产管理是什么?为什么重要?核心组成部分(分类分级、登记追踪、质量管理、安全合规)、实施方法、未来趋势、战略意义

文章目录 一、引言&#xff1a;数据的新时代二、什么是数据资产管理&#xff1f;2.1 定义2.2 核心功能 三、为什么数据资产管理至关重要&#xff1f;3.1 面对的数据管理挑战 四、数据资产管理的核心组成部分4.1 数据分类与分级4.2 数据资产登记与追踪4.3 数据质量管理4.4 数据安…

C++高阶算法[汇总]

&#xff08;一&#xff09;高精度算法概述 高精度算法是指能够处理超出常规数据类型表示范围的数值的算法。在 C 中&#xff0c;标准数据类型通常有固定的位数和精度限制&#xff0c;而高精度算法可以解决大数运算、金融计算和科学计算等领域的问题。 &#xff08;二&#x…

springboot365高校疫情防控web系统(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;高校疫情防控的设计与实现 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为…

Electron实现打开子窗口加载vue路由指定的组件页面白屏

白屏有两种情况&#xff1a; Vue项目使用的history路由的话就会显示空白&#xff0c;加载不出来路由&#xff0c;也不能跳转路由 这种情况看我上一篇文章Electron vue3 打包之后不能跳转路由-CSDN博客 Electron中已经能正常加载页面跳转路由&#xff0c;但是创建子窗口加载子页…

智能探针技术:实现可视、可知、可诊的主动网络运维策略

网络维护的重要性 网络运维是确保网络系统稳定、高效、安全运行的关键活动。在当今这个高度依赖信息技术的时代&#xff0c;网络运维的重要性不仅体现在技术层面&#xff0c;更关乎到企业运营的方方面面。网络运维具有保障网络的稳定性、提升网络运维性能、降低企业运营成本等…

泷羽sec-shell脚本(全) 学习笔记

声明&#xff01; 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&a…

鸿蒙学习使用模拟器运行应用(开发篇)

文章目录 1、系统类型和运行环境要求2、创建模拟器3、启动和关闭模拟器4、安装应用程序包和上传文件QA:在Windows电脑上启动模拟器&#xff0c;提示未开启Hyper-V 1、系统类型和运行环境要求 Windows 10 企业版、专业版或教育版及以上&#xff0c;且操作系统版本不低于10.0.18…

MySQL 利用JSON特性完成复杂数据存储和查询

情景描述 下面一个应用场景&#xff0c;是数据库需要存储文库类的信息。文库分多个种类&#xff0c;比如图书类、论文类等多个类别&#xff0c;每个类别有不同的字段信息。 常规处理方法 要在单张表中去存储不同种类的文库数据&#xff0c;表就会变成这样的结构&#xff1a; …

【数据结构】哈希 ---万字详解

unordered系列关联式容器 在C98中&#xff0c;STL提供了底层为红黑树结构的一系列关联式容器&#xff0c;在查询时效率可达到log_2 N&#xff0c;即最差情况下需要比较红黑树的高度次&#xff0c;当树中的节点非常多时&#xff0c;查询效率也不理想。最好 的查询是&#xff0c…

【Redis篇】Hash的认识以及相关命令操作

目录 前言 基本命令 HSET HGET HEXISTS HDEL HKEYS HVALS HGETALL HMGET HLEN HSETNX HINCRBY HINCRBYFLOAT 内部编码 高内聚&#xff0c;低耦合 前言 可以看出&#xff1a; Redis 的 Hash 是一个键&#xff08;key&#xff09;下包含多个字段&#xff08;field…

可解释机器学习 | Python实现LGBM-SHAP可解释机器学习

机器学习 | Python实现GBDT梯度提升树模型设计 目录 机器学习 | Python实现GBDT梯度提升树模型设计基本介绍模型使用参考资料基本介绍 LightGBM(Light Gradient Boosting Machine)是一种基于决策树的梯度提升框架,是一种高效的机器学习模型。SHAP(SHapley Additive exPlan…

mysql--二进制安装编译安装yum安装

二进制安装 创建用户和组 [rootlocalhost ~]# groupadd -r -g 306 mysql [rootlocalhost ~]# useradd -r -g 306 -u 306 -d /data/mysql mysql 创建文件夹并添加所属文件用户和组 [rootlocalhost ~]# mkdir -p /data/mysql [rootlocalhost ~]# chown mysql:mysql /data/mysql …

大模型开发和微调工具Llama-Factory-->WebUI

WebUI LLaMA-Factory 支持通过 WebUI 零代码微调大模型。 通过如下指令进入 WebUI llamafactory-cli webui# 如果是国内&#xff0c; # USE_MODELSCOPE_HUB 设为 1&#xff0c;表示模型从 ModelScope 魔搭社区下载。 # 避免从 HuggingFace 下载模型导致网速不畅 USE_MODELSC…

【WPS】【EXCEL】将单元格中字符按照分隔符拆分按行填充到其他单元格

问题&#xff1a;实现如下图的效果 解答&#xff1a; 一、函数 IFERROR(TRIM(MID(SUBSTITUTE($A$2,",",REPT(" ",LEN($A$2))),(ROW(A1)-1)*LEN($A$2)1,LEN($A$2))),"") 二、在单元格C2中填写如下函数 三、全选要填充的单元格并且按CTRLD 函数…

Java有关数组的相关问题

Java中的栈和堆的含义 栈 存储局部变量&#xff1a;栈主要用于存储方法中的局部变量&#xff0c;包括基本数据类型&#xff08;int、double、boolean等&#xff09;和对象的引用&#xff08;不包含对象本身&#xff09;。 遵循后进先出原则&#xff1a;当一个方法被调用时&…