- 題目
10-排序6 Sort with Swap(0, i) (25分) - 分析
我剛開始想着直接排序。先試了幾個例子,經過總結之後,覺得步驟應該是這樣:
- 看0在不在位置0處(即得到0所在的位置pos0),如果不在,把0與應該在pos0位置處的數字交換,然後重複執行步驟1;如果在則向下執行。
- 看數組是否有序,如果數組無序,把0與第一個無序的數字交換,然後執行步驟1;如果有序則停止。
在上述執行中,記錄swap0i的次數。
- 代碼
#include <stdio.h>
int num[100000];
int find(int x,int n){
for(int i=0; i<n; i++){
if(num[i] == x) return i;
}
return -1;
}
void swap0i(int pos0,int i, int n){
int tmp = num[i];
num[i] = num[pos0];
num[pos0] = tmp;
}
//返回-1表明有序了,否則返回第一個不是正確數的位置
int isSorted(int n){
int i;
for(i=0; i<n; i++){
if(num[i] != i) return i;
}
return -1;
}
int main(int argc, const char * argv[]) {
int n,i,ans = 0;
int pos0 = 0,loc;
scanf("%d",&n);
for(i=0; i<n; i++){
scanf("%d",&num[i]);
//if(num[i] == 0) pos0 = i;
}
while ((loc =isSorted(n)) != -1) {
pos0 = find(0, n);
if(pos0 == 0){
swap0i(pos0,loc, n);
ans++;
}
while (pos0 != 0) {
loc = find(pos0, n);
swap0i(pos0,loc, n);
ans++;
pos0 = loc;
}
}
printf("%d\n",ans);
return 0;
}
- 運行結果
- 算法複雜度分析
有兩層循環(while),最裏面的時間複雜度loc = find(pos0, n)是O(N),所以大概的時間複雜度是O(N^3)。所以肯定會超時。 - 改進一:以空間換時間,把每一個數的位置直接保存在一個數組中,這樣loc = find(pos0, n)就可以變成O(1)的時間複雜度。
#include <stdio.h>
int num[100000];
int pos[100000];
void swap0i(int pos0,int i, int n){
int tmp = num[i];
num[i] = num[pos0];
num[pos0] = tmp;
pos[0] = i;
pos[tmp] = pos0;
}
//返回-1表明有序了,否則返回第一個不是正確數的位置
int isSorted(int n){
int i;
for(i=0; i<n; i++){
if(num[i] != i) return i;
}
return -1;
}
int main(int argc, const char * argv[]) {
int n,i,ans = 0;
int pos0 = 0,loc;
scanf("%d",&n);
for(i=0; i<n; i++){
scanf("%d",&num[i]);
pos[num[i]] = i;
//if(num[i] == 0) pos0 = i;
}
while ((loc =isSorted(n)) != -1) {
pos0 = pos[0];
if(pos0 == 0){
swap0i(pos0,loc, n);
ans++;
}
while (pos0 != 0) {
loc = pos[pos0];
swap0i(pos0,loc, n);
ans++;
pos0 = loc;
}
}
printf("%d\n",ans);
return 0;
}
- 運行結果
- 算法複雜度分析
有兩層循環(while),最裏面的時間複雜度是O(1),所以大概的時間複雜度是O(N^2)。所以還是會超時。 - 再次分析
我參考了浙江大學陳姥姥的講課內容,需要用到一個結論:
N個數字的排列是有若干個獨立的環組成。
其實我們通過上述的步驟,就可以很容易上圖中的意思。
同時注意如果多元環中都沒有0,那麼總的交換次數就等於(假設n個元素中有S個單元環,K個多元環):
每個多元環的交換次數爲Ni+1,全部加起來就是n-S+K。
現在我們只需要直接統計各種環的個數了。
#include <stdio.h>
#define maxn 100000
int num[maxn];
int flag[maxn]; //flag[i]用來標記第i個位置是否被遍歷過
int pos[maxn]; //pos[i]用來標記數字i在num[]中的哪個位置
int main(int argc, const char * argv[]) {
int n,i,ans = 0;
int S = 0, K = 0;
scanf("%d",&n);
for(i=0; i<n; i++){
//初始化三個數組
scanf("%d",&num[i]);
flag[i] = 0;
pos[num[i]] = i;
//順便直接統計單元素環
if(num[i] == i){
S++;
flag[i] = 1;
}
}
int beginNum,tmp;
for(i=0; i<n; i++){
if(flag[i]) continue;
else{
K++;
beginNum = num[i];
flag[beginNum] = 1;
flag[i] = 1;
tmp = i;
while ((tmp=pos[tmp]) != beginNum) {
flag[tmp] = 1;
}
}
}
if(num[0] == 0){
ans = n-S+K;
}else{
ans = n-S+K-2;
}
printf("%d\n",ans);
return 0;
}
- 運行結果
-
明顯所花費的時間短多了。 - 時間複雜度:
我們只對整個數組進行了一次遍歷,所以時間複雜度是O(N)。