关于排序中最少交换次数的证明(置换环)

关于排序中最少交换次数的证明(置换环)

利用置换环可以进行证明。这里我只做感性的证明。

问题

对于长度为 n n n的数组 a a a a i ∈ N a_i\in N aiN a i a_i ai互不相同,每次操作可以选择任意两个位置 i , j i,j i,j交换 a i , a j a_i,a_j ai,aj,要求排序的最少交换次数。

答案

最少交换次数 c n t s w a p = n − c n t c i r c l e cnt_{swap}=n-cnt_{circle} cntswap=ncntcircle

c n t c i r c l e cnt_{circle} cntcircle 就是数组中置换环的个数。

方法

首先我们对数组进行排序,然后进行映射到 [ 1 , n ] [1,n] [1,n]这个区间。

比如 m ( a j ) = i m(a_j)=i m(aj)=i, 说明 a j a_j aj这个元素最终的位置应该在 i i i。可以理解为 j j j i i i连一条有向边。

同时再开一个数组标记位置是否访问过。我们利用 m m m数组暴力找环的个数即可。

时间复杂度: O ( n ) O(n) O(n)

int getSwapCnt(vector<int>&a){
	int n = a.size(),cnt=0;
	vector<int>b(a);
	sort(b.begin(),b.end());
	unordered_map<int,int>m;
	vector<bool>vis(n);
	for(int i=0;i<n;i++) m[b[i]] = i;
	for(int i=0;i<n;i++)
		if(!vis[i]){
			int j = i;
			while(!vis[j]){
				vis[j] = true;
				j = m[a[j]];
			}
			cnt++;
		}
	return cnt;
}

下面这个写法是反向的一个建边,可以允许 a i a_i ai相同。

int a[N],b[N],n;
bool vis[N];
bool cmp(int &x,int &y){
	return a[x]<a[y];
}
int getSwapCnt(){
	int cnt=0;
	for(int i=1;i<=n;i++) b[i] = i;
	sort(b+1,b+n+1,cmp);
	for(int i=1;i<=n;i++)
		if(!vis[i]){
			int j = i;
			while(!vis[j]){
				vis[j] = true;
				j = b[j];
			}
			cnt++;
		}
	return cnt;
}

简要证明

显然如果有 n n n个环的话,说明此时已经排序好了。 c n t = n − n = 0 cnt=n-n=0 cnt=nn=0

那么我们的目标就是要达到 n n n个环。

每次我们可以选择一个环中的两个结点进行交换,这样环的个数会加1,变成两个。如下图所示:

swap-node

那么既然每次操作环个数加1,我们已经有 m m m个环了,那么要达到 n n n个环,显然最少次数是 n − m n-m nm次操作,加 n − m n-m nm个环。

所以答案就是 n − m n-m nm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

酷酷的Herio

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

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

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

打赏作者

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

抵扣说明:

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

余额充值