數組的循環移動

數組的循環移動是考查的算法中比較常見的,有循環左移,循環右移之類的。當然,不會直接就考查移動的算法,還會有一定的時間複雜度,空間複雜度的要求之類的,這纔是算法要求的,所以這兩天剛好看到類似的題目,就記錄一下。

題目要求


如有數組abcd1234,讓數組向右邊移動兩位,變成了34abcd12。這就是數組的循環右移操作,那麼要求在儘可能少的時間複雜度下實現將整個數組移動m次的這個操作。


不考慮時間複雜度的實現

對於這個移動,我們可以考慮將整個數組一次一次的移動,移動m次。實現代碼如下:

for(int i = 0;i < m;i++){
    int temp = a[n-1];
    int j;
    for(j = n-2;j >= 0;j--){
        a[j+1] = a[j];
    }//當j=0的時候結束循環,但是還是會減一
    a[j+1] = temp;
}

上訴的代碼就是最容易實現的,內層循環是實現一次移動,外層循環加上之後實現m次移動,時間複雜度爲O(m*n).

考慮時間複雜度的實現

我們還是把字符串看成有兩段組成的,記位XY。左旋轉相當於要把字符串XY變成YX。我們先在字符串上定義一種翻轉的操作,就是翻轉字符串中字符的先後順序。把X翻轉後記爲XT。顯然有(XT)T=X。

我們首先對X和Y兩段分別進行翻轉操作,這樣就能得到XTYT。接着再對XTYT進行翻轉操作,得到(XTYT)T=(YT)T(XT)T=YX。正好是我們期待的結果。

分析到這裏我們再回到原來的題目。我們要做的僅僅是把字符串分成兩段,第一段爲前面N-M個字符,其餘的字符分到第二段(剩餘M個字符)。再定義一個翻轉字符串的函數,按照前面的步驟翻轉三次就行了,就是先翻轉前面一段字符,再翻轉後面一段字符,最後翻轉整個字符串。這樣的時間複雜度和空間複雜度都合乎要求。
實現代碼如下:

//翻轉函數
void reverse(int *a,int start,int end);
{
    int temp;
    for(;start<end;start++,end--){
        temp = a[end];
        a[end] = a[start];
        a[start] = temp;
    }
}
//主函數
void shift(int *a,int n,int m)
{
    m = m % n;
    reverse(a,0,n-m-1);//前面部分逆序
    reverse(a,n-m,n-1);//後面m個字符逆序
    reverse(a,0,n-1);//整體逆序
}

這樣只會就能實現時間複雜度爲O(n)的算法了,這裏其實用到了漢諾塔的類似的思想。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章