2.1 Bubble Sort

本文介绍了一个关于冒泡排序的问题,并提供了两种解决方案:一种是直接模拟排序过程,另一种是利用归并排序进行优化,同时讨论了如何计算每个数字在排序过程中到达的最左和最右位置的差值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

★实验任务
给定一个1~N的排列P,即1到N中的每个数在P都只出现一次。 现在要对排列P进行冒泡排序,代码如下:
for (int i = 1; i <= N; ++i)
for (int j = N, t; j > i; ‐‐j)
if (P[j ‐ 1] > P[j])
t = P[j], P[j] = P[j ‐ 1], P[j ‐ 1] = t;
在排序过程中,数字的位置可能会发生变化。对于1~ N的每个数字,你需要输出过程中达到的最左位置下标和最右位置下标的差的绝对值。

★数据输入
第一行为N,表示排列长度。 第二行为排列P。 数据保证: 80%的数据,N <= 1000 100%的数据,N <= 100000

★数据输出
输出一行,第i个数字表示数字i最左与最右位置的差的绝对值。

★输入样例
4
3 2 1 4
★输出样例
2 1 2 0
★Hint
样例冒泡排序过程:
swap 2 1: 3 2 1 4 > 3 1 2 4
swap 3 1: 3 1 2 4 > 1 3 2 4
swap 3 2: 1 3 2 4 > 1 2 3 4

解题思路:两种做法:
①直接依题意模拟和更新状态
【TLE WARNING】 只能拿80分…因为是最简单的写法,AC做法是第二个
卡了无数天了没能用会线段树&树状数组

//Matsuri
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAX 200001

int N,P[MAX],l[MAX],r[MAX];
//l[i]返回i最左位置下标,r[i]返回i的最右位置下标 
 
int main()
{
	scanf("%d",&N);
	for (int i=1;i<=N;i++)
	{
		scanf("%d",&P[i]);
		l[P[i]]=i; //将每个数据的"最左"和"最右"位置信息初始化 
		r[P[i]]=i;
	}
	for (int i=1;i<=N;i++)
	{
		for (int j=N;j>i;j--)
        {
        	if (P[j-1]>P[j]) std::swap(P[j-1],P[j]); //每趟都把小的往前面丢  
		}  
		for (int j=1;j<=N;j++)
		{
			if (j<l[P[j]]) l[P[j]]=j;
			if (j>r[P[j]]) r[P[j]]=j; 
		}
	} 
    
    for (int i=1;i<=N;i++) printf("%d ",abs(l[P[i]]-r[P[i]]));
	
	return 0;
}

②归并排序优化【可AC】
经过多次尝试后发现,一个数在升序排序过程中能移动到多左边和多右边,取决于该数的右边有多少数据是小于它的。发现这一点之后,我们就能明白整个有序序列是由冒泡排序还是由其他排序方法得到的就无所谓了,观察数据规模,我们得用O(nlogn)或更优做法来解决本题,这里使用了归并排序。
pos[a]为数据a初始所在位置,cnt[a]为位于数据a右边,且小于数据a的数据的数量
a的最左位置下标:min(排序后a所在位置,排序前a所在位置)
a的最右位置下标:pos[a]+cnt[a]
该做法要求对归并排序过程熟悉

//Matsuri
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAX 200001

int N,P[MAX];
int pos[MAX],cnt[MAX];  

int min(int a,int b)
{
	return a<b?a:b;
}

void Merge(int a[],int L,int mid,int R)
{
	int len=R-L+1;
	int *tmp=new int[len],c=0;
	int i=L,j=mid+1;
	while(i<=mid && j<=R) 
	{
		if (a[i]<=a[j]) tmp[c++]=a[j++]; //降序排序 
		else
		{
			cnt[a[i]]+=R-j+1; //写成降序排序就是为了这一步,方便统计比a[i]小且位于a[i]后面的数据的数量 
			tmp[c++]=a[i++];
		} 
	}
	while (i<=mid) tmp[c++]=a[i++];
	while (j<=R) tmp[c++]=a[j++];
	for (int k=0;k<len;k++)
	{
		a[L++]=tmp[k];
	}
}

void MergeSort(int a[],int L,int R)
{
	if (L==R) return;
	int mid=(L+R)>>1;
	MergeSort(a,L,mid);
	MergeSort(a,mid+1,R);
	Merge(a,L,mid,R);
}

int main()
{
	scanf("%d",&N);
	for (int i=1;i<=N;i++)
	{
		scanf("%d",&P[i]);
		pos[P[i]]=i;
	}
	
	MergeSort(P,1,N);

    for (int i=1;i<=N;i++) printf("%d ",pos[i]+cnt[i]-min(i,pos[i]));
	
	return 0;
}

树状数组做法有缘再补上

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值