[基础算法]快速排序 归并排序

  1.快速排序—分治


  1. 确定分界点:一般是取中间的点。
  2. 调整区间:左区间所有的数都小于等于x,右区间所有的数都大于等于x。
  3. 递归处理左右两段。

解法: 

#include <iostream>

using namespace std;

const int N = 1e6+10;

int n;
int q[N];

void quick_sort(int q[], int l, int r) {
	if (l >= r) return;
	int x = q[l+((r-l)>>1)], i = l - 1, j = r + 1;
	while (i < j) {
		do i++; while (q[i] < x);
		do j--; while (q[j] > x);
		if (i < j) {
			swap(q[i], q[j]);
		}
		
	}

	quick_sort(q, l, j);
	quick_sort(q, j+1, r);

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

	quick_sort(q, 0, n - 1);

	for (int i = 0; i < n; i++) printf("%d ", q[i]);
	
	return 0;
}

//另一种写法
#include <iostream>

using namespace std;

const int N = 1e6+10;

int n;
int q[N];

void quick_sort(int q[], int l, int r) {
	if (l >= r) return;
	int x = q[l+r+1>>1)], i = l - 1, j = r + 1;
	while (i < j) {
		do i++; while (q[i] < x);
		do j--; while (q[j] > x);
		if (i < j) {
			swap(q[i], q[j]);
		}
		
	}

	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);

	for (int i = 0; i < n; i++) printf("%d ", q[i]);
	
	return 0;
}

平均时间复杂度:nlog(n);

边界情况分析:快速排序为分治排序,若分治后还出现(l,r)的情况,会无限递归

分析一: 以j为划分时,x不能选q[r]

               以i为划分时,x不能选q[l] 

例:若以j为划分,x选q[r], 当q[l ~r-1]<x时,j会取到r,此时quick_sort(q,l,j)即quick_sort(q,l,r)会造成无限递归。所以选x的时候,x=q[l+r<<1],x不能取q[l+r+1<<1],因为l+r+1为向上取整,会取到q[r]

若以i为划分,x选q[l],当q[l+1~r]>x时,i会取到l,此时quick_sort(q,i,r)即为quick_sort(q,l,r)会造成无限递归。所以选x的时候,x=q[l+r+1<<1],x不能取q[l+r<<1],因为l+r为向下取整,会取到q[l]

分析二:结束一次循环时,q[j]<=x,q[i]>=x

        因为j和i越来越逼近,每一次排好序之后从i++,j++开始排序,他们一定会在同一个数相遇,若这个数正好为x,则i和j移动到此处停止,i等于j;若这个数不为x这个数肯定不可能既满足x大于x又满足小于x,i和j肯定会移动其中一个,从而结果j肯定会小于i,而不会等于i,i对应的肯定是大于等于x的,j对应的值肯定是小于等于x的。又因为j(不包括j)右边所有的数都满足大于等于j,而i(不包括i)左边所有的数都满足小于等于x,所以在传参时,传入(l,j),(j+1)或者(l,i-1),(i,r)。

分析三:不能为while(q[i]<=x)和while(q[j]>=x) 

        若q[l~r]全部相同,则循环的过程中,若q[i]>=x,i会一直自增,数组会越界,若越界后的数组仍然满足q[i]>=x的话,对于一个大数组肯定会TLE。


2.归并排序—分治


  1. 确定分界点,取中间点 
  2. 递归排序
  3. 归并—合二为一

解法:

#include <iostream>

using namespace std;

const int N = 1000000;

int n;
int q[N], temp[N];

void merge_sort(int q[], int l, int r) {
	if (l >= r) return;

	int mid = l+r>>1;

	merge_sort(q, l, mid);
	merge_sort(q, mid + 1, r);

	int k = 0, i = l, j = mid + 1;
	while (i <=mid && j <= r)
		if (q[i] <= q[j]) temp[k++] = q[i++];
		else temp[k++] = q[j++];
	while (i <= mid) temp[k++] = q[i++];
	while (j <= r) temp[k++] = q[j++];

	for (i = l, j = 0; i <= r; i++, j++) q[i] = temp[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;
}

平均时间复杂度:log(n);

       归并算法就是取数组的中间下标,定义两个指针分别指向左区间和右区间的第一个元素,然后将左区间和右区间的数一个一个地进行比较,将较小值存入临时数组,并将该指针右移一位,另一指针不移动。若一个区间遍历完毕,另一个区间还剩下数,则将剩下的数直接放入临时数组即可。这里递归的作用其实就是将数组的数由少至多排好序,因为栈顶是两个数的排序,这两个数在经过归并之后出栈,此时两个数的序列就排好了,接下来可能就轮到另外两个数或者三个或者四个数进行排序并归,那么越来越多的数就被排列好了,随着merge-sort方法的栈顶一个一个出栈,数组就逐渐了完成排列。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值