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

【数据结构】排序算法篇二

【数据结构】排序算法篇二

  • 1. 快速排序(hoare版本)
    • (1)基本思想:
    • (2)动态图解:
    • (3)代码实现:
    • (4)特性总结:
  • 2. 快速排序(推荐:前后指针版本)
    • (1)基本思想:
    • (2)动态图解:
    • (3)静态图解(一趟):
    • (4)代码实现:
  • 3. 快速排序(挖坑法)
    • (1)具体步骤:
    • (2)静态图解:
    • (3)动态图解:
    • (4)代码实现:
  • 4. 计数排序
    • (1)基本思想:
    • (2)具体步骤:
    • (3)代码实现:
    • (4)特性总结:
  • 5. 归并排序(递归法)
    • (1)基本思想:
    • (2)动态图解:
    • (3)代码实现:
    • (4)辅助理解:
    • (5)特性总结:
  • 6. 归并排序(非递归法1)
    • (1)辅助理解:
  • 7. 归并排序(非递归法2)

1. 快速排序(hoare版本)

(1)基本思想:

任取待排序元素序列中的某元素作为基准值key(把它直接排在它最终要排的那个位置),按照该排序码将待排序集合分割成两子序列左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

(2)动态图解:

在这里插入图片描述

(3)代码实现:

void Swap(int* p, int* q)
{int tmp = *p;*p = *q;*q = tmp;
}
// 三数取中
int GetMidNumi(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[mid] > a[left]){if (a[right] > a[mid])return mid;else if (a[right] < a[left])return left;elsereturn right;}else//(a[mid]<a[left]{if (a[right] > a[left])return left;else if (a[right] < a[mid])return mid;elsereturn right;}
}
void QuickSort1(int* a, int left, int right)
{//最后区间长度为一或零if (left >= right)return;int begin = left, end = right;//保存区间//随机选keyi,防止数组有序导致递归栈溢出//int rani = left + rand() % (right - left);//随机选keyi,此处加上left是因为每次排单趟left即区间的左边界会变//Swap(&a[left], &a[rani]);//三数取中目的,防止数组有序导致递归栈溢出int midi = GetMidNumi(a, left, right);if(midi != left)//中间那个数是自己不需要交换Swap(&a[left], &a[midi]);int keyi = left;//若左作为key,必须要让右边先走,“如此可保证a[left]值小于a[keyi]值”,若右作为key,左先走while (left < right){//1.注意循环内也要包含条件left < right//2. a[right] >= a[keyi]注意此处等于不可少,若少了当数组有重复值时会死循环while (left < right && a[right] >= a[keyi])//右先走找小right--;while (left < right && a[left] <= a[keyi])//左后走找大left++;Swap(&a[left], &a[right]);}//left与right相遇时的位置即为key最终应该存在的位置Swap(&a[left], &a[keyi]); //a[left]值小于a[keyi]值,将keyi排在了它最终要排的那个位置keyi = left;QuickSort1(a, begin, keyi - 1);QuickSort1(a, keyi + 1, end);
}

(4)特性总结:

  1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(logN)
  4. 稳定性:不稳定

在这里插入图片描述

2. 快速排序(推荐:前后指针版本)

(1)基本思想:

在这里插入图片描述

(2)动态图解:

在这里插入图片描述

(3)静态图解(一趟):

在这里插入图片描述

(4)代码实现:

void QuickSort2(int* a, int left, int right)
{if (left >= right)return;//三数取中int midi = GetMidNumi(a, left, right);if (midi != left)Swap(&a[midi], &a[left]);int keyi = left;int prev = left, cur = left + 1;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur)//关键语句{Swap(&a[cur], &a[prev]);}cur++;}Swap(&a[prev], &a[keyi]);keyi = prev;QuickSort2(a, left, keyi - 1);QuickSort2(a, keyi + 1, right);
}

3. 快速排序(挖坑法)

(1)具体步骤:

若先保存最左位置的值为key,让最左位置作为坑位,则让右边先走去找比key小的,找到过后将其给左边坑位,然后更新该位置为新坑位,然后让左边开走,找到比key大的将其给右边的新坑位,重复以上步骤直到相遇
若先保存最右位置的值为key并将其设为坑位,则让左边先走,然后类同以上步骤

(2)静态图解:

在这里插入图片描述

(3)动态图解:

在这里插入图片描述

(4)代码实现:

void Swap(int* p, int* q)
{int tmp = *p;*p = *q;*q = tmp;
}
//三数取中
int GetMidNumi(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[mid] > a[left]){if (a[right] > a[mid])return mid;else if (a[right] < a[left])return left;elsereturn right;}else//(a[mid]<a[left]{if (a[right] > a[left])return left;else if (a[right] < a[mid])return mid;elsereturn right;}
}
void QuickSort3(int* a, int left, int right)
{if (left >= right)return;int begin = left, end = right;int midi = GetMidNumi(a, left, right);if (midi != left)Swap(&a[left], &a[midi]);int key = a[left];int hole = left;while (left < right){while (left < right && a[right] >= key)right--;a[hole] = a[right];//Swap(&a[hole], &a[right])效果相同,坑位会被其他值覆盖hole = right;while (left < right && a[left] <= key)left++;a[hole] = a[left];//Swap(&a[hole], &a[left]);hole = left;}a[hole] = key;QuickSort3(a, begin, hole - 1);QuickSort3(a, hole + 1, end);
}

4. 计数排序

(1)基本思想:

计数排序又称为鸽巢原理,是对哈希直接定址法的变形应用。

(2)具体步骤:

  1. 统计相同元素出现次数
  2. 根据统计的结果将序列回收到原来的序列中
    在这里插入图片描述

(3)代码实现:

void CountSort(int* a, int aSize)
{int min = a[0], max = a[0];for (int i = 0; i < aSize; i++){if (a[i] < min){min = a[i];}if (a[i] > max){max = a[i];}}int range = max - min + 1;int* aCount = (int*)calloc(range, sizeof(int));if (aCount == NULL){perror("calloc fail");return;}//相对位置计数for (int i = 0; i < aSize; i++){aCount[a[i] - min]++;}//回收到原数组进行排序int j = 0;for (int i = 0; i < range; i++){while (aCount[i]--){a[j++] = min + i;}}free(aCount);
}

(4)特性总结:

  1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
  2. 时间复杂度:O(MAX(N,范围))
  3. 空间复杂度:O(范围)
  4. 存在缺点,当数值相邻两个数差值较大时,会开辟较多无用空间造成浪费

5. 归并排序(递归法)

(1)基本思想:

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and
Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列即先使每个子序列有
序,再使子序列段间有序
。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤:
在这里插入图片描述

(2)动态图解:

在这里插入图片描述

(3)代码实现:

void _MergeSort(int* a, int begin, int end, int* tmp)
{if (begin >= end)return;int mid = (begin + end) / 2;// [begin, mid] [mid+1,end],子区间递归排序_MergeSort(a, begin, mid, tmp);_MergeSort(a, mid + 1, end, tmp);// [begin, mid] [mid+1,end]归并int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;//此处i不可一赋值0,由于递归区间不都是从原数组下标0处开始//使区间有序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++];}//由于递归区间不都是从原数组下标0处开始,所以copy时要加上beginmemcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));//将归并后的有序区间copy到原数组与之对应的区间
}//单独开一个函数目的是:解决递归过程中会一直malloc造成不必要的空间浪费的问题
void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail\n");return;}_MergeSort(a, 0, n - 1, tmp);free(tmp);
}

(4)辅助理解:

在这里插入图片描述

(5)特性总结:

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定

6. 归并排序(非递归法1)

(1)辅助理解:

在这里插入图片描述
控制边界:
在这里插入图片描述

//归并排序非递归法1,待区间元素个数为gab的一层全部数由算法在tmp数组中排好序后,一下将tmp数组中的有序数拷贝回原数组a中
void MergeSortNonR1(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gab = 1;//while循环次数为总层数while (gab < n){//for循环内为对区间元素个数为gab的一层排序for (int i = 0; i < n; i += 2 * gab){int begin1 = i, end1 = i + gab - 1;int begin2 = i + gab, end2 = i + 2 * gab - 1;//调整区间边界,防越界if (end1 >= n)//end1越界,先将end1修改为边界,再将[begin2,end2]区间改为不存在区间就不会执行后面对应算法以达到目的{end1 = n - 1;begin2 = n;end2 = n - 1;}else if (begin2 >= n)//begin2越界,将[begin2,end2]区间改为不存在区间就不会执行后面对应算法以达到目的{begin2 = n;end2 = n - 1;}else if (end2 >= n)//end2越界,将end2修改为边界{end2 = n - 1;}int j = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin2] >= a[begin1]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1)tmp[j++] = a[begin1++];while (begin2 <= end2)tmp[j++] = a[begin2++];}//该层排完序后一把拷贝回原数组amemcpy(a, tmp, sizeof(int) * n);gab *= 2;}free(tmp);
}

7. 归并排序(非递归法2)

与非递归法2:归并一部分拷贝一部分,
归并非递归法1:待区间元素个数为gab的一层全部数由算法在tmp数组中排好序后,一下将tmp数组中的有序数拷贝回原数组a中

//归并排序非递归法2,归并一部分拷贝一部分
void MergeSortNonR2(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gab = 1;//while循环次数为总层数while (gab < n){//for循环内为对区间元素个数为gab的一层排序for (int i = 0; i < n; i += 2 * gab){int begin1 = i, end1 = i + gab - 1;int begin2 = i + gab, end2 = i + 2 * gab - 1;//调整区间边界,防越界if (end1 >= n || begin2 >= n){break;}if (end2 >= n){end2 = n - 1;}int j = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin2] >= a[begin1]){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));//end1在变,i没变}gab *= 2;}free(tmp);
}

相关文章:

【数据结构】排序算法篇二

【数据结构】排序算法篇二 1. 快速排序&#xff08;hoare版本&#xff09;&#xff08;1&#xff09;基本思想&#xff1a;&#xff08;2&#xff09;动态图解&#xff1a;&#xff08;3&#xff09;代码实现&#xff1a;&#xff08;4&#xff09;特性总结&#xff1a; 2. 快速…...

python进阶篇-day09-数据结构与算法(非线性结构与排序算法)

非线性结构(树状结构) 特点: 每个节点都可以有n个子节点(后继节点) 和 n个父节点(前驱节点) 代表: 树, 图...... 概述 属于数据结构之 非线性结构的一种, 父节点可以有多个子节点(后续节点) 特点 有且只有1个根节点 每个节点都可以有1个父节点及任意个子节点, 前提: 根节点除…...

线性代数基础

Base 对于矩阵 A&#xff0c;对齐做 SVD 分解&#xff0c;即 U Σ V s v d ( A ) U\Sigma V svd(A) UΣVsvd(A). 其中 U 为 A A T AA^T AAT的特征向量&#xff0c;V 为 A T A A^TA ATA的特征向量。 Σ \Sigma Σ 的对角元素为降序排序的特征值。显然&#xff0c;U、V矩阵…...

LCR 021

题目&#xff1a;LCR 021 解法一&#xff1a;计算链表长度 遍历两次&#xff0c;第一次获取链表长度 L&#xff08;包括虚拟节点&#xff09;&#xff0c;第二次遍历到第 L-n 个节点&#xff08;从虚拟节点遍历&#xff09; public ListNode removeNthFromEnd(ListNode head, …...

【阿雄不会写代码】全国职业院校技能大赛GZ036第四套

也不说那么多了&#xff0c;要用到这篇博客&#xff0c;肯定也知道他是干嘛的&#xff0c;给博主点点关注点点赞&#xff01;&#xff01;&#xff01;这样博主才能更新更多免费的教程&#xff0c;不然就直接丢付费专栏里了&#xff0c;需要相关文件请私聊...

Vue组件:使用$emit()方法监听子组件事件

1、监听自定义事件 父组件通过使用 Prop 为子组件传递数据&#xff0c;但如果子组件要把数据传递回去&#xff0c;就需要使用自定义事件来实现。父组件可以通过 v-on 指令&#xff08;简写形式“”&#xff09;监听子组件实例的自定义事件&#xff0c;而子组件可以通过调用内建…...

数据分析-埋点

1、数据埋点的定义 针对特定用户行为或事件进行捕获、处理何发送的相关技术及其实施过程。 2、数据埋点的原理 埋点是数据采集的重要方式。通过在页面上植入代码&#xff0c;监控用户行为(例:页面加载、按钮点击等)。用户一旦触发了该事件&#xff0c;就会根据埋点信息将相关数…...

【文心智能体】通过工作流使用知识库来实现信息查询输出,一键查看旅游相关信息,让出行多一份信心

欢迎来到《小5讲堂》 这是《文心智能体平台》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 创建灵感基本配置头像名称和简介人物设定角色与目标思考路…...

服务器监控工具都是监控服务器的哪些性能和指标

服务器监控工具通常用于确保服务器及其相关服务的正常运行。这些工具可以帮助管理员快速识别并解决问题&#xff0c;从而减少停机时间和性能下降的风险。以下是服务器监控工具通常会监控的一些主要内容&#xff1a; 系统健康状态&#xff1a; CPU使用率 内存&#xff08;RAM&…...

不小心删除丢失了所有短信?如何在 iPhone 上查找和恢复误删除的短信

不小心删除了一条短信&#xff0c;或者丢失了所有短信&#xff1f;希望还未破灭&#xff0c;下面介绍如何在 iPhone 上查找和恢复已删除的短信。 短信通常都是非正式和无关紧要的&#xff0c;但短信中可能包含非常重要的信息。因此&#xff0c;如果您删除了一些短信以清理 iPh…...

【skyvern 快速上手】一句话让AI帮你实现爬虫+自动化

目录 skyvern介绍主要特点工作流程 部署&#xff08;重点介绍源码部署&#xff09;源码部署docker快速部署 运行&#xff08;基于源码&#xff09;后端前端 快速使用示例总结 skyvern介绍 Skyvern 是一款利用大语言模型&#xff08;LLM&#xff09;和计算机视觉技术来自动化浏…...

【C++ Primer Plus习题】14.1

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream> #include "wine.h" …...

在Ubuntu上运行QtCreator相关程序

背景&#xff1a;希望尝试在Linux系统上跑一下使用QtCreator相关的程序&#xff0c;因为有一些工作岗位要求有Linux上使用Qt的经验。 (1)我是把Windows上的程序移过来的&#xff0c;Windows上文件名称是不区分大小写的。 而Ubuntu上是区分的 所以一部分头文件需要进行修改&am…...

MybatisPlus 快速入门

目录 简介 安装 Spring Boot2 Spring Boot3 Spring 配置 Spring Boot 工程 Spring 工程 常见注解 条件构造器 流式查询 使用示例 批量操作 使用示例 自定义SQL Service接口 CRUD 扩展功能 代码生成 安装插件 通用枚举 配置枚举处理器 插件功能 配置示例…...

Java.lang中的String类和StringBuilder类介绍和常用方法

目录 Java.lang中的String类和StringBuilder类介绍和常用方法 String类介绍 String类的底层成员介绍 基本介绍 回顾String传址调用问题 String类对象的创建方式 String面试题 创建对象or不创建对象 创建了几个对象and共有几个对象 String常用方法 判断字符串是否相等方法 获取字…...

notepad++软件介绍(含安装包)

Notepad 是一款开源的文本编辑器&#xff0c;主要用于编程和代码编辑。它是一个功能强大的替代品&#xff0c;常常被用来替代 Windows 系统自带的记事本。 Notepad win系统免费下载地址 以下是 Notepad 的一些主要特点和功能&#xff1a; 多语言支持&#xff1a;Notepad 支持多…...

chapter13-常用类——(章节小结)——day17

498-常用类阶段梳理...

RTX AI PC 和工作站上部署多样化 AI 应用支持 Multi-LoRA

今天的大型语言模型&#xff08;LLMs&#xff09;在许多用例中都取得了前所未有的成果。然而&#xff0c;由于基础模型的通用性&#xff0c;应用程序开发者通常需要定制和调整这些模型&#xff0c;以便专门针对其用例开展工作。 完全微调需要大量数据和计算基础设施&#xff0…...

C++ STL-deque容器入门详解

1.1 deque容器基本概念 功能&#xff1a; 双端数组&#xff0c;可以对头端进行插入删除操作 deque与vector区别&#xff1a; vector对于头部的插入删除效率低&#xff0c;数据量越大&#xff0c;效率越低deque相对而言&#xff0c;对头部的插入删除速度回比vector快vector访…...

数据结构之折半查找

折半查找&#xff08;Binary Search&#xff09;&#xff0c;也称为二分查找&#xff0c;是一种在有序数组中查找特定元素的搜索算法。其工作原理是&#xff0c;通过不断将待查找的区间分成两半&#xff0c;并判断待查找的元素可能存在于哪一半&#xff0c;然后继续在存在可能性…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来

一、破局&#xff1a;PCB行业的时代之问 在数字经济蓬勃发展的浪潮中&#xff0c;PCB&#xff08;印制电路板&#xff09;作为 “电子产品之母”&#xff0c;其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透&#xff0c;PCB行业面临着前所未有的挑战与机遇。产品迭代…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...

flow_controllers

关键点&#xff1a; 流控制器类型&#xff1a; 同步&#xff08;Sync&#xff09;&#xff1a;发布操作会阻塞&#xff0c;直到数据被确认发送。异步&#xff08;Async&#xff09;&#xff1a;发布操作非阻塞&#xff0c;数据发送由后台线程处理。纯同步&#xff08;PureSync…...

【若依】框架项目部署笔记

参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作&#xff1a; 压缩包下载&#xff1a;http://download.redis.io/releases 1. 上传压缩包&#xff0c;并进入压缩包所在目录&#xff0c;解压到目标…...