常用排序算法总结(直接插入排序、选择排序、冒泡排序、堆排序、快速排序、希尔排序、归并排序)

目录

一. 直接插入排序

二:选择排序

三:冒泡排序

四.堆排序

五:希尔排序

六:快速排序(递归与非递归)

七.归并排序(递归与非递归)


一. 直接插入排序

🌟排序思路

        直接插入排序的基本原理是将一条记录插入到已排好的有序表中,从而得到一个新的、记录数量增1的有序表,其思路就和我们摸扑克牌一样,每摸到一张牌按照大小把他插入到对应位置,这样等摸完全部的牌时,我们手里的牌就是有序的

动态图解:

💬特点

🚩时间复杂度:

        O(N^2)(若待排序表为有序的则时间复杂度为O(N))

🚩空间复杂度:

        空间复杂度为O(1)

🚩稳定性: 

        稳定

⚡代码演示: 

void InsertSort(int* a, int n)
{for (int i = 0; i < n - 1; i++){int end = i;//首先保存一下待插入的元素int tmp = a[end + 1];//比较的最坏情况时end=0while (end >= 0){//若待排序元素比a[end]小,则让a[end]向后移动腾位置if (tmp < a[end]){a[end + 1] = a[end];end--;}else{//由于本来数组就为有序数组,当不满足上述条件时,说明找到了待插入的位置break;}}//插入数据a[end + 1] = tmp;}
}

二:选择排序

🌟排序思路

 每次选出数组的最小值和最大值,分别置于数组头尾处,缩小排序范围重复操作

动态图解:

💬特点

🚩时间复杂度:

        时间复杂度为:O(N^2)

🚩空间复杂度:

        空间复杂度为O(1)

🚩稳定性:

        不稳定

⚡代码演示: 

void SelectSort(int* a, int n)
{int begin = 0, end = n - 1;while (begin < end){int mini = begin, maxi = begin;for (int i = begin + 1; i <= end; ++i){if (a[i] < a[mini]){mini = i;}if (a[i] > a[maxi]){maxi = i;}}Swap(&a[begin], &a[mini]);if (maxi == begin){maxi = mini;}Swap(&a[end], &a[maxi]);++begin;--end;}
}

三:冒泡排序

🌟排序思路

比较相邻元素的大小,将大的元素往后交换,每一轮比较能确定一个最大值的位置

动态图解:

💬特点

🚩时间复杂度:

        时间复杂度为:O(N^2)

🚩空间复杂度:

        空间复杂度为O(1)

🚩稳定性:

        稳定

 ⚡代码演示: 

/* 冒泡排序 */
void BubbleSort(int arr[], int length) 
{for (int i = 0; i < length; i++){for (int j = 0; j < length -  i - 1; j++){if (arr[j] > arr[j + 1]){int temp;temp = arr[j + 1];arr[j + 1] = arr[j];arr[j] = temp;}}}
}

四.堆排序

🌟排序思路

1.首先将待排序数组建为大堆,此时堆顶元素就为数组最大值了

2.交换堆顶和堆尾元素,此时最大元素就到了堆尾,目前数组最大元素就排好了,现在就假设堆里没有当前这个最大元素了,堆头下面的左右子树仍然是大堆,只需要再将堆顶元素向下调整到合适位置,剩下的n-1个元素还是大堆

3.堆头堆尾交换,向下调整,如此反复就可排序

ps.排序以升序为例,升序建大堆,降序建小堆      

💬特点

🚩时间复杂度:

       O(N*lgN)

🚩空间复杂度:

        O(1)

🚩稳定性:

        不稳定

⚡代码演示: 

#include<stdio.h>
typedef int HeapDataType;void swap(HeapDataType* a, HeapDataType* b)
{int tmp = *a;*a = *b;*b = tmp;
}
//大堆
void AdjustUp(HeapDataType* a, int child)
{int parent = (child - 1) / 2;while (child>0){if (a[parent] < a[child]){swap(&a[parent], &a[child]);child = parent;parent = (parent - 1) / 2;}else{break;}}
}
//大堆
void AdjustDown(HeapDataType* a, int size, int parent)
{int child = parent * 2 + 1;while (child<size){//找较大的孩子if (a[child + 1] > a[child] && child+1<size){child = child + 1;}if (a[parent] < a[child]){swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}void HeapSort(HeapDataType* a, int size)
{//建堆for (int i = 1; i < size; i++){AdjustUp(a, i);}//排序:升序int end = size - 1;while (end>0){swap(&a[0], &a[end]);AdjustDown(a, end, 0);  //end指最后一个元素,同时end的值为前面元素的个数end--;}
}

 五:希尔排序

🌟排序思路

  1. 预排序:先将数组分组,将每个组排序,目的是让整个数组相对有序
  2. 插入排序:待数组相对有序后,进行直接插入排序,这样效率比较高

图解:

假设待排序数组为[9 8 7 6 5 4 3 2 1]

1.我们将他们每隔三个坐标分为一组,假设gap=3

2.对三个数组分别进行直接插入排序,使数组整体相对有序

3.对数组整体进行插入排序

 💬特点

🚩时间复杂度:

       O(N^1.3)

🚩空间复杂度:

        O(1)

🚩稳定性:

        不稳定

 ⚡代码演示: 

void ShellSort(int* a, int n)
{//首先将gap定为nint gap = n;/*while (gap > 1){gap = gap / 3 + 1;for (int j = 0; j < gap; j++){for (int i = j; i < n - gap; i += gap){int end = i;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;}}}*/while (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 (tmp < a[end]){a[end + gap] = a[end];end -= gap;}else{break;}}a[end + gap] = tmp;}}
}

六:快速排序(递归与非递归)

1.⚡hoare

🌟排序思路

        将数组第一个元素定位关键值,定义begin和end指针,先让end从后往前找到比关键值小的数,begin从前往后找比关键值大的数,然后交换两数,直到 begin==end,再让关键值和begin所指的元素交换,最后返回关键值所在位置,便于后续进行递归或非递归操作

动态图解:

⚡代码演示: 

void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}//hoare
int PartSort1(int* a, int begin, int end)
{int left = begin, right = end;int keyi = begin;while (left < right){//右边找小while (left < right && a[right] >= a[keyi]){right--;}//左边找大while (left < right && a[left] < a[keyi]){left++;}swap(&a[left], &a[right]);}swap(&a[left], &a[keyi]);return left;
}

 2.⚡挖坑法

🌟排序思路

         首先将关键值定为数组第一个元素,并将坑位定为begin,先让end从后往前找到比关键值小的数,将这个数放到坑位,并更新坑位,再让begin从前往后找比关键值大的数,将这个数放到坑位,并更新坑位,直到 begin==end,再让关键值和坑位的元素交换,最后返回关键值所在位置

动态图解:

⚡代码演示: 

//挖坑法
int PartSort2(int* a, int begin, int end)
{int mid = GetMid(a, begin, end);swap(&a[begin], &a[mid]);int key = a[begin];int hole = begin;while (begin < end){//右边找小,填入坑内,更新坑位while (begin<end && a[end]>=key){--end;}a[hole] = a[end];hole = end;//左边找大,填入坑内,更新坑位while (begin<end && a[begin]<=key){++begin;}a[hole] = a[begin];hole = begin;}a[hole] = key;return hole;
}

 3.⚡双指针法

🌟排序思路

        将数组第一个元素定为关键值,定义两个指针prev和cur,先让prev指向数组的第一个元素,cur指向prev的下一个元素,cur的作用是找比关键值小的元素,若cur所指元素不小于关键值则cur++,直到cur所值元素小于关键值,此时,prev和cur之间的元素都是大于关键值的元素,若prev+1不是cur的话就可以让prev++所指元素与cur所指元素交换了,直到cur指向数组的最后一个元素

动态图解:


 

⚡代码演示:

//双指针法
int PartSort3(int* a, int begin, int end)
{int mid = GetMid(a, begin, end);swap(&a[begin], &a[mid]);int key = begin;int prev = begin;int cur = prev + 1;while (cur <= end){if (a[cur] < a[key] && ++prev != cur){swap(&a[prev], &a[cur]);}cur++;}swap(&a[prev], &a[key]);return prev;
}

💬特点

🚩时间复杂度:

最好情况:O(n*lgn)

最坏情况:O(n^2)

🚩空间复杂度

O(lgn)

🚩稳定性

不稳定

 🚩快速排序递归实现

⛲小优化:

        上述三个方法都是快速排序的单趟排序,但是上述排序还有一个小缺陷,因为三个方法都是固定第一个元素为关键值的,如果数组为有序的,那么从后往前找小就要遍历整个数组,效率会很小,所以通常会再写一个找中间值的函数:在数组开头结尾和中间三个数中找出一个大小在中间的数,并让这个数和数组第一个数交换,这样就会减少上述情况的发生

int GetMid(int* a, int begin, int end)
{int mid = (begin + end) / 2;if (a[begin] > a[mid]){if (a[mid] > a[end])return mid;else if (a[end] > a[begin])return end;elsereturn begin;}else{if (a[begin] > a[end])return begin;else if (a[end] > a[mid])return mid;elsereturn end;}
}void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}//hoare
int PartSort1(int* a, int begin, int end)
{int mid = GetMid(a, begin, end);swap(&a[begin], &a[mid]);int left = begin, right = end;int keyi = begin;while (left < right){//右边找小while (left < right && a[right] >= a[keyi]){right--;}//左边找大while (left < right && a[left] < a[keyi]){left++;}swap(&a[left], &a[right]);}swap(&a[left], &a[keyi]);return left;
}//挖坑法
int PartSort2(int* a, int begin, int end)
{int mid = GetMid(a, begin, end);swap(&a[begin], &a[mid]);int key = a[begin];int hole = begin;while (begin < end){//右边找小,填入坑内,更新坑位while (begin<end && a[end]>=key){--end;}a[hole] = a[end];hole = end;//左边找大,填入坑内,更新坑位while (begin<end && a[begin]<=key){++begin;}a[hole] = a[begin];hole = begin;}a[hole] = key;return hole;
}
//双指针法
int PartSort3(int* a, int begin, int end)
{int mid = GetMid(a, begin, end);swap(&a[begin], &a[mid]);int key = begin;int prev = begin;int cur = prev + 1;while (cur <= end){if (a[cur] < a[key] && ++prev != cur){swap(&a[prev], &a[cur]);}cur++;}swap(&a[prev], &a[key]);return prev;
}
void QuickSort(int* a, int begin,int end)
{if (begin >= end)return;//三种方法任选其一即可int keyi = PartSort3(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

 🚩快速排序非递归实现

🌟实现思路:

1.创建一个栈,将数组的右边界下标和左边界下标依次入栈

2.循环弹出数组的左右边界下标,并对该区间进行单趟排序,确定关键值的下标,分为左右两个区间

3.若左区间元素个数大于一个,将左区间右边界下标和左边界下标依次入栈,右区间同理

4.重复操作步骤2 3直到栈为空

⚡代码演示: 

void QuickSortNonR(int* a, int begin, int end)
{ST s;STInit(&s);STPush(&s,end);STPush(&s,begin);while (!STEmpty(&s)){int left = STTop(&s);STPop(&s);int right = STTop(&s);STPop(&s);int key = PartSort1(a, left, right);if (left < key - 1){STPush(&s, key - 1);STPush(&s, left);}if (right > key + 1){STPush(&s, right);STPush(&s, key+1);}}STDestroy(&s);
}

 七.归并排序(递归与非递归)

一:⛲递归实现

🌟算法思路

归并排序是用分治思想,分治模式在每一层递归上有三个步骤:

分解(Divide): 将n个元素分成个含n/2个元素的子序列。
解决(Conquer):用合并排序法对两个子序列递归的排序。
合并(Combine):合并两个已排序的子序列已得到排序结果。
        该算法需要先将数组分解,直到每个子序列为一个元素,再将子序列两两合并排序,思路可以参考二叉树的后序递归

 💬特点

平均时间复杂度:O(nlogn)
最佳时间复杂度:O(n)
最差时间复杂度:O(nlogn)   
空间复杂度:O(n)

动图展示:

⚡代码演示: 

void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin >= end)return;//分解int mid = (begin + end) / 2;_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid+1, end, tmp);// 归并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, sizeof(int) * (end - begin + 1));
}void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}

二:⛲非递归实现

🌟算法思路:

        归并排序的递归实现是先将数组分解,直到每个子序列只有一个元素,在将子序列两两归并,非递归的实现思路则没有了分解的过程,直接将数组元素一个与一个归并,在两个与两个归并,在四个与四个归并.......,直到最后两组归并,数组变为有序数组

⚡代码演示:

void MergeSortNonR(int* a, int begin, int end)
{int n = end - begin + 1;int* tmp = (int*)malloc(sizeof(int) * (n));if (tmp == NULL){perror("malloc");return;}int gap = 1;//当gap小于n时两个组才都有元素,才需要归并while (gap < n){int j = 0;for (size_t i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;//边界管理if (end1 >= n||begin2>=n){break;}if (end2 >= n){end2 = n - 1;}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);
}

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

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

相关文章

【白皮书下载】GPU计算在汽车中的应用

驾驶舱域控制器 (CDC) 是汽车 GPU 的传统应用领域。在这里&#xff0c;它可以驱动仪表板上的图形&#xff0c;与车辆保持高度响应和直观的用户界面&#xff0c;甚至为乘客提供游戏体验。随着车辆屏幕数量的增加和分辨率的提高&#xff0c;对汽车 GPU 在 CDC 中进行图形处理的需…

Spark On Hive配置测试及分布式SQL ThriftServer配置

文章目录 Spark On Hive的原理及配置配置步骤在代码中集成Spark On Hive Spark分布式SQL执行原理及配置配置步骤在代码中集成Spark JDBC ThriftServer 总结 Spark On Hive的原理及配置 Spark本身是一个执行引擎&#xff0c;而没有管理metadate的能力&#xff0c;当我们在执行S…

Jenkins环境配置篇-更换插件源

作为持续集成的利器 Jenkins 已经得到了广泛地应用&#xff0c;仅仅作为一个工具&#xff0c;Jenkins 已然有了 自己的生态圈&#xff0c;支持其的 plugin 更是超过 1300。在实际中如何使用以及如何更好地使用 jenkins&#xff0c;一直是大家在实践并讨论的。本系列文章将会从如…

Beego之Beego快速入门

1、beego快速入门 1.1 新建项目 新建一个项目&#xff1a; [rootzsx src]# bee new quickstart 2023/02/19 15:55:50.370 [D] init global config instance failed. If you do not use this, just ignore it. open conf/app.conf: no such file or directory 2023/02/19 1…

接口自动化框架搭建-写在前面

从今天开始&#xff0c;我将带领大家一起学习接口自动化框架的搭建&#xff0c;在学习之前&#xff0c;我们先了解搭建一个接口自动化框架需要具备哪些知识&#xff0c;应该做哪些准备工作 测试开发工程师的入门条件 近几年比较流行测试开发岗位&#xff0c;很多小伙伴都不知…

C++:史上最坑小游戏

史上最坑小游戏&#xff0c;先别使用&#xff0c;往后看&#xff01; #include<bits/stdc.h> #include <windows.h> using namespace std; int panduolamohe ; int main(){cout<<"玩到无语的小游戏&#xff01;&#xff01;&#xff01;";Sleep(5…

将Matlab图窗中的可视化保存为背景透明的矢量图

将matlab绘制的结果复制为矢量图时&#xff0c;去除背景的操作如下&#xff1a; 先打开/绘制图形窗口(不要关闭)在命令行终端输入axis off关闭坐标系继续在命令行终端分别输入&#xff1a; ax gca; copygraphics(ax,ContentType,vector,BackgroundColor,none); 此时&#xff…

消息队列介绍

什么是 MQ MQ(message queue)&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是 message 而已&#xff0c;还是一种跨进程的通信机制&#xff0c;用于上下游传递消息。在互联网架构中&#xff0c;MQ 是一种非常常 见的上下游“逻辑解耦…

Unity -简单键鼠事件

“Test_03” KeyTest 键鼠事件每帧都要监听&#xff0c;要放在Update()中处理 public class KeyTest : MonoBehaviour {// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){// 【鼠标点击事件】 0左键、1右键…

【PGSQL】date_trunc 函数

date_trunc 函数用于在 PostgreSQL 中将日期或时间戳值截断&#xff08;向下取整&#xff09;到指定的精度级别。当您想要忽略较小的时间单位&#xff08;例如&#xff0c;小时、分钟、秒&#xff09;&#xff0c;专注于较大的单位&#xff08;例如&#xff0c;天、月、年&…

SpringBoot 项目中后端实现跨域的5种方式!!!

文章目录 SpringBoot 项目中后端实现跨域的5种方式&#xff01;&#xff01;&#xff01;一、为什么会出现跨域问题二、什么是跨域三、非同源限制四、Java后端 实现 CORS 跨域请求的方式1、返回新的 CorsFilter(全局跨域)2、重写 WebMvcConfigurer(全局跨域)3、使用注解 (局部跨…

实战纪实 | 某配送平台zabbix 未授权访问 + 弱口令

本文由掌控安全学院 - 17828147368 投稿 找到一个某src的子站&#xff0c;通过信息收集插件wappalyzer&#xff0c;发现ZABBIX-监控系统&#xff1a; 使用谷歌搜索历史漏洞&#xff1a;zabbix漏洞 通过目录扫描扫描到后台&#xff0c;谷歌搜索一下有没有默认弱口令 成功进去了…

Failed resolution of: Lorg/apache/hc/core5/http/message/BasicNameValuePair;

这是我引用的库文件 implementation (“com.esri.arcgisruntime:arcgis-android:100.15.3”) 使用ArcGIS绘制天地图报错如下&#xff1a; Caused by: java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/hc/core5/http/message/BasicNameValuePair; 查询资料得…

Python——enumerate与zip结合使用

对于同时需要遍历两个及以上的列表的情况下&#xff0c;需要enumerate与zip结合使用。 一、例子 for i, (image, feature) in enumerate(zip(images, features)):.........

LeetCode:206. 反转链表

力扣链接 算法思想&#xff1a;由于单链表是单向的&#xff0c;想要对当前元素进行操作&#xff0c;需找到前一个元素。本题利用双指针&#xff0c;初始pre指针指向NULL&#xff0c;cur指针指向head.再对局部翻转之前&#xff0c;先把下一个结点存到temp指针中。当进行完如下代…

构建中国人自己的私人GPT

创作不易&#xff0c;请大家多鼓励支持。 在现实生活中&#xff0c;很多人的资料是不愿意公布在互联网上的&#xff0c;但是我们又要使用人工智能的能力帮我们处理文件、做决策、执行命令那怎么办呢&#xff1f;于是我们构建自己或公司的私人GPT变得非常重要。 先看效果 他的…

YOLOv8全网首发:新一代高效可形变卷积DCNv4如何做二次创新?高效结合SPPF

💡💡💡本文独家改进:DCNv4更快收敛、更高速度、更高性能,与YOLOv8 SPPF高效结合 收录 YOLOv8原创自研 https://blog.csdn.net/m0_63774211/category_12511737.html?spm=1001.2014.3001.5482 💡💡💡全网独家首发创新(原创),适合paper !!! 💡💡💡…

视频水平翻转,开启创意之旅

想象一下&#xff0c;一段普通的视频在瞬间被水平翻转&#xff0c;画面中的一切仿佛被镜子所反射&#xff0c;给人一种惊艳而又奇妙的感受。在这个充满无限可能的数字时代&#xff0c;我们不应被传统的视觉效果所束缚&#xff0c;尝试运用水平翻转技术&#xff0c;为你的作品注…

Linux /etc/issue 内的各代码意义

\d 本地端时间的日期&#xff1b; \l 显示第几个终端机接口&#xff1b;&#xff08;小写的L&#xff09; \m 显示硬件的等级 &#xff08;i386/i486/i586/i686…&#xff09;&#xff1b; \n 显示主机的网络名称&#xff1b; \O 显示 domain name&#xff1b; \r 操作系统…

安装Office Web Apps 2013

Office Web Apps 可以让用户通过浏览器就能操作Office文件&#xff08;Word,PowerPoint,Excel&#xff09;&#xff0c;客户端可以不安装Office&#xff0c;而且不用担心2003和2007文档格式的问题。下面入正题。 一、安装环境 因为Office Web Apps要求安装环境中不能用其它服…