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

前置知识:讲解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,63,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

相关推荐
Jerry1 小时前
LeetCode 28. 找出字符串中第一个匹配项的下标
算法
Jerry3 小时前
LeetCode 459. 重复的子字符串
算法
海石6 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石6 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
Jerry8 小时前
LeetCode 151. 反转字符串中的单词
算法
gugucoding9 小时前
31. 【C语言】堆栈与队列的实现
c语言·开发语言·数据结构·链表
ChaoZiLL10 小时前
我的数据结构3——链表(link list)
数据结构·链表
a11177610 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜10 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry11 小时前
LeetCode 541. 反转字符串 II
算法