算法详细讲解- 快速排序与归并排序

快速排序

讲解

快速排序是基于分治来做的。

那么问题来了,分治又是什么呢?

分治,简单来说,就是"分而治之"的意思。基本思路是把一个大问题拆分成几个小问题,如果这些小问题还比较大,就继续拆,直到这些问题变得足够简单,可以直接解决。解决了所有的小问题之后,再把这些解决方案合并起来,就能解决原来的大问题了。

那分治又是怎么工作的?

基本来看大致是这么个流程:选择基准 -> 分割 -> 递归排序

  • 选择分界点:从数列中挑出一个元素作为分界点。
  • 分割:以分界点为界将序列一分为二。所有比基准小的元素放到基准前面,所有比基准大的元素放到基准后面(相同的数可以放到任一边)。
  • 递归排序:对两个子序列(一个是基准左边的序列,另一个是基准右边的序列)重复上述步骤,直到每个序列里只剩下一个元素或者为空为止。

通过这种方式,一个复杂的问题(给一组数排序)被分解成了一些简单的子问题(每次只关注于排序一个小的子序列),这正是分治思想的核心。然后,通过解决这些子问题,并将它们的结果合并起来,我们就能够解决原始的问题。

那分治就这么随意吗?当然不是,在实际情况会复杂许多。

首先就是确定分界点:首先我们可以想象一个数列,最左边界索引 为l,最右边界索引 为r。那么分界点就会有以下四种情况:q[l],q[r],q[(l+r)/2],随机

注意分界点不是一个数值,而是数组的索引。

所以就可以根据以上步骤总结出模板了。

暴力的模板

  1. 新开两个数组a与b,用于存储一分为二之后的子序列
  2. 将要分治操作的序列看作是新开的数组q
  3. 在数组q上确定分界点,也就是索引,并确定索引上的值
  4. 然后递归处理数组q,小于等于的数值放进数组a,大于等于的数值放进数组b
  5. 最后将a,b两个数组拼起来,放进数组q

这么做需要开辟一个额外空间。就不用演示了,没有技术含量。

做题模板

  1. 定义指针i指向数组最左边界l,定义指针j指向数组最右边界r,并定义分界点
  2. i指针一直往分界点走,直到遇见比分界点处的数大的数,停止,那这时候就应该把i指针所指的数移到分界点的右侧
  3. 那么这个时候i指针不动,移动j指针,j指针一直往分界点走,停止,直到遇见比分界点处小的数,那这时候就应该把j指针所指的数移到分界点的左侧
  4. 那么两个指针都停下来了,而且所指的数都是错位的,那么swap进行交换就可以,让错位的数对号入座
  5. 一直递归直到i,j指针相遇,就完成了

模板题

785. 快速排序 - AcWing题库

解法

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

const int N  = 100005;

int n;
int q[N];

void quick_sort(int q[], int l, int r) {
    // 1. 判断是否是有效区间:没有数或者是只有一个数
	if (l >= r) return; 
    // 两个指针需要先移动一次再去做判断(与下面的dowhile对应)
    // 所以需要有预先的偏移量(l-1与j+1)
    // 2. 确定指针与分界点
	int i = l - 1, j = r + 1, x = q[l + r >> 1]; 
    // 3. 使用dowhile指针正常移动,swap递归交换
	while (i < j) {
		do i++; while (q[i] < x);
		do j--; while (q[j] > x);
		if (i < j) swap(q[i],q[j]);
	}
    // 4. 传入左右两段,进行递归处理
	quick_sort(q, l, j);
	quick_sort(q, j+1, r);
    // quick_sort(q, l, i - 1);
	// quick_sort(q, i, r);
}

int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) scanf("%d", &q[i]);
	quick_sort(q, 0, n - 1); // 注意这里传入的是索引,所以要n - 1
	for (int i = 0; i < n; i++) printf("%d ", q[i]);
	return 0;
}

基本步骤

  1. 判断是否是有效区间:没有数或者是只有一个数

  2. 确定指针与分界点

  3. 使用dowhile进行指针的移动与比较,swap递归交换

  4. 传入左右两段,进行递归处理

注意事项

  • 因为两个指针需要先移动一次再去做判断(与下面的dowhile对应,所以需要有预先的偏移量(l-1与j+1)
  • do(i++); while (q[i] <= x);do(j--); while (q[j] >= x);这是错误的写法。

举个例子:传入的数组为[2, 2, 2, 2, 2],i 会一直增加,直到越界;j 会一直减少,直到越界,那么ij 永远不会停下来,或者最终 i 超出数组范围。

练习题

0实现快速排序 - 蓝桥云课

0卡java排序 - 蓝桥云课

归并排序

讲解

归并排序也是一种分治算法。它的基本思想是:

  1. :将原数组分成两个子数组;
  2. :递归地对子数组分别进行归并排序;
  3. :将两个有序子数组合并成一个有序数组。

模板题

787. 归并排序 - AcWing题库

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

const int N = 100005;
int n, q[N], tmp[N];

void merge_sort(int q[], int l ,int r) {
    // 1.判断是否是有效区间:一个数或者没有数
	if (l >= r) return;
	// 2.确定分界点:mid = (l+r)/2
	int mid = l + r >> 1;
	// 3.从分界点开始,将原数组分成两个子数组
	merge_sort(q, l, mid);
	merge_sort(q, mid + 1, r);
	// 4.定义两个数组分别对应的初始双指针与临时数组tmp的计数k
	int k = 0, i = l, j = mid + 1;
	// 5.双指针 i 和 j 分别遍历左右两个子数组,按顺序把较小的元素放入tmp
	while (i <= mid && j <= r) {
		if (q[i] <= q[j]) {
			tmp[k++] = q[i++];
		} else {
			tmp[k++] = q[j++];
		}
	}
	// 6.处理剩余元素(其中一个子数组可能还有未处理的元素)
	while (i <= mid) {
		tmp[k++] = q[i++];
	}
	while (j <= r) {
		tmp[k++] = q[j++];
	}
	// 7.将临时数组复制回原数组
	for (i = l, j = 0; i <= r; i++, j++) {
		q[i] = tmp[j];
	}
}

int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		scanf("%d", &q[i]);
	}
	merge_sort(q, 0, n - 1);
	for (int i = 0; i < n; i++) {
		printf("%d ", q[i]);
	}
	return 0;
}

基本步骤

1.判断是否是有效区间:一个数或者没有数

2.确定分界点:mid = (l+r)/2

3.从分界点开始,将原数组分成两个子数组

4.定义两个数组分别对应的初始双指针,以及临时数组的计数k

5.双指针遍历两个数组,按顺序把较小数放入临时数组

6.处理剩余元素

7.临时数组复制到原数组

注意事项

练习题

6.实现归并排序 - 蓝桥云课

快速排序与归并排序的区别

序算法 思想
快速排序 选择一个基准元素,将数组划分为两个子数组:左边小于等于基准,右边大于等于基准,然后递归地对子数组排序。
归并排序 将数组分为两个子数组,分别排序后合并(先递归排序,后合并)。
排序算法 划分方式
快速排序 原地划分不需要额外空间,通过交换元素完成划分。
归并排序 非原地划分需要一个临时数组来合并两个有序子数组。
排序算法 稳定性
快速排序 不稳定(交换可能破坏相同元素的相对顺序)
归并排序 稳定(合并时优先左边数组元素)
快速排序 归并排序
使用双指针 i 和 j 从两端向中间扫描,找到不符合条件的元素进行交换 拆分到最小单位后,再合并两个有序数组
不需要额外空间(原地排序) 需要一个临时数组 tmp[] 来合并数据
特性 快速排序 归并排序
时间复杂度 平均 O(n log n),最坏 O(n²) 始终 O(n log n)
空间复杂度 O(log n) O(n)
稳定性 不稳定 稳定
是否原地排序
适用场景 内排序、速度快 外排序、稳定排序
相关推荐
每天都在想吃啥10 分钟前
day22 哈希表和二叉树
数据结构·哈希算法·散列表
Das129 分钟前
【初识数据结构】CS61B 中的归并排序和选择排序
数据结构·算法·排序算法
yanchao_hu4 小时前
数据结构基本内容(第四篇:队列)
数据结构
香蕉可乐荷包蛋6 小时前
排序算法 (Sorting Algorithms)-JS示例
javascript·算法·排序算法
一只小风华~6 小时前
JavaScript:数组常用操作方法的总结表格
前端·javascript·数据结构·vue.js·算法
一匹电信狗7 小时前
【C++】手搓一个STL风格的vector容器
c语言·数据结构·c++·算法·leetcode·stl·visual studio
小小小白的编程日记7 小时前
C语言中的数据结构--栈和队列(2)
c语言·数据结构
李永奉7 小时前
C语言-数组:数组(定义、初始化、元素的访问、遍历)内存和内存地址、数组的查找算法和排序算法;
c语言·算法·排序算法
找不到、了8 小时前
Java排序算法之<希尔排序>
java·算法·排序算法