2024年寒假算法班集训day07-知识总结及题解(快排与归并)

概念

快速排序和归并排序是两种常用的高效排序算法,它们都采用分治策略,但在细节上有所不同:

快速排序:

原理:通过一个称为“基准”的元素将数组分为两个子数组,使得左边的元素都不大于基准,右边的元素都不小于基准,然后递归地在两个子数组上应用相同的过程。
优点:平均情况下非常快,时间复杂度为O(n log n)。
注意:最坏情况下(已排序或反向排序的数据)时间复杂度退化为O(n^2)。
归并排序:

原理:将数组分为两半,对每一半递归地应用归并排序,然后将排序好的两半合并成一个有序数组。
优点:性能稳定,时间复杂度始终为O(n log n),适合大数据量的排序。
注意:需要额外的内存空间来存储临时数组。
通过这节课,学生应能够理解这两种排序算法的工作原理,并在实际编程中正确选择并实现它们。

1、排身高-3107

在这里插入图片描述
快速排序是一种高效的排序算法,它采用分治法的思想,通过一个轴点(pivot)将数组分为两部分,然后独立对这两部分进行排序。具体过程包括选择一个轴点,通常是数组的第一个或最后一个元素,然后重新排列数组,使得轴点左边的所有元素都不大于轴点,轴点右边的所有元素都不小于轴点,最后递归地在轴点两边的子数组上重复这个过程。

题目分析:

我们需要使用快速排序算法来对一组小动物的身高进行排序。

输入描述解析:

  • 第一行是一个整数 n,代表有 n 个身高需要排序。
  • 第二行包含 n 个整数,每个整数代表一个小动物的身高。

输出描述解析:

  • 输出是一行,包含升序排列的身高,数字之间用一个空格隔开。

代码解析:

void qsort(int left,int right){
   
	int x=a[left],l=left,r=right;
	if(l>=r)	return;
	while(l<r){
   
		while(l<r && a[r]>=x)	r--;
		a[l]=a[r];
		while(l<r && a[l]<=x)	l++;
		a[r]=a[l];
	}
	a[r]=x;
	qsort(left,r-1);
	qsort(r+1,right);
}
  • qsort 函数是快速排序的核心。函数接收两个参数 leftright,表示要排序的数组段的起始和终止位置。
  • x 是基准值,这里取的是每个数组段的第一个元素 a[left]
  • lr 是两个指针,分别从数组的两端开始向中间移动,交换元素以确保基准值左边的元素都不大于它,右边的元素都不小于它。
  • l >= r 时,递归结束。
  • 在每次循环中,右指针 r 向左移动,直到找到一个小于基准值的元素。然后将该元素复制到左指针 l 的位置。
  • 左指针 l 向右移动,直到找到一个大于基准值的元素。然后将该元素复制到右指针 r 的位置。
  • 当左右指针相遇时,将基准值复制到该位置,此时基准值的位置固定。
  • 递归调用 qsort 函数,对基准值左边和右边的子数组进行排序。

main 函数中,我们首先读取 n 的值,然后读取 n 个身高值存入数组 a。接着,我们调用 qsort 函数对整个数组进行排序,并最终输出排序后的数组。

性能考量:

快速排序的平均时间复杂度为 (O(n \log n)),在大多数情况下非常高效。对于小规模的数据(如本题中的 n<1000),快速排序通常表现良好。

示例输出解释:

对于样例输入:

6
5 6 2 9 8 1

排序后的输出为:

1 2 5 6 8 9

每个身高值都按照从低到高的顺序排列,且每个数值之间有一个空格。

#include<iostream>
using namespace std;
int a[10001];//存储序列
void qsort(int left,int right){
   //自定义快速排序函数
	int x=a[left],l=left,r=right;//x:取第一个数字为基准值 l:最左边数字下标  r:最右边数字下标
	if(l>=r)	return ;//递归结束条件
	while(l<r){
   //移动元素
		while(l<r && a[r]>=x)	r--;
		a[l]=a[r];
		while(l<r && a[l]<=x)	l++;
		a[r]=a[l];
	}
	a[r]=x;//固定基准值
	qsort(left,r-1);//基准值左边排序
	qsort(r+1,right);//基准值右边排序
}
int main(){
   
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)	cin>>a[i];
	qsort(1,n);//调用快排函数
	for(int i=1;i<=n;i++)	cout<<a[i]<<" ";
	return 0;
}

2、兔子排行榜-2938

在这里插入图片描述
题目要求我们对兔子王国的小兔子们按总成绩进行排序,然后输出前k名的小兔子的名字和成绩。由于成绩都是整数,并且如果成绩相同,则按照输入的顺序输出。这里可以使用归并排序算法来实现。

题目分析:

归并排序是一个有效的排序算法,特别适合于大量数据的排序,其基本思想是将数组分成两半,递归地对它们进行归并排序,然后将结果合并起来。在合并过程中,如果两个子数组的头部元素相比,我们总是选择较大的元素先放入辅助数组中,这样可以确保排序的稳定性,即相同成绩的兔子会保持它们原始的顺序。

代码解析:

在代码中定义了一个结构体 node 来存储每个兔子的名字和成绩。man 数组用于存储所有兔子的信息,而 tmp 数组用于在归并过程中临时存储数据。

struct node{
   
	string name;
	double score;
}man[100010],tmp[100010];

merge 函数是归并排序中的合并步骤,它将两个已排序的子数组合并为一个已排序的数组:

void merge(long long p,long long q){
   
	// ...
	while(first<=mid&&second<=q){
   
		if(man[first].score>=man[second].score){
   
			// ...
		}else{
   
			// ...
		}
	}
	// ...
	for(int i=0;i<q-p+1;i++){
   
		man[p+i].score=tmp[i].score;
		man[p+i].name=tmp[i].name;
	}
}

partition 函数是归并排序的拆分步骤,它递归地将数组分成越来越小的部分,直到每个部分只有一个元素,然后开始合并它们:

void partition(long long p,long long q){
   
	// ...
	if(mid>p) partition(p,mid);
	if(mid+1<q) partition(mid+1,q);
	merge(p,q);
}

main 函数中,首先读取兔子的数量 n 和要输出的前 k 名的数量,然后读取每个兔子的名字和成绩,存入 man 数组。调用 partition 函数对整个数组进行排序,最后输出前 k 名兔子的名字和成绩。

性能考量:

归并排序的时间复杂度为 ( O(n \log n) ),这对于题目中的数据范围(1万到10万)是足够的。代码使用了递归,对于较大的数据量,递归的深度也是可以接受的。

示例输出解释:

对于样例输入:

3 2
xiaotong 100
xiaocheng 93
xiaomei 99

我们需要对这三个兔子按成绩进行排序,并输出前2名。排序后的数组应该是:

xiaotong 100
xiaomei 99
xiaocheng 93

因此,对于这个输入,程序应该输出:

xiaotong 100
xiaomei 99

这代表成绩最高的两个兔子及其成绩。注意,即使归并排序是稳定的,但这里代码中的 score 字段是 double 类型的,理应是 int,因为成绩是整数,这是代码中的一个小错误。不过,这不会影响排序的结果,因为在这个情境中,成绩是唯一的。

#include<bits/stdc++.h>
using namespace std;
struct node{
   
	string name;
	double score;
}man[100010],tmp[100010];
void merge(long long p,long long q){
   //拆分子函数传值的下标
	//mid中间值,first左区间,second右区间
	long long mid=(p+q)/2,t=-1,first=p,second=mid+1;
	while(first<=mid&&second<=q){
   //左右区间都有值
		if(man[first].score>=man[second].score){
   //左区间按序跟右区间比较
			tmp[++t].score=man[first].score;//小于等于右区间的数进入辅助数组
			tmp[t].name=man[first++].name;
		}else{
   
			tmp[++t].score=man[second].score;//否则右区间的数进入辅助数组
			tmp[t
  • 20
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值