思路与总结:
-
这一排序方法的核心思想就是每次让一个数回到正确位置:
-
如样例初始状态
{4, 0, 2, 1, 3}
此时数字0
在①号位,而①号位本应该是数字1
,那么找到数字1
,发现它在③号位,故而两数交换位置 得到新数列 即Swap(0, 1)
=>{4, 1, 2, 0,3}
;此时数字1
在①号位,数字1
已归位。这一操作:交换次数cnt
+1,并归位一个数。接着重复这一过程。直至所有数字都归位。 -
但还有一个问题忽略了,那就是当数字
0
在0号位,如数列{0,1,2,4,3}
,那岂不是没的换了。此时应找到一个不在本位的数,与0
交换,如该例1
与2
都在正确位置,而3
与4
不在正确位置, 故交换0
和3
得{3,1,2,4,0}
。这一操作:交换次数cnt
+1,但并没有让某数正确归位。这时0
在④号位,故而下一步该0
与4
交换,让4
回到④号位(这一操作:交换次数cnt
+1,并归位一个数)。。。直至全部数字归位。 -
此题只需设置一个整形数组
pos[maxn]
,pos[i]
表示的是数字i
在几号位,如样例{4, 0, 2, 1, 3}
则初始状态pos[0] = 1, pos[1] = 3, pos[2] = 2, pos[3] = 4, pos[4] = 0;
-
还需设一个整形变量
rest
表除了数字0
以外还未归位的数,每当归位一个数,rest--
,当rest==0
,表明n个数全部归位。 -
还应设置一个整形变量
k
,可以设初值k=1
(初值为0也没问题)pos[k]==k
说明数字k
已归位,k
作为一个标记标量,用以在当“pos[0] == 0
无法进行交换时,寻找一个未归位的数,使得交换继续,交换pos[0]
与pos[k]
”,有了k
可以使每次寻找无需从头到尾遍历n个数,而每次只需从k开始向后寻找即可,使得总共寻找的时间复杂度变为O(n)(若不设置k每次遇到0在0号位的情况都取去遍历序列来寻找不在其位的数,会有测试点超时)。
AC代码
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 100010;
int pos[maxn] ;
int rest, n, k=0, cnt = 0;
int main(){
scanf("%d",&n);
int x;
rest = n-1;
for(int i=0; i<n; i++){
scanf("%d",&x);
pos[x] = i;
if(x!=0 && pos[x]==x) rest--;
}
while(rest>0){
if(pos[0]!=0){
swap(pos[0], pos[pos[0]]);
cnt++;
rest--;
}
else{
while(pos[k]==k){
k++; //从1开始枚举,找到不在本位的数字
}
swap(pos[0], pos[k]);
cnt++;
}
}
printf("%d\n",cnt );
return 0;
}