当前位置: 首页 > news >正文

从入门到精通数据结构----四大排序(上)

目录

首言:

 1. 插入排序

  1.1 直接插入排序

  1.2 希尔排序

2. 选择排序 

 2.1 直接选择排序  

  2.2 堆排序

3. 交换排序

  3.1 冒泡排序

  3.2 快排 

 结尾:

首言:

    本篇文章主要介绍常见的四大排序:交换排序、选择排序、插入排序、归并排序。上主要介绍前三种。由常见的时间复杂度较大的,再到复杂到较小的比较难的排序。由浅入深,层层递进,实现对排序的深刻理解.

 1. 插入排序

  1.1 直接插入排序

      实现简单的插入排序,需要我们首先进行单趟的排序。其基本思想为:故名思意就是将尾部最后面位置的数插入到比他大的数的前面,并将大的数据向后移动。方法为:找到尾部的值定义为 end + 1,前面的值为 end , 然后进行判断的比较。

     单趟排序实现的代码为将 end 赋值为 n - 2,之后先前进行比较。

void Part_InitSort(int* a, int n)//a 表示一个数组,n 表示这个数组的大小
{//进行单趟的排序,找到末尾位置int end = n - 2;//使它指向数组的倒数第二个int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];end--;}else{break;}}//当遇到小于 tmp 的值的时候跳出循环,并且将 end + 1 的位置放上 tmp。a[end + 1] = tmp;
}

    之后将单趟排序放入到整个的排序之中去,从第一个位置开始(先进行比较前面的两个数字),记录它的下标放到 tmp 里面如果是 tmp 小的话就进行移动。直到这个 n 表示的后面的倒数第二个的时候,实现全部的排序。进行替换的时候循环结束的条件为到 end 减到 0 为止。

void InitSort(int* a, int n)
{//利用单趟插入的排序的想法,进行一个循环的排序int i = 0;for (i = 0; i < n - 1; i++){//从前两个位置开始int end = i;int tmp = a[end + 1];while (end >= 0){if (tmp < a[end]){a[end + 1] = a[end];end--;}else{break;}}a[end + 1] = tmp;}
}

  1.2 希尔排序

    由于直接插入排序的时间复杂度非常的高。对其进行优化,现在给出这样一个变量 grap 进行划分,不在一个一个的比较,直接比较相差 grap 个数之后的数进行比较(进行预排),使用预排序可以实现使整个数组,实现接近有序,从而大大减少直接插入排序的次数。如图所示。 

void ShellSort(int* a, int n)
{//首先我们先不考虑 grap 的大小,假设为 3, 就是相当于将前面的位置的 1 换成了 grapint grap = 3;for (int i = 0; i < n - grap; i++){int tmp = a[i + grap];int end = i;while (end >= 0){if (tmp < a[end]){a[end + grap] = a[end];end -= grap;}else{break;}}a[end + grap] = tmp;}//接下来就是对于 garp 的范围进行确定。 
}

    经过大量的实践得到了:当每次 grap = grap / 3 (grap = n);进行的预排序时间复杂度是最好的。

    进行 grap 的选择的时候具有:如果 grap 很大但是跳的很快,但是越不接近有序的特点。所以当不断缩小空间的时候,由于前面的铺垫,会让后面范围小的插入排序进行次数减少。但是希尔排序也具有不稳定的特点。 

    希尔排序进行结束的条件是grap < 1,因为最后一次执行的就是直接插入排序,但是因为接近有序了,所以需要的次数非常的少。

void ShellSort(int* a, int n)
{//进行一次的希尔排序,并不是已经排序好的,是需要不断地去缩小空间,实现排序//int grap = n;while (grap > 1){grap = (grap / 3) + 1;// + 1 是为了保证最后一次始终有 1 for (int i = 0; i < n - grap; i++){int tmp = a[i + grap];int end = i;while (end >= 0){if (tmp < a[end]){a[end + grap] = a[end];end -= grap;}else{break;}}a[end + grap] = tmp;}}
}

2. 选择排序 

 2.1 直接选择排序  

     基本思想:定义一个 tmp(用来进行遍历),每次都从第一个位置开始,到 end 结束,如果是a[tmp]大于定义的最大的内个,就将 tmp 赋值给最大的那个下标(小的也一样)。找到最大的最小的那个之后,进行交换,交换的时候最小的交换到第一个位置,最小的交换到最后位置。随后进行 end--,begin++。在进行下一步操作。

     注意选择排序的交换的是带有下标的数组元素,不是直接的替换(直接替换的话,会造成数据的串改)。下面这个串代码包括了选择排序与交换函数。

void SelectSort(int* a, int n)
{assert(a);int begin = 0, end = n - 1, i = 0;for (i = begin; i <= end; i++){//需要寻找的是下标int mini = begin, maxi = end;int tmp = begin;for (tmp = begin + 1; tmp <= end; tmp++){if (a[tmp] > a[maxi]){maxi = tmp;}if (a[tmp] < a[mini]){mini = tmp;}}//在同时进行寻找 最大 与 最小 的时候可能会有最大值在第一个位置的情况.//最小值在开始位置时,这不影响。但是最大值在一开始位置就影响。Swap(&a[begin], &a[mini]);//交换了之后,mini 位置 存放的是最大值if (begin == maxi){maxi = mini;}Swap(&a[maxi], &a[end]);end--;begin++;}
}
void Swap(int* a, int* b)
{int* tmp = a;*a = *tmp;*b = *tmp;
}

  2.2 堆排序

    堆排序就是通过建立堆,来进行排序。升序建立建大堆,降序建小堆。 建堆的话需要进行向下调整算法实现大(小)堆。

   1.向下调整算法(以建立大堆为例):要求左右子树都是小堆,才可以使用向下调整算法。原理是:通过父亲结点跟左右孩子节点进行比较,如果是孩子比父亲大,就需要进行交换,接着向下走。直到父亲结点比所有的孩子都大的时候就停止。

    2.细节:循环结束的条件是,child == n,因为识别的是数组的下标所有的大小都是 n - 1。其次,跟新完父亲结点之后的孩子结点也是需要更新的最后的退出条件为当父亲节点比所有的孩子结点都要大的时候,就跳出循环,表示大堆已经建立。

void AdjustDown(int* a, int n, int parent)
{int Child = parent * 2 + 1;//找到左孩子.//当孩子不小于n就停止while (Child < n){if (Child + 1 < n && a[Child] < a[Child + 1])//找到孩子里面最大的一个{Child += 1;}if (a[parent] < a[Child]){Swap(&a[parent], &a[Child]);parent = Child;Child = parent * 2 + 1;}else//因为下面的就都是符合条件的,不需要在进行向下调整.{break;}}
}

2.通过这个算法,在底部从下往上开始进行排序,实现大堆的建立。建立完大堆之后将第一个位置的值放到最后一个位置。排序 n - 1个数。

注意事项:1.建立大堆是需要从最后一个父亲结点,向前进行向下调整算法。2. 注意 i 的范围是从 (n - 1 - 1)/ 2开始的 . 3. 下一次进来的时候,是去排序 end  个数字。

//堆排序的时间复杂度为 n * log(n)
void HeapSort(int* a, int n)
{//堆排序,建立升序用大堆。所以需要一个向下调整函数。//1.首先先建立一个大堆.int i = 0;for (i = (n - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, n, i);}//2.将第一个位置的最大值,放到堆的最后int end = n - 1;while (end >= 0){Swap(&a[0], &a[end]);AdjustDown(a, end, 0);end--;}	
}

3. 交换排序

  3.1 冒泡排序

    冒泡排序是最简单的交换排序,从第一个位置开始向后进行一次交换,直到找到了不符合条件的那个数字,就停止。

    注意实现:i 表示的是进行多少次的交换,主要看的是的 j ,每次都是从 0,第一个位置开始进行计算,比较的是两个数字,所以是 n - 1,但是每次当放到最后的一个位置的时候,会少一个数字,所以每次都要- i;

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

  3.2 快排 

     快速排序是经常会考到,他的主要做法为定义 begin 和 end(倒数第二个数字),最后一个数字 key,begin 去找到比 key 大的数然后停下来,end 去找比 key 小的数然后停下来。之后再进行交换比 key 大的值,与比 key 小的值。当 begin = end 的时候将 key 放到这个位置。

1. 首先先实现单趟的排序。

//函数的left 与 right 区间为;[left, right]
int PartSort1(int* a, int left, int right)
{//选择一个 key,我选择的是最后的位置,所以就需要让 left 进行先走int key = a[right];while (left < right){//左边 去寻找最大的while (left < right && a[left] <= key){left++;}//右边 去寻找最小的while (left < right && a[right] >= key){right--;}//每次进行寻找,找到了 left 指向了较大的, right 指向较小的。就进行交换Swap(&a[left], &a[right]);}//结束循环时表示 left 与 left 相遇到了一起。随便与 k 进行交换可。Swap(&a[key], &a[right]);return left;//叫替换了的位置返回,方便后续进行拆分
}

2. 通过单趟排序,实现整体的快速排序。主要思想是:通过递归让左右都实现有序,不断的向下分直到不能分为止,也就是只有一个数,left 跟 right 都是在一起的时候,表明这个一段数组是有序的。 

void QuickSort(int* a, int left, int right)
{//每次都进行循环,结束条件为左指针遇上了右指针.if (left >= right){return;}//每次递归都划分为两个区域,left - div - 1,div + 1 - right;int div = PartSort2(a, left, right);//进行左边的排序QuickSort(a, left, div - 1);QuickSort(a, div + 1, right);
}

3. 细节:(1) 选定后面的值一定是从最前面的开始进行计算。因为前面去找的是最大的值,需要将最大的值放到后面,如果是右边先走的话,就不是将最小的值放到后面,因此 div 的右边就不是都大于 div 的。 

 (2) 当最后一次进行交换的时候会将 key 位置放到想要的位置,它的前面都是小于 key 的,后面都是大于 key 的。

(3) 其次当对于一个有序的数组直接的进行排序的话复杂度非常的高为:O(n2),所以需要进行取中间值,避免最后取到 key 的是极值的情况。这个取中间值的方法只要是运用在了进行部分的快速排序算法当中去。让 key 位置是保证为中间值,不是极值。

int MidInit(int* a, int begin, int end)
{int mid = (begin + end )/2;if (a[begin] > a[mid]){if (a[begin] < a[end]){return begin;}else if (a[mid] > a[end]){return mid;}else{return end;}}else //这种情况表示的是 a[begin] < a[mid]{if (a[begin] > a[end]){return begin;}else if (a[mid] > a[end]){return mid;}else{return end;}}
}

(4) 快速排序,还有另外的两种方法:挖坑法前后指针法,以及再进行优化。这个一节我放到下一篇文章中进行讲解。

 结尾:

     如果对你有帮助还请帮我点个免费的赞,支持一下我,我也会不断地改进争取写出跟高质量的文章。我们共同努力!!!!😊😊😊

相关文章:

从入门到精通数据结构----四大排序(上)

目录 首言&#xff1a; 1. 插入排序 1.1 直接插入排序 1.2 希尔排序 2. 选择排序 2.1 直接选择排序 2.2 堆排序 3. 交换排序 3.1 冒泡排序 3.2 快排 结尾&#xff1a; 首言&#xff1a; 本篇文章主要介绍常见的四大排序&#xff1a;交换排序、选择排序、插入排序、归并排…...

【bug】使用transformers训练二分类任务时,训练损失异常大

使用transformers训练二分类任务时&#xff0c;训练损失异常大 问题分析 问题 training_loss异常大&#xff0c;在二分类损失中&#xff0c;收敛在1~2附近&#xff0c;而eval_loss却正常&#xff08;小于0.5&#xff09; 分析 参考&#xff1a; Bug in gradient accumulation…...

文献阅读与笔记整理技巧

文献阅读 1.原因 &#xff08;1&#xff09;了解背景知识&#xff08;硕博学位论文&#xff0c;大牛文献综述&#xff09; &#xff08;2&#xff09;把握研究方向&#xff08;行业最新论文&#xff0c;大牛文献综述&#xff09; &#xff08;3&#xff09;学习设计思路&am…...

Python Flask中集成SQLAlchemy和Flask-Login

在现代Web应用开发中,数据库和用户认证是两个非常重要的功能。Flask作为一个轻量级的Python Web框架,本身只提供了最基本的Web功能。但是,它可以通过集成各种优秀的扩展库来增强功能。本文将介绍如何在Flask应用中集成SQLAlchemy(数据库)和Flask-Login(用户认证),并提供一个完整…...

esp32 JTAG 串口 bootload升级

文章目录 一、前言二、了解 JTAG 和 Ymodem 的工作原理2.1 环境准备2.2 Ymodem 协议工作原理2.3 固件分区准备 三、关键升级函数五、使用shell 测试 一、前言 如果使用 JTAG 串口 结合 Ymodem 协议 实现 ESP32 的固件升级&#xff0c;整体逻辑将围绕通过串口传输固件文件并将其…...

【linux】(17)压缩和解压

tar tar 是一个用于创建、维护、修改和解压缩存档文件的 Linux 命令。tar 常常用于备份文件或者将多个文件打包成一个文件以便于传输或存储。以下是 tar 命令的详细教程&#xff0c;包括常用选项和示例&#xff1a; 基本语法 tar [选项] [文件或目录]常用选项 -c&#xff1…...

摄像机视频分析软件下载LiteAIServer视频智能分析平台玩手机打电话检测算法技术的实现

随着科技的不断进步&#xff0c;摄像机视频分析软件的发展已经为我们的生活带来了许多便捷。其中&#xff0c;LiteAIServer视频智能分析平台的玩手机打电话检测算法技术尤为突出&#xff0c;它利用先进的图像处理和人工智能技术&#xff0c;能够自动识别并监控视频中的玩手机或…...

springboot购物推荐网站的设计与实现(代码+数据库+LW)

摘要 随着信息互联网购物的飞速发展&#xff0c;一般企业都去创建属于自己的电商平台以及购物管理系统。本文介绍了东大每日推购物推荐网站的开发全过程。通过分析企业对于东大每日推购物推荐网站的需求&#xff0c;创建了一个计算机管理东大每日推购物推荐网站的方案。文章介…...

【Unity3D插件】Unity3D HDRP Outline高亮发光轮廓描边插件教程

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享QQ群&#xff1a;398291828小红书小破站 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 最近用Unity3D的HDRP&#xff08;高清渲染管…...

QT基础 UI编辑器 QT5.12.3环境 C++环境

一、UI编辑器 注意&#xff1a;创建工程时&#xff0c;要勾上界面按钮 UI设计师界面的模块 UI编辑器会在项目构建目录中自动生成一个ui_xxx.h&#xff08;构建一次才能生成代码&#xff09;&#xff0c;来表示ui编辑器界面的代码&#xff0c;属于自动生成的&#xff0c;一定不…...

计算机网络socket编程(5)_TCP网络编程实现echo_server

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 计算机网络socket编程(5)_TCP网络编程实现echo_server 收录于专栏【计算机网络】 本专栏旨在分享学习计算机网络的一点学习笔记&#xff0c;欢迎大家在评论区交…...

go语言闭包捕获的是变量的引用而不是变量的值

在 Go 语言中&#xff0c;闭包捕获的是变量的引用&#xff0c;而不是变量的值。这意味着闭包会引用循环变量或外部变量的实际内存位置&#xff0c;而不是在闭包创建时复制变量的值。这种行为有时会导致意外的结果&#xff0c;尤其是在循环中创建多个闭包时。 闭包捕获变量的引…...

周期法频率计的设计

目录 周期法频率计 分析&#xff1a; 设计过程&#xff1a; 周期法频率计 对于低频信号&#xff0c;应用周期法进行测频。周期法测频的基本原理是&#xff1a;应用标准频率信号统计被测信号两个相邻脉冲之间的脉冲数&#xff0c;然后通过脉冲数计算出被测信号的周期&#xff…...

【Linux】drop cache与reclaim的区别

前言 在 Linux 内核中&#xff0c;drop cache和reclaim是两种不同的内存管理机制&#xff0c;它们的目的和实现方式有所不同。 Drop Cache 定义 drop cache 是一种手动操作&#xff0c;允许用户通过向 /proc/sys/vm/drop_caches 写入特定的值&#xff0c;直接清除系统中的缓…...

【Linux课程学习】:命令行参数,环境变量

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux课程学习 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 命令行参数&#xff1a; 用命令行参数实现不同…...

HTB:WifineticTwo[WriteUP]

目录 连接至HTB服务器并启动靶机 信息搜集 使用rustscan对靶机TCP端口进行开放扫描 使用nmap对靶机开放端口进行脚本、服务扫描 使用curl访问靶机8080端口 使用浏览器直接访问/login路径 漏洞利用 使用searchsploit搜索该WebAPP漏洞 Payload USER_FLAG&#xff1a;bb…...

mac安装Pytest、Allure、brew

安装环境 安装pytest 命令 pip3 install pytest 安装allure 命令&#xff1a;brew install allure 好吧 那我们在安装allure之前 我们先安装brew 安装brew 去了官网复制了命令 还是无法下载 如果你们也和我一样可以用这个方法哦 使用国内的代码仓库来执行brew的安装脚本…...

关于相机选型的一些参数说明

上一篇&#xff1a;关于相机的一些参数计算&#xff08;靶面、视野等&#xff09; 目录 1.卷帘快门和全局快门1.1 卷帘快门1.2 全局快门PS&#xff1a;视觉伺服与快门选择 2.黑白和彩色3.CCD和CMOS3.1 CCD3.2 CMOSCCD VS CMOS 4.面阵和线扫4.1 面阵4.2 线扫4.3 面阵 VS 线扫 5.…...

深入解析 Cron 表达式高级用法:Spring 与 Linux Crontab 的全面对比与实践20241120

深入解析 Cron 表达式高级用法&#xff1a;Spring 与 Linux Crontab 的全面对比与实践 任务调度是后台服务中的重要组成部分&#xff0c;无论是定期数据备份、日志归档还是周期性报表生成&#xff0c;Cron 表达式始终是描述这些任务规则的核心工具。本文将聚焦 Spring Cron 表…...

24软专 数据结构

1、A[n]&#xff0c;k&#xff0c;将数组向右循环移动k位。要求时间复杂度O(n)&#xff0c;空间O(1)。 思路&#xff1a;采用三次反转数组的操作&#xff0c;可以实现时间复杂度为O(n)&#xff0c;空间复杂度为O(1)的算法。 void moveElem(int array[],int k,int length){//a…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

ArcGIS Pro+ArcGIS给你的地图加上北回归线!

今天来看ArcGIS Pro和ArcGIS中如何给制作的中国地图或者其他大范围地图加上北回归线。 我们将在ArcGIS Pro和ArcGIS中一同介绍。 1 ArcGIS Pro中设置北回归线 1、在ArcGIS Pro中初步设置好经纬格网等&#xff0c;设置经线、纬线都以10间隔显示。 2、需要插入背会归线&#xf…...

数据挖掘是什么?数据挖掘技术有哪些?

目录 一、数据挖掘是什么 二、常见的数据挖掘技术 1. 关联规则挖掘 2. 分类算法 3. 聚类分析 4. 回归分析 三、数据挖掘的应用领域 1. 商业领域 2. 医疗领域 3. 金融领域 4. 其他领域 四、数据挖掘面临的挑战和未来趋势 1. 面临的挑战 2. 未来趋势 五、总结 数据…...