排序算法:归并排序,golang实现

news2024/9/21 18:58:45

目录

前言

归并排序

代码示例

1. 算法包

2. 归并排序代码

3. 模拟程序

4. 运行程序

5. 从大到小排序

归并排序主要操作

1. 合并

2. 分割(Divide)与递归排序(Conquer)

总体思想

循环次数测试

假如 10 条数据进行排序

假如 20 条数据进行排序

假如 30 条数据进行排序

假设 5000 条数据,对比 冒泡、选择、插入、快速、堆

归并排序的适用场景

1. 大数据集

2. 链表排序

3. 外部排序

4. 稳定性需求


前言

在实际场景中,选择合适的排序算法对于提高程序的效率和性能至关重要,本节课主要讲解"归并排序"的适用场景及代码实现。

归并排序

归并排序(Merge Sort)是一种分而治之的排序算法。它将一个大列表分成两个小列表,分别对这两个小列表进行排序,然后将排序好的小列表合并成一个最终的排序列表。归并排序的关键在于合并(Merge)过程,它确保了在合并的过程中,两个已排序的序列被合并成一个新的、有序的序列。

代码示例

下面我们使用Go语言实现一个归并排序

1. 算法包

创建一个 pkg/algorithm.go

mkdir pkg/algorithm.go

如果看过上节课的堆排序,则已存在该文件,我们就不需要再创建了

2. 归并排序代码

打开 pkg/algorithm.go 文件,代码如下

从小到大 排序

package pkg

// BubbleSort 冒泡排序
...

// SelectionSort 选择排序
...

// InsertionSort 插入排序
...

// QuickSort 快速排序
...

// partition 分区操作
...

// HeapSort 堆排序
...

// heapify 将以 i 为根的子树调整为最大堆
...

// MergeSort 归并排序
func MergeSort(arr []int) []int {
	if len(arr) <= 1 {
		return arr
	}

	// 找到中点,分割数组
	mid := len(arr) / 2
	left := MergeSort(arr[:mid])
	right := MergeSort(arr[mid:])

	// 合并两个已排序的切片
	return merge(left, right)
}

// merge 函数用于合并两个已排序的切片
func merge(left, right []int) []int {
	var result []int
	i, j := 0, 0

	for i < len(left) && j < len(right) {
		if left[i] < right[j] {
			result = append(result, left[i])
			i++
		} else {
			result = append(result, right[j])
			j++
		}
	}

	// 如果左侧有剩余,则追加到结果切片
	result = append(result, left[i:]...)
	// 如果右侧有剩余,则追加到结果切片
	result = append(result, right[j:]...)

	return result
}

3. 模拟程序

打开 main.go 文件,代码如下:

package main

import (
	"demo/pkg"
	"fmt"
)

func main() {
	// 定义一个切片,这里我们模拟 10 个元素
	arr := []int{98081, 27887, 31847, 84059, 2081, 41318, 54425, 22540, 40456, 3300}
	fmt.Println("arr 的长度:", len(arr))
	fmt.Println("Original data:", arr) // 先打印原始数据
	newArr := pkg.MergeSort(arr)       // 调用归并排序
	fmt.Println("New data:  ", newArr) // 后打印排序后的数据
}

4. 运行程序

go run main.go

能发现, Original data 后打印的数据,正是我们代码中定义的切片数据,顺序也是一致的。

New Data 后打印的数据,则是经过归并排序后的数据,是从小到大的。

5. 从大到小排序

如果需要 从大到小 排序也是可以的,在代码里,需要将两个 if 判断比较的 符号 进行修改。

修改 pkg/algorithm.go 文件:

package pkg

// BubbleSort 冒泡排序
...

// SelectionSort 选择排序
...

// InsertionSort 插入排序
...

// QuickSort 快速排序
...

// partition 分区操作
...

// HeapSort 堆排序
...

// heapify 将以 i 为根的子树调整为最大堆
...

// MergeSort 归并排序
func MergeSort(arr []int) []int {
	if len(arr) <= 1 {
		return arr
	}

	// 找到中点,分割数组
	mid := len(arr) / 2
	left := MergeSort(arr[:mid])
	right := MergeSort(arr[mid:])

	// 合并两个已排序的切片
	return merge(left, right)
}

// merge 函数用于合并两个已排序的切片
func merge(left, right []int) []int {
	var result []int
	i, j := 0, 0

	for i < len(left) && j < len(right) {
		if left[i] > right[j] {
			result = append(result, left[i])
			i++
		} else {
			result = append(result, right[j])
			j++
		}
	}

	// 如果左侧有剩余,则追加到结果切片
	result = append(result, left[i:]...)
	// 如果右侧有剩余,则追加到结果切片
	result = append(result, right[j:]...)

	return result
}

只需要一丁点的代码即可

从 package pkg 算第一行,上面示例中在第四十五行代码,我们将 "<" 改成了 ">" ,这样就变成了 从大到小排序了

归并排序主要操作

主要操作包括 分割合并


1. 合并

合并操作由 merge 函数实现,它接收两个已排序的切片 left 和 right,并返回一个新的、包含两个切片所有元素且已排序的切片。

  • 初始化:首先,创建一个空的切片 result 用于存储合并后的结果。同时,使用两个索引 i 和 j 分别指向 left 和 right 的起始位置
  • 比较与合并:然后,使用一个循环,比较 left[i] 和 right[j] 的大小。将较小的元素追加到 result 中,并移动相应的索引。这个过程一直持续到任一切片中的所有元素都被添加到 result 中
  • 追加剩余元素:如果 left 和 right 中还有剩余的元素(即某个切片的索引没有遍历完),则直接将剩余的元素追加到 result 的末尾。这是因为在循环结束时,剩余的元素一定是已排序的(它们来自原始的已排序切片)

2. 分割(Divide)与递归排序(Conquer)

分割与递归排序操作由 mergeSort 函数实现。

  • 基本情况:如果输入的切片 arr 的长度小于或等于 1,则不需要排序,直接返回该切片。因为单个元素或空切片都可以被认为是已排序的
  • 分割:找到切片的中点 mid,将切片分为两部分:arr[:mid] 和 arr[mid:]
  • 递归排序:对着两部分分别调用 mergeSort 函数进行递归排序。这会将问题分解成更小的子问题,直到子问题小到满足基本情况
  • 合并:最后,使用 merge 函数将这两个递归排序后的切片合并成一个有序的切片,并返回该切片

总体思想

归并排序通过递归地将数组分解成越来越小的半子表,对半子表排序,然后再将排好序的半子表合并成有序的表来工作。这个过程需要额外的存储空间来存放合并后的数组,因此其空间复杂度为 O(n)。然而,归并排序的时间复杂度是稳定的 O(n log n),并且由于其分治特性,它在实际应用中非常有效,尤其是在处理大数据集时。

循环次数测试

参照上面示例进行测试(因考虑到每次手动输入 10 条、20 条、30 条数据太繁琐,所以我写了一个函数,帮助我自动生成 0到100000 的随机整数)

假如 10 条数据进行排序

总计循环了 32 

假如 20 条数据进行排序

总计循环了 84 

假如 30 条数据进行排序

总计循环了 137 

假设 5000 条数据,对比 冒泡、选择、插入、快速、堆

  • 冒泡排序:循环次数 12,502,499 次
  • 选择排序:循环次数 12,502,499 次
  • 插入排序:循环次数 6,323,958 次
  • 快速排序:循环次数 74,236 次
  • 堆排序:循环次数 59,589 次
  • 归并排序:循环次数 60,288 次

归并排序的适用场景

归并排序在以下场景表现良好

1. 大数据集

对于非常大的数据集,归并排序通常比快速排序或插入排序更有效,因为归并排序的时间复杂度是 O(n log n),并且它的性能相对稳定,不会因数据集的不同而大幅度变化

2. 链表排序

由于归并排序在合并过程中不需要额外的空间(除了递归栈),所以在链表排序时非常高效。链表数据结构的特性使得分割和合并操作相对简单

3. 外部排序

当数据集太大,无法全部加载到内存时,可以使用归并排序的外部版本。在这个版本中,数据被分割成多个块,每块单独排序后存储在磁盘上,然后通过归并操作将它们合并成一个有序的文件

4. 稳定性需求

归并排序是稳定的排序算法,这意味着相等的元素在排序后仍然保持原来的顺序。这在需要保持元素原始顺序的某些应用中非常有用

尽管归并排序在很多场景下都很有用,但它也有缺点,主要是需要额外的空间 O(n) 来存储临时数组。这在内存受限的情况下可能是一个问题。

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

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

相关文章

虾皮笔试0620-编程题

难度偏易&#xff0c;给出解题思路。 按照空格分割字符串&#xff0c;每个字符串用双指针反转小写字母。 记录原来位置到二维数组&#xff0c;排序&#xff0c;从小到达购买&#xff0c;再把英雄对应之前的位置排序输出。 先变成循环链表&#xff0c;找到旋转后的头节点&#x…

day2 PS教程——搞定图层的使用方法,效率大翻倍

day2 PS教程——搞定图层的使用方法&#xff0c;效率大翻倍 目录 1.图层 智能对象 2.蒙版 与智能对象绑定 使用橡皮——镂空 剪切模板 上放图片&#xff0c;下放图形&#xff0c;按ALT同时&#xff0c;点击两图层间的即可 底下可以放黑色背景 选中多个图层后&#xff0…

海尔智家三翼鸟:从家电到场景,能否跨越智能化陷阱?

在智能家居浪潮的席卷之下&#xff0c;三翼鸟作为海尔智家旗下的场景品牌&#xff0c;曾一度被视为传统家电厂商转型升级的典范。然而&#xff0c;在光鲜亮丽的宣传背后&#xff0c;三翼鸟正逐步暴露出难以忽视的困境与挑战&#xff0c;其智能化之路似乎并不如预期般顺畅。 从用…

内存一直增加—-代码里有matplotlib绘图的代码

问题描述 最近在调试代码的时候发现内存一直在呈线性增加,持续一段时间后程序就会停止,但是排查了好久也没有发现问题. 最后发现竟然是绘图代码的问题,没有plt.close(),导致生成的绘图一直保存在内存里不断增加. 解决方案 增加一行代码plt.close(),把绘图关闭 点击访问博…

AcWing 1191. 家谱树(图论,拓扑排序的模板)

有个人的家族很大&#xff0c;辈分关系很混乱&#xff0c;请你帮整理一下这种关系。 给出每个人的孩子的信息。 输出一个序列&#xff0c;使得每个人的孩子都比那个人后列出。 输入格式 第 1 行一个整数 n&#xff0c;表示家族的人数&#xff1b; 接下来 n 行&#xff0c;…

app逆向实战:某新闻7.38.0版本加固脱壳和参数分析

本篇博客旨在记录学习过程&#xff0c;不可用于商用等其它途径 入口 这次研究的是头条数据接口&#xff0c;每次向下滑动即可刷新请求 抓包 根据抓包结果得知动态参数是st和sn&#xff0c;大胆猜测sn的生成跟st有关&#xff0c;其它参数是固定的&#xff0c;后面看生成具体…

windows如何让右键点击时不折叠选项(展开显示更多选项)?

升级windows后&#xff0c;发现右键菜单自动折叠了&#xff0c;用起来很不方便&#xff0c;有没有办法&#xff0c;让右键菜单自动展开那 &#xff1f; 期望的效果是这样的&#xff1a; 具体操作请参考我这篇公众号文章。 windows如何让右键点击时不折叠选项&#xff08;展开…

细说MCU构建两路包含ADC和DAC的测量系统的方法

目录 一、参考工程 二 、硬件配置 1.配置GPIO 2.配置ADC1和ADC2 3.配置DAC1和DAC2 4.配置定时器 5.配置串口 6.选择时钟源和Debug 7.配置系统时钟和ADC时钟 三、代码修改 1.定义DAC波形数据 2.重定义外部中断回调函数 3.重定义ADC回调函数 4.初始化 5.变…

校园气膜体育馆:快速搭建高端多功能运动场—轻空间

在现代校园建设中&#xff0c;体育设施的完善至关重要。气膜体育馆作为一种创新的解决方案&#xff0c;因其快速搭建、高端品质和多功能使用而受到广泛关注。它能够满足包括篮球和羽毛球在内的多种体育项目需求&#xff0c;为校园提供了一个理想的运动场地。 迅速搭建&#xff…

网工内推 | 上市公司网工,14薪,IP/软考认证优先,带薪年假

01 威派格 &#x1f537;招聘岗位&#xff1a;网络工程师 &#x1f537;岗位职责&#xff1a; 1.负责项目的网络规划、设计、实施&#xff1b;网络基础架构的持续优化。 2.负责网络设备和网络环境的日常维护&#xff0c;排错&#xff0c;分析故障原因&#xff0c;提供维护方…

【调试笔记-20240731-Linux-Wordpress 添加 wp-weixin 插件支持微信用户扫码注册登录】

调试笔记-系列文章目录 调试笔记-20240731-Linux-Wordpress 添加 wp-weixin 插件支持微信用户扫码注册登录 文章目录 调试笔记-系列文章目录调试笔记-20240731-Linux-Wordpress 添加 wp-weixin 插件支持微信用户扫码注册登录 前言一、调试环境操作系统&#xff1a;Windows 10 …

网页出现 404 这些代表什么你知道吗?带你了解网络请求状态码

每个状态码都代表不同的含义&#xff0c;下面我们就一起来看一看这些状态码都代表什么意思。 网络请求状态码 网络请求状态码是服务器在响应客户端请求时返回的三位数字代码。这些代码用于指示请求的结果&#xff0c;包括成功、重定向、客户端错误和服务器错误。那网络状态码…

Langchain入门教程

1、框架介绍 LangChain 是一个用于构建大语言模型应用的开源框架&#xff0c;2022年10月作为开源项目推出&#xff0c;目前已经在开发社区颇具名气&#xff0c;构建起了自己的一片开发生态。 LangChain 在 2023 年 3 月获得了 Benchmark Capital 的 1000 万美元种子轮融资&am…

用于实现无线数据传输和通信连接的2.4GHz无线芯片-RF298

2.4GHz无线射频收发芯片是一种常见的无线通信模块&#xff0c;它工作在2.4GHz频段&#xff0c;用于实现无线数据传输和通信连接&#xff1b;通常用于各种无线通信设备&#xff0c;如手机、Wi-Fi路由器、蓝牙设备、无线鼠标键盘、电视和机顶盒遥控器、智能家居及物联网系统、遥控…

数据结构_study(五)

树 n(n>0)个结点的有限集 空树&#xff1a;n0 在非空树中&#xff1a; 有且仅有一个特定的根root结点&#xff1b; n>1时&#xff0c;其余结点可以分为m个互不相交的有限集&#xff0c;其中每个集合也是一棵树&#xff0c;根的子树 一对多 结点的度&#xff1a;结点拥…

SpringBoot整合Flink CDC实时同步postgresql变更数据,基于WAL日志

SpringBoot整合Flink CDC实时同步postgresql变更数据&#xff0c;基于WAL日志 一、前言二、技术介绍&#xff08;Flink CDC&#xff09;1、Flink CDC2、Postgres CDC 三、准备工作四、代码示例五、总结 一、前言 在工作中经常会遇到要实时获取数据库&#xff08;postgresql、m…

“等保测评下的数据加密与隐私保护“

在当今数字化时代&#xff0c;数据已成为企业最宝贵的资产之一。然而&#xff0c;数据泄露、隐私侵犯等事件频发&#xff0c;不仅给企业带来经济损失&#xff0c;更严重损害了公众信任。等保测评&#xff0c;作为国家信息安全等级保护制度的重要组成部分&#xff0c;对数据加密…

微信小程序开发:宿主环境—组件

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

Python安装与环境配置,2024最新,超详细保姆级教程!

安装Python 来到Python官网&#xff1a;https://www.python.org/ Downloads>Windows&#xff1a; 选择想要的版本后点击进去&#xff1a; 下载后点击安装&#xff1a; 在本地电脑输入命令提示符&#xff1a;winR 环境变量配置 若执行命令提示符&#xff0c;输入Python后&…

Centos7.9开机自启更新系统时间

在CentOS 7.9中设置自动同步系统时间&#xff0c;主要依赖于NTP&#xff08;Network Time Protocol&#xff09;服务。以下是详细的步骤&#xff1a; 1、安装NTP服务 首先&#xff0c;你需要确保NTP服务已经安装在系统上。如果尚未安装&#xff0c;可以通过以下命令进行安装&…