案例7-1.5:与零交换

本文探讨了一种独特的排序问题,要求仅通过与0交换的方式将给定的非负整数排列升序,通过环形结构分析和策略优化,提出一种高效算法,解决了如何找到最少交换次数的问题。讨论了含零闭环与不含零闭环的处理方式,给出了代码示例和关键步骤的解释。
摘要由CSDN通过智能技术生成

将 { 0, 1, 2, …, N-1 } 的任意一个排列进行排序并不困难,这里加一点难度,要求你只能通过一系列的 Swap(0, *) —— 即将一个数字与 0 交换 —— 的操作,将初始序列增序排列。例如对于初始序列 { 4, 0, 2, 1, 3 },我们可以通过下列操作完成排序:
Swap(0, 1) ⟹ { 4, 1, 2, 0, 3 }
Swap(0, 3) ⟹ { 4, 1, 2, 3, 0 }
Swap(0, 4) ⟹ { 0, 1, 2, 3, 4 }
本题要求你找出将前 N 个非负整数的给定排列进行增序排序所需要的最少的与 0 交换的次数。

输入格式
输入在第一行给出正整数 N (≤10
5
);随后一行给出{ 0, 1, 2, …, N-1 } 的一个排列。数字间以空格分隔。
输出格式
在一行中输出将给定序列进行增序排序所需要的最少的与 0 交换的次数。
输入样例
10
3 5 7 2 6 4 9 0 8 1
输出样例
9

这个题目比较有意思,复述一遍题目就是只能通过交换零与其他数字使序列递增

移动的方法肯定是将零现在所在的位置换成原本应该在这里的数值,这样重复交换。但是原本位于零所在位置的数值在哪里?那就要通过查找,每次交换都需要查找,这样会很浪费时间。
代码就不贴出来了,直接超时(😂)

以 4 0 2 1 3为例:

在这里插入图片描述
我们可以发现交换的过程实际上是构成环的过程


0 -> 1 ->3 ->4 的过程

究其超时的原因,主要是每次查找浪费太多的时间。所以我们考虑,能否不查找直接确定交换的位置呢?

在这里插入图片描述
我们再来看这组数据:以结果论为出发点,4所在的0号位置一定是最后一次交换的! 为什么呢? 我们要计算交换的最少次数,那么肯定最后一步是将数字0交换到0号位置,即交换0,4是最后一步。那么倒数第二步是什么呢?同理,因为最后一步交换0,4,那么我们考虑4的位置,4的位置目前被3所占据,那么倒数第二步就是0与3交换,同理3的位置被1所占据,那么就是0与1交换喽。
即 0->4->3->1->0 形成闭环,交换次数为 4-1=3,即环个数减1

但是又有一种情况:(不画图了,画图太累了)
0 1 2 3 4 5 6 7 8 9
3 5 7 2 6 4 9 0 8 1
这组数据,我们只有发现 0->3->2->7->0 形成闭环,剩下的数据为另一个闭环,怎么办呢?当对含零闭环交换完成后,我们可以通过交换0与另一个闭环里的任意一个数使0进入新的闭环进行新的交换!

针对不含零闭环,我们假设环大小为n,加入零后,大小变为n+1,需要交换 n+1-1 = n次,由于之前把零交换到非零环中,交换次数加1,所以总次数为n+1

接下来上代码:

#include<iostream>
#include<vector>
using namespace std;
int n;
vector<int> v;
vector<bool>visited;
int main()
{
	int temp = 0,times =0;
	cin >> n;
	v.resize(n);
	visited.resize(n, 0);
	for (int i = 0; i < n; i++)
		cin >> v[i];
	for (int i = 0; i < n; i++)
	{
		//遍历一次数组即可
		//如果当前的数没有被访问过且位置不对
		//则要寻找和他相互连接的环
		if (!visited[i] && v[i] != i)
		{
			bool flag = 0;
			//是否含零
			int t = i;
			temp = 0;
			while (!visited[t])
			{
				if (v[t] == 0)
					flag = 1;
				visited[t] = 1;
				t = v[t];
				temp++;
				//统计每次环的个数
			}
			if (flag)
				times += temp - 1;
			else times += temp + 1;
		}
		else visited[i] = 1;
	}
	cout << times;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Agreenhan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值