数据结构|排序算法(三)选择排序 堆排序 归并排序

一、选择排序

1.算法思想

选择排序(Selection Sort)是一种简单直观的排序算法,其基本思想是:每次都从待排序部分中选出最小的一个数据和待排序的第一个数据交换。

将待排序序列分为已排序和未排序两部分,初始时已排序部分为空,未排序部分包含整个待排序序列。在每一轮迭代中,从未排序部分中选择最小(或最大)的元素,将其与未排序部分的第一个元素交换位置,从而将该元素添加到已排序部分的末尾。重复这个过程,直到整个序列都被排序。
选择排序的具体步骤:
从整个序列中选择最小的元素,将其与第一个位置的元素交换。此时,第一个位置的元素就是整个序列中最小的元素,已排序部分包含这一个元素,未排序部分包含剩余的元素。
在剩余的未排序元素中选择最小的元素,将其与未排序部分的第一个位置(即第二个位置)的元素交换。此时,前两个位置的元素是有序的,已排序部分包含两个元素,未排序部分包含剩余的元素。
重复上述步骤,每次都从未排序部分中选择最小的元素,并将其与未排序部分的第一个位置的元素交换,直到未排序部分只剩下一个元素。此时,整个序列已经完全有序。

2.代码实现 

//选择排序
void SelectSort(int* arr, int len)
{int tmp;int minIndex;//最小值的下标for (int i = 0; i < len - 1; i++){minIndex = i;for (int j = i + 1; j < len; j++){if (arr[minIndex] > arr[j]){minIndex = j;}}if (minIndex != i)//最小值和待排序的第一个为同一个,则不交换,提高效率{tmp = arr[minIndex];arr[minIndex] = arr[i];arr[i] = tmp;}}
}

 3.复杂度分析

1.选择排序的时间复杂度为O(n2),其中n是待排序序列的长度。这是因为对于一个长度为n的序列,需要进行n−1轮选择和交换操作,每轮操作需要遍历未排序部分的元素,平均需要比较n/2次。

2.选择排序的空间复杂度为O(1),因为它只需要使用常数级别的额外空间来进行元素的交换。

3. 不稳定:在选择排序里,当把最小元素和未排序序列的首个元素交换时,可能会改变相等元素的相对顺序。

二、堆排序

堆是一种特殊的完全二叉树,分为大根堆和小根堆。

大根堆的每个节点的值都大于或等于其左右子节点的值,小根堆则相反,每个节点的值都小于或等于其左右子节点的值。(大根堆小根堆只看父子关系)


堆排序的基本思想是将待排序的序列构建成一个堆,然后依次取出堆顶元素并调整堆,直到整个序列有序。

1.算法思想

  • 建堆:将给定的数组构建成一个大根堆(或小根堆)。从最后一个非叶子节点开始,依次向上调整每个节点,使其满足堆的性质。
  • 交换元素:将堆顶元素与堆的最后一个元素交换位置,此时堆顶元素是当前堆中的最大(或最小)值,将其取出,放到已排序序列的末尾。
  • 调整堆:交换元素后,堆的性质可能被破坏,需要对剩余的元素重新调整堆,使其再次满足堆的性质。重复步骤 2 和 3,直到堆中只剩下一个元素,此时整个数组已经有序。

 1->调整成大根堆

(1)从最后一棵子树开始,从后往前调整

(2)每次调整,从上往下

(3)整体调整成大根堆

具体调整:

定义一个临时变量tmp,把根放到tmp里,找左右孩子的最大值,和tmp比较,如果比tmp大,则放到根的位置,继续递归比较新的左右孩子的最大值

调整顺序:

调整子树:

 调整完成:

 2->根和待排序的最后一个交换

根是最大的,交换后则视作有序

3->再次调整成大根堆

2.代码实现

//堆排序void HeapAdjust(int* arr, int start, int end)
{int tmp = arr[start];for (int i = 2 * start + 1; i <= end;i=2*i+1)//从上往下{if (i < end && arr[i] < arr[i + 1])//如果有右孩子,且左孩子的值小于右孩子{i++;}//i一定是左右孩子的最大值if (arr[i] > tmp){arr[start] = arr[i];start = i;}else{break;}}arr[start] = tmp;
}void HeapSort(int* arr, int len)
{//第一次建立大根堆(从后往前,多次调整)//根分别为4 3 2 1的子树for (int i = (len-1-1)/2; i >= 0; i--)//i的初值与结点总数有关,i=总结点数len-根-{HeapAdjust(arr, i, len - 1);}//每次将根和待排序的最后一个交换,然后再次调整(注意是待排序部分)int tmp;//用于交换for (int i = 0; i < len - 1; i++){tmp = arr[0];arr[0] = arr[len - 1 - i];arr[len - 1 - i] = tmp;//再次调整HeapAdjust(arr, 0, len - 1 - i - 1);}return;
}

3.复杂度分析 

建立大根堆的时间复杂度:O(n)

每次调整大根堆时间复杂度:O(logn),调整次数n-1次,总时间复杂度O(nlogn)

综合建堆和排序两个阶段,堆排序的时间复杂度为O(n+nlogn),通常简化为O(nlogn)。这是堆排序的平均时间复杂度;

空间复杂度O(1);

不稳定

三、归并排序

1.算法思想

归并排序的基本思想是将一个数组分成两个子数组,对每个子数组进行排序,然后将排序好的子数组合并成一个排序好的数组。这个过程是递归进行的,直到子数组的长度为 1,此时子数组已经是有序的。

1.分解:将待排序的数组不断地分成两半,直到每个子数组只有一个元素。可以使用递归实现这一步骤。
2.合并:将两个已经排序好的子数组合并成一个排序好的数组。在合并过程中,比较两个子数组的元素,将较小的元素依次放入一个临时数组中,直到其中一个子数组的元素全部被放入临时数组。然后将另一个子数组中剩余的元素全部放入临时数组。最后将临时数组中的元素复制回原数组。

注意边界越界。 

2.代码实现

//归并排序
//一次归并
//gap:归并段的长度
static void Merge(int* arr, int len, int gap)
{int low1 = 0;//第一个归并段的起始下标int high1 = low1 + gap - 1;//第一个归并段的结束下标int low2 = high1 + 1;//第二个归并段的起始下标int high2 = low2 + gap - 1 < len - 1 ? low2 + gap - 1 : len - 1;//第二个归并段的结束下标//防止越界//存放归并好的数据int* brr = (int*)malloc(len * sizeof(int));assert(brr != NULL);int i = 0;//brr的下标//有两个归并段while (low2 < len)//表面至少存在两个归并段{//两个归并段都有数据,需要比较low1和low2while (low1<=high1&&low2<=high2){if (arr[low1] <= arr[low2]){brr[i++] = arr[low1++];}else{brr[i++] = arr[low2++];}//谁小存谁}//一个归并段的数据已经完成了,另一个还有数据while (low1 <= high1)//第一个归并段还有数据{brr[i++] = arr[low1++];}while (low2 <= high2)//第二个归并段还有数据{brr[i++] = arr[low2++];}//下两个归并段low1 = high2 + 1;high1 = low1 + gap - 1;low2 = high1 + 1;high2 = low2 + gap < len ? low2 + gap - 1 : len - 1;}//只有一个归并段while (low1<len){brr[i++] = arr[low1++];}//将归并好的数据拷贝到arr中for (i = 0; i < len; i++){arr[i] = brr[i];}free(brr);
}void MergeSort(int* arr, int len)
{for (int i = 1; i < len; i *= 2){Merge(arr, len, i);//一次归并}
}

3.复杂度分析

归并排序是一种基于分治思想的排序算法:

时间复杂度:
最好情况:当待排序序列已经是有序的时候,归并排序依然需要进行logn层的归并操作,每层归并操作需要比较和移动n次元素,所以时间复杂度为O(nlogn)。
最坏情况:当待排序序列是逆序的时候,同样需要logn层归并操作,每层归并操作也需要比较和移动n次元素,时间复杂度也是O(nlogn)。平均情况:归并排序将序列分成两部分,然后对两部分分别排序,再将排好序的两部分合并。

在平均情况下,每次划分都能将序列分成大致相等的两部分,所以时间复杂度为O(nlogn)。

空间复杂度:归并排序在合并过程中需要借助额外的存储空间来存放临时数据,最坏情况下需要
O(n)的辅助空间来完成排序。

稳定性:归并排序是稳定的排序算法。在归并过程中,当左右两个子序列中出现相等元素时,先将左边子序列中的元素放入临时数组,然后再将右边子序列中的元素放入临时数组,这样相等元素的相对顺序在排序前后不会发生改变。
综上所述,归并排序的时间复杂度为O(nlogn),空间复杂度为O(n),并且是稳定的排序算法。

以上是排序算法第三部分关于选择排序、堆排序以及归并排序的知识,如果有帮助可以点赞收藏一下,会持续更新输出有用的内容,感兴趣可以关注我!

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

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

相关文章

Vue3 + TypeScript,关于item[key]的报错处理方法

处理方法1&#xff1a;// ts-ignore 注释忽略报错 处理方法2&#xff1a;item 设置为 any 类型

8.观察者模式:思考与解读

原文地址:观察者模式&#xff1a;思考与解读 更多内容请关注&#xff1a;7.深入思考与解读设计模式 引言 在开发软件时&#xff0c;系统的某些状态可能会发生变化&#xff0c;而你希望这些变化能够自动通知到依赖它们的其他模块。你是否曾经遇到过&#xff0c;系统中某个对象…

【HD-RK3576-PI】Ubuntu桌面多显、旋转以及更新Logo

硬件&#xff1a;HD-RK3576-PI 软件&#xff1a;Linux6.1Ubuntu22.04 在基于HD-RK3576-PI硬件平台运行Ubuntu 22系统的开发过程中&#xff0c;屏幕方向调整是提升人机交互体验的关键环节。然而&#xff0c;由于涉及uboot引导阶段、内核启动界面、桌面环境显示全流程适配&#x…

Rsync+sersync2实现目录实时同步

Sersync rsync 实现实时同步服务 sersync2二进制包目录规划 /app/tools/sersync/ /app/tools/sersync/bin /app/tools/sersync/conf项目架构是这样的&#xff1a; ------------------- ------------------- ------------------- | | …

MySQL视图高级应用与最佳实践

1. 视图与索引的协同优化​​ ​​物化视图&#xff08;模拟实现&#xff09;​​ MySQL原生不支持物化视图&#xff0c;但可通过“定时刷新”的物理表模拟&#xff1a; -- 1. 创建存储结果的物理表 CREATE TABLE cached_monthly_sales (product_id INT,total_sales DECIMAL(10…

string的模拟实现 (6)

目录 1.string.h 2.string.cpp 3.test.cpp 4.一些注意点 本篇博客就学习下如何模拟实现简易版的string类&#xff0c;学好string类后面学习其他容器也会更轻松些。 代码实现如下&#xff1a; 1.string.h #define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include <…

Unity:像素(Pixels) 和 单位(Units)

目录 从第一性原理出发&#xff1a;什么是像素和 Unit&#xff1f; &#x1f9f1; 1. 像素&#xff08;Pixel&#xff09;&#xff1a;图像的最小单位 &#x1f4d0; 2. Unity Unit&#xff08;单位&#xff09;&#xff1a;游戏世界中的度量单位 核心换算公式&#xff1a;…

【失败总结】Win10系统安装docker

1.启用或关闭windows功能中&#xff0c;将Hyper-V功能勾选全部启用&#xff0c;容器勾选。设置好后要重启电脑。 2.管网下载下载安装Docker  Docker官网&#xff1a;https://www.docker.com/ 3.可以自定义Docker安装路径 新建安装目录&#xff1a;d:\MySoftware\Docker并将D…

《Adaptive Layer-skipping in Pre-trained LLMs》- 论文笔记

作者&#xff1a;Xuan Luo, Weizhi Wang, Xifeng Yan Department of Computer Science, UC Santa Barbara xuan_luoucsb.edu, weizhiwangucsb.edu, xyancs.ucsb.edu 1. 引言与动机 1.1 背景 LLM 的成功与挑战: 大型语言模型 (LLMs) 在翻译、代码生成、推理等任务上取得巨大成…

DQN在Gym的MountainCar环境的实现

DQN on MountainCar 引言 在本次实验里&#xff0c;我构建了DQN和Dueling DQN&#xff0c;并在Gymnasium库的MountainCar环境中对它们展开测试。我通过调整训练任务的超参数&#xff0c;同时设计不同的奖励函数及其对应参数&#xff0c;致力于获取更优的训练效果。最后&#…

计算机网络综合实验指南

计算机网络综合实验指南 本实验将结合《计算机网络自顶向下》前三章的核心概念&#xff0c;通过实际操作加深对应用层、运输层和网络层的理解。实验涵盖 HTTP/TCP抓包分析、DNS解析观察、网页性能评估及简单Socket编程&#xff0c;帮助你将理论转化为实践。 实验准备 工具&…

【AI部署】腾讯云GPU-RUN—SadTalker的AI数字人视频—未来之窗超算中心

磁盘空间 创建未来之窗 查看磁盘命令 df -h 指定路径创建环境 conda create --prefix sadtalker python3.10 指令路径运行环境 conda activate ./sadtalker 安装环境 pip install torch1.12.1cu113 torchvision0.13.1cu113 torchaudio0.12.1 --extra-index-url https://…

爬虫利器SpiderTools谷歌插件教程v1.0.0!!!web端JavaScript环境检测!!!

SpiderTools谷歌插件教程v1.0.0 一、SpiderTools简介二、下载通道三、插件介绍四、插件使用五、工具函数使用 补环境工具推荐&#xff1a;爬虫补环境利器webEnv 一、SpiderTools简介 SpiderTools主要用于检测和监控网页的JavaScript运行环境。该插件可以帮助开发者更好地查看…

Android开发协调布局滑动悬停

Android开发协调布局滑动悬停 直接给个xml,防止下次忘了怎么写。 <?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"x…

Linux学习——TCP

一.TCP编程API 1.socket函数 1.socket函数 include include int socket(int domain,int type,int protocol); 参数 domain AF_INET AF_INET6 AF_UNIX,AF_LOCAL AF_NETLINK AF_PACKET type SOCK_STREAM: 流式…

Linux驱动开发--异步通知与异步I/O

3、异步通知与异步I/O 3.1 Linux信号 阻塞与非阻塞访问、poll()函数提供了较好的解决设备访问的机制&#xff0c;但是如果有了异步通知&#xff0c;整套机制则更加完整了。 异步通知的意思是&#xff1a;一旦设备就绪&#xff0c;则主动通知应用程序&#xff0c;这样应用程序…

大语言模型推理能力的强化学习现状理解GRPO与近期推理模型研究的新见解

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【Linux系统】Linux基础指令(详解Linux命令行常用指令,每一个指令都有示例演示)

文章目录 一、与文件路径相关的指令0.补充知识&#xff1a;路径的认识1.pwd 指令2.cd 指令&#xff08;含家目录的介绍&#xff09; 二、创建和删除文件的指令0.补充知识&#xff1a;普通文件和目录文件1.touch 指令&#xff08;可以修改文件的时间戳&#xff09;2.mkdir 指令3…

LangChain 单智能体模式示例【纯代码】

# LangChain 单智能体模式示例import os from typing import Anyfrom langchain.agents import AgentType, initialize_agent, Tool from langchain_openai import ChatOpenAI from langchain.tools import BaseTool from langchain_experimental.tools.python.tool import Pyt…

解决:VSCode C++ conan 安装第三方库后 头文件报错

文章目录 1 头文件include路径查找报错参考 1 头文件include路径查找报错 找到conan_toolchain.cmake中 INCLUDE_PATH list(PREPEND CMAKE_INCLUDE_PATH "/Users/hanliqiang/.conan2/p/b/fmte8c4f7a755477/p/include")生成C编译配置 CtrlShiftP 中选择C Edit Confi…