10-排序6 Sort with Swap(0, i)

  • 題目
    10-排序6 Sort with Swap(0, i) (25分)
  • 分析
    我剛開始想着直接排序。先試了幾個例子,經過總結之後,覺得步驟應該是這樣:
    1. 看0在不在位置0處(即得到0所在的位置pos0),如果不在,把0與應該在pos0位置處的數字交換,然後重複執行步驟1;如果在則向下執行。
    2. 看數組是否有序,如果數組無序,把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)。
發佈了90 篇原創文章 · 獲贊 46 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章