归并排序 图解 递归 + 非递归 + 笔记

news2024/11/24 12:41:57

前置知识:讲解019-算法笔试中处理输入和输出,讲解020-递归和master公式

  • (1)左部分排好序,右部分排好序,利用merge过程让左右整体有序
  • (2)merge过程:谁小拷贝谁,直到左右两部分所有的数字耗尽
  • (3)递归实现和非递归实现
  • (4)时间复杂度O(n*logn)
  • (5)需要辅助数组,所以额外空间复杂度O(n)
  • (6)归并排序为什么比O(n^2)的排序快?因为比较行为没有浪费!
  • (7)利用归并排序的便利性可以解决很多问题,例如归并分治

注意:有些资料说可以用原地归并排序,把额外空间复杂度变成O(1),不要浪费时间去学。因为原地归并排序确实可以省空间,但是会把复杂度变成O(n^2)

  • 对这个数组arr=[6,4,2,3,9,4] ,进行归并排序 

  • 挑其中一步来演示: 把[2,4,6]和[3,4,9]合并(merge)

最后再刷回原数组 

void merge(int left, int mid, int right) {
	int i = left;
	int a = left;
	int b = mid + 1;
	while (a <= mid && b <= right) {
		help[i++] = arr[a] <= arr[b] ? arr[a++] : arr[b++];
	}
	// 左侧指针,右侧指针,必有一个越界,另一个不越界
	while (a <= mid) {
		help[i++] = arr[a++];
	}
	while (b <= right) {
		help[i++] = arr[b++];
	}
	for (i = left; i <= right; i++) { // 把 help 里面的数据重新刷回到原数组arr
		arr[i] = help[i];
	}
}

(1)归并排序递归版

// 递归方法
void mergeSort(int left, int right) {
	if (left == right) return;
	int mid = (left + right) / 2;
	mergeSort(left, mid);
	mergeSort(mid + 1, right);
	merge(left, mid, right);
}

(2)归并排序非递归版

void mergeSort2() {
	// 一共发生O(logn)次
	for (int left, mid, right, step = 1; step < n; step <<= 1) {
		// 内部分组merge,时间复杂度:O(n)
		left = 0;
		while (left < n) {
			mid = left + step - 1;
			if (mid + 1 >= n) {
				// 已经没有右侧了
				break;
			}
			// 有右侧,求右侧的右边界
			right = min(left + (step << 1) - 1, n - 1);
			// left ... mid mid+1 ... right
			//								left ... mid mid+1 ... right
			//															 left ... mid mid+1 ... right
			merge(left, mid, right);
			left = right + 1;
		}
	}	
}

完整代码:

/*
* 前置知识:讲解019-算法笔试中处理输入和输出,讲解020-递归和master公式
	(1)左部分排好序,右部分排好序,利用merge过程让左右整体有序
	(2)merge过程:谁小拷贝谁,直到左右两部分所有的数字耗尽
	(3)递归实现和非递归实现
	(4)时间复杂度O(n*logn)
	(5)需要辅助数组,所以额外空间复杂度O(n)
	(6)归并排序为什么比O(n^2)的排序快?因为比较行为没有浪费!
	(7)利用归并排序的便利性可以解决和诺问题 - 归并分治 - 下节课
	注意:
	有些资料说可以用原地归并排序,把额外空间复杂度变成O(1),不要浪费时间去学
	因为原地归并排序确实可以省空间,但是会把复杂度变成O(n^2)
	有关排序更多的概念,注意点,闭坑指南,将在后序课程继续
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
#include <malloc.h>
#define MAXI 501
int arr[] = { 6,4,2,3,9,4 };
int n = sizeof(arr) / sizeof(arr[0]);
int help[MAXI];

void merge(int left, int mid, int right) {
	int i = left;
	int a = left;
	int b = mid + 1;
	while (a <= mid && b <= right) {
		help[i++] = arr[a] <= arr[b] ? arr[a++] : arr[b++];
	}
	// 左侧指针,右侧指针,必有一个越界,另一个不越界
	while (a <= mid) {
		help[i++] = arr[a++];
	}
	while (b <= right) {
		help[i++] = arr[b++];
	}
	for (i = left; i <= right; i++) { // 把 help 里面的数据重新刷回到原数组arr
		arr[i] = help[i];
	}
}

/*
	归并排序递归版
	假设left...right一共 n 个数
	T(n) = 2 * T(n/2) + O(n)
	a = 2,b = 2,c = 1
	根据master公式,时间复杂度:O(n * logn)
	空间复杂度:O(n)
*/
// 递归方法
void mergeSort(int left, int right) {
	if (left == right) return;
	int mid = (left + right) / 2;
	mergeSort(left, mid);
	mergeSort(mid + 1, right);
	merge(left, mid, right);
}

// 归并排序非递归版
// 时间复杂度:O(n * logn)
// 空间复杂度:O(n)
void mergeSort2() {
	// 一共发生O(logn)次
	for (int left, mid, right, step = 1; step < n; step <<= 1) {
		// 内部分组merge,时间复杂度:O(n)
		left = 0;
		while (left < n) {
			mid = left + step - 1;
			if (mid + 1 >= n) {
				// 已经没有右侧了
				break;
			}
			// 有右侧,求右侧的右边界
			right = min(left + (step << 1) - 1, n - 1);
			// left ... mid mid+1 ... right
			//								left ... mid mid+1 ... right
			//															 left ... mid mid+1 ... right
			merge(left, mid, right);
			left = right + 1;
		}
	}	
}

int main() {
	//mergeSort(0, n - 1);
	mergeSort2();
	for (int i = 0; i < n; i++) {
		cout << " " << arr[i] << " " << endl;
	}
	system("pause");
	return 0;
}

完整图:

参考和推荐视频:

算法讲解021【必备】归并排序_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1wu411p7r7/?spm_id_from=333.999.list.card_archive.click&vd_source=a934d7fc6f47698a29dac90a922ba5a3

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

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

相关文章

Nacos使用指南

Nacos使用指南 1.认识Nacos Nacos是SpringCloudAlibaba的一个组件&#xff0c;遵循SpringCloud规范 2.Nacos的优势 1.支持服务端主动检测服务提供者状态。临时实例采用心跳检测&#xff0c;非临时实例采用主动检测 2.Nacos支持服务列表变更消息推送&#xff0c;消息更加及…

设计模式之建造者(Builder)

用来构建复杂对象 分离复杂对象的构建和表示 同样的构建过程可以创建不同的表示 当有一个复杂的对象&#xff0c;构建它的时候&#xff0c;比较复杂 例如&#xff0c;构建一个对象&#xff0c;要传入50个参数&#xff0c;可以先构建part1&#xff0c;再构建part2…最后调用bu…

Mysql数据库 14.SQL语言 视图

一、视图的概念 视图&#xff1a;就是由数据库中一张或多张表根据特定的条件查询出的数据狗造成的虚拟表 二、视图的作用 安全性&#xff0c;简单性 三、视图的语法 语法 create view 视图表 as select_statement; 代码实现 #创建视图 将查询结果创建称为视图&#x…

华为ipsec vpn双链路主备备份配置案例

配置就是这配置&#xff0c;已查官方也是这样配置&#xff0c;意外是完成后不通&#xff0c;待以后处理&#xff01; FW_A配置&#xff1a; dhcp enable ip-link check enable ip-link name check_b destination 2.2.2.2 interface GigabitEthernet1/0/0 mode icmp next-hop …

vue2+elementui使用MessageBox 弹框$msgbox自定义VNode内容:实现radio

虽说实现下面的效果&#xff0c;用el-dialog很轻松就能搞定。但是这种简单的交互&#xff0c;我更喜欢使用MessageBox。 话不多说&#xff0c;直接上代码~ <el-button type"primary" size"mini" click"handleApply()" >处理申请</el-b…

实操创建属于自己的亚马逊云科技VPS服务:Amazon Lightsail

本文主要讲述如何独立创建自己的亚马逊云科技VPS服务&#xff0c;希望此文能帮助你对亚马逊云科技VPS服务也就是Amazon Lightsail&#xff0c;有个新的认识&#xff0c;对所使用的VPS有所帮助。 Amazon Lightsail是一款无论云计算的新手还是专家&#xff0c;都可通过其快速启动…

MySQL中UUID主键的优化

UUID&#xff08;Universally Unique IDentifier 通用唯一标识符&#xff09;&#xff0c;是一种常用的唯一标识符&#xff0c;在MySQL中&#xff0c;可以利用函数uuid()来生产UUID。因为UUID可以唯一标识记录&#xff0c;因此有些场景可能会用来作为表的主键&#xff0c;但直接…

C++——基础

初学C的时候&#xff0c;有没有想过&#xff0c;为什么C支持重载&#xff0c;而C不支持重载呢&#xff1f;&#xff1f; 其实&#xff0c;一个程序运行起来都要经过四步骤 预处理编译汇编链接 预处理阶段会经过去注释&#xff0c;宏替换&#xff0c;头文件展开&#xff0c;条…

SPSS曲线回归

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件请点击此链接下…

RabbitMQ的 五种工作模型

RabbitMQ 其实一共有六种工作模式&#xff1a; 简单模式&#xff08;Simple&#xff09;、工作队列模式&#xff08;Work Queue&#xff09;、 发布订阅模式&#xff08;Publish/Subscribe&#xff09;、路由模式&#xff08;Routing&#xff09;、通配符模式&#xff08;Topi…

快速入门安装及使用git与svn的区别常用命令

一、导言 1、什么是svn&#xff1f; SVN是Subversion的简称&#xff0c;是一个集中式版本控制系统。与Git不同&#xff0c;SVN没有分布式的特性。在SVN中&#xff0c;项目的代码仓库位于服务器上&#xff0c;团队成员通过向服务器提交和获取代码来实现版本控制。SVN记录了每个…

C语言-for循环入门代码

#include <stdio.h>int main() {int count;for (count 0; count < 10; count){printf("1\n");}system("pause");return 0; }1 1 1 1 1 1 1 1 1 1 请按任意键继续. . .灵活的for循环 死循环 while(1) {... }#include <stdio.h> int main(…

千帆SDK开源到GitHub,开发者可免费下载使用!

目录 一、SDK的优势 二、千帆SDK&#xff1a;快速落地LLM应用 三、如何快速上手千帆SDK 1、SDK快速启动 快速安装 平台鉴权 如何获取AK/SK 以“Chat 对话”为调用示例 2. SDK进阶指引 3. 通过Langchain接入千帆SDK 为什么选择Langchain 开源社区 千帆社区 好消息&…

Java11新增特性

前言 在前面的文章中&#xff0c;我们已经介绍了 Java9的新增特性 和 Java10的新增特性 ,下面我们书接上文&#xff0c;来介绍一下Java11的新增特性 版本简介 Java 11 是 Java 平台的最新版本&#xff0c;于2018年9月25日发布。这个版本是自Java 8以来最重要的更新之一&…

n-gram语言模型——句子概率分布计算与平滑

n-gram语言模型——句子概率分布计算与平滑 前言 语言模型 等价假设 n元语法 句子概率分布计算方式 数据平滑 Lidstone平滑(1-gram) Laplace平滑(1-gram) 附上两种平滑在1-gram下代码 Lidstone平滑与Laplace平滑(2-gram) 附上两种平滑在2-gram下代码 前言 语言模型…

隧道施工工艺流程vr线上虚拟展示成为产品3D说明书

行业内都知道&#xff0c;汽车生产的大部分都需要冲压加工来完成&#xff0c;因此汽车冲压工艺是汽车制造过程中的重要环节&#xff0c;传统的展示方式往往局限于二维图纸和实地操作&#xff0c;难以充分展现工艺的细节和流程。然而&#xff0c;随着技术的进步&#xff0c;汽车…

Java类和对象(续)

书接上回我们已经学完了对象的初始化&#xff0c;今天的内容更加精彩。 1.封装 面向对象程序的三大特征&#xff1a;封装&#xff0c;继承&#xff0c;多态。 本章主要也是要研究封装&#xff0c;简单来说就是套壳屏蔽细节。 封装的概念&#xff1a; 封装&#xff1a;将数据和…

【PWN · ret2csu】[HNCTF 2022 WEEK2]ret2csu

记一道ret2csu 一、题目 二、思路 1.ret2csu用write泄露write的真实地址->泄露libc->获得system的真实地址 2.ret2csu用read写/bin/sh字符串到bss段上 3.ret2csu用write将system的真实地址写到bss段上 4.ret2csu调用system 三、exp from pwn import * from pwn impo…

[100天算法】-最短无序连续子数组(day 70)

题目描述 给定一个整数数组&#xff0c;你需要寻找一个连续的子数组&#xff0c;如果对这个子数组进行升序排序&#xff0c;那么整个数组都会变为升序排序。你找到的子数组应是最短的&#xff0c;请输出它的长度。示例 1:输入: [2, 6, 4, 8, 10, 9, 15] 输出: 5 解释: 你只需要…

this和super

文章目录 this用法普通的直接引用区分形参与实参 super用法普通的直接引用区分子类与父类同名的属性或方法 this和super 与 构造方法总结 this this引用表示当前对象对象的引用。 用法 普通的直接引用 public class Test {int a ;int b;public Test() {this.b 0;} }调用当…