题目
https://pintia.cn/problem-sets/994805342720868352/problems/994805403651522560
逻辑解析
创建一个哈希数组a,用以存储每个数字所在位置(下标)
题目中是以0为基点将每个数字送回它所在的位置,但我们不可能每次都遍历一遍数组找到当前的0应该和谁交换(时间复杂度太高),所以需要设立交换区,而在本题中a[0]这个位置就是交换区
下面声明几个法则(也就是默认正确的逻辑):
法则一:当交换区a[0]=0时,表示一轮交换结束,0回到它应有的位置a[0]上
法则二:a[i]!=i表示当前位置不符合条件,需要到交换区等待交换
最后一轮发现最后一个位置a[n-1]的数还不符合条件,那一定说明只剩下a[n-1]和交换区a[0]不符合条件,将二者通过法则二交换就能直接满足条件,在此特别声明
那么我来解释一下下面的代码逻辑:既然a[0]是交换区,那就从a[1]开始比较,如果a[0]!=0,那就通过swap(a[0],a[a[0]])
将交换区的数交换到符合条件的位置上,直至a[0]=0,由法则一可知这轮交换结束
如果a[i]!=i
,说明在上一轮交换中没有将当前位置的数交换回来,那就将当前位置的数送到交换区等待下一轮交换(法则二),进入到交换区a[0]位置的非0数一定会在下一轮中通过swap(a[0],a[a[0]])的方式回到它应有的位置上(因为满足a[0]!=0)
注意,在上述逻辑中,0只是代表着一轮交换的结束,即使它又被交换出去,也会在最后交换回来
AC代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,t,cnt=0,a[100005];//a为哈希数组
cin>>n;
for(int i=0;i<n;i++)
{
cin>>t;
a[t]=i;//存储每个数字的下标
}
for(int i=1;i<n;i++)
{
if(a[i]!=i)//数字与下标不一致
{
while(a[0]!=0)
{
swap(a[0],a[a[0]]);
cnt++;
}
if(a[i]!=i)
{
swap(a[0],a[i]);
cnt++;
}
}
}
cout<<cnt;
}