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

前置知识:讲解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] , 进行归并排序

呵呵哒的瞎想(可以不看):f(0,0)是6,f(1,1)是4,只有获得了这两个数值,才能合并成f(0,1),也就是[4,6]。而这个过程正好是后序遍历。左右中

cpp 复制代码
// 递归方法
void mergeSort(vector<int>& arr, int left, int right) {
	if (left == right) return;
	int mid = (left + right) / 2;
	mergeSort(arr, left, mid);      // 左
	mergeSort(arr, mid + 1, right); // 右
	merge(arr, left, mid, right);   // 中
}
  • 挑其中一步来演示:把[2,4,6]和[3,4,9]合并(merge)

最后再刷回原数组

cpp 复制代码
void merge(vector<int>& arr,int left, int mid, int right) {
	int n = right - left + 1;
	vector<int> help(n,0);
	int i = 0;
	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 = 0; i <n; i++) { // 把 help 里面的数据重新刷回到原数组arr
		arr[i+left] = help[i];
	}
}

(1)归并排序递归版

cpp 复制代码
// 递归方法
void mergeSort(vector<int>& arr, int left, int right) {
	if (left == right) return;
	int mid = (left + right) / 2;
	mergeSort(arr, left, mid);
	mergeSort(arr, mid + 1, right);
	merge(arr, left, mid, right);
}

(2)归并排序非递归版

cpp 复制代码
// 归并排序非递归版
// 时间复杂度:O(n * logn)
// 空间复杂度:O(n)
void mergeSort2(vector<int>& arr) {
	int n = arr.size();
	// 一共发生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(arr,left, mid, right);
			left = right + 1;
		}
	}	
}

完整代码:

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

void merge(vector<int>& arr,int left, int mid, int right) {
	int n = right - left + 1;
	vector<int> help(n,0);
	int i = 0;
	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 = 0; i <n; i++) { // 把 help 里面的数据重新刷回到原数组arr
		arr[i+left] = 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(vector<int>& arr, int left, int right) {
	if (left == right) return;
	int mid = (left + right) / 2;
	mergeSort(arr, left, mid);
	mergeSort(arr, mid + 1, right);
	merge(arr, left, mid, right);
}

// 归并排序非递归版
// 时间复杂度:O(n * logn)
// 空间复杂度:O(n)
void mergeSort2(vector<int>& arr) {
	int n = arr.size();
	// 一共发生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(arr,left, mid, right);
			left = right + 1;
		}
	}	
}

int main() {
	vector<int> arr = { 6,4,2,3,9,4};
	int n = arr.size();
	mergeSort(arr, 0, n - 1);
	//mergeSort2(arr);
	for (int i = 0; i < n; i++) {
		cout << " " << arr[i] << " " << endl;
	}
	system("pause");
	return 0;
}

完整图:

参考和推荐视频:

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

相关推荐
Kenneth風车12 分钟前
【机器学习(九)】分类和回归任务-多层感知机(Multilayer Perceptron,MLP)算法-Sentosa_DSML社区版 (1)11
算法·机器学习·分类
最后一个bug17 分钟前
rt-linux中使用mlockall与free的差异
linux·c语言·arm开发·单片机·嵌入式硬件·算法
蹉跎x1 小时前
力扣1358. 包含所有三种字符的子字符串数目
数据结构·算法·leetcode·职场和发展
rainoway2 小时前
CRDT宝典 - yata算法
前端·分布式·算法
坊钰2 小时前
【Java 数据结构】移除链表元素
java·开发语言·数据结构·学习·链表
巫师不要去魔法部乱说2 小时前
PyCharm专项训练4 最小生成树算法
算法·pycharm
IT猿手3 小时前
最新高性能多目标优化算法:多目标麋鹿优化算法(MOEHO)求解GLSMOP1-GLSMOP9及工程应用---盘式制动器设计,提供完整MATLAB代码
开发语言·算法·机器学习·matlab·强化学习
阿七想学习3 小时前
数据结构《排序》
java·数据结构·学习·算法·排序算法
王老师青少年编程3 小时前
gesp(二级)(12)洛谷:B3955:[GESP202403 二级] 小杨的日字矩阵
c++·算法·矩阵·gesp·csp·信奥赛
Kenneth風车3 小时前
【机器学习(九)】分类和回归任务-多层感知机(Multilayer Perceptron,MLP)算法-Sentosa_DSML社区版 (1)111
算法·机器学习·分类