閱讀162 返回首頁    go 阿裏雲 go 技術社區[雲棲]


劍指Offer詳解之左旋轉字符串

(1)暴力移位法

這種方法可能是最直觀,最容易想出的方法。但這也是最壞的方法。時間複雜度挺高,用這種方法容易超時。
這種方法是一位一位的移動實現左旋轉操作。
【代碼】
#include <stdio.h>
#include <malloc.h>
#include <string.h>

char *str;

char* Reverse(char* str,int n){
    if(str == NULL || n < 0){
        return "";
    }
    int len = strlen(str);
    int i;
    char temp;
    //循環左移n位
    while(n--){
        //左移
        //保存第一個字符
        temp = str[0];
        for(i = 1;i < len;i++){
            str[i-1] = str[i];
        }
        str[len-1] = temp;
    }
    return str;
}


int main() {
    int i,n;
    str = (char*)malloc(sizeof(char)*1001);
    while(scanf("%s",str) != EOF){
        scanf("%d",&n);
        //左旋轉
        str = Reverse(str,n);
        //輸出
        for(i = 0;i < strlen(str);i++){
            printf("%c",str[i]);
        }
        printf("\n");
    }//while
    return 0;
}

對上述的方法進行進一步的優化。
UDBOJ 4
對上述字符串循環左移4次,8次,12次。。。。。(n % len )效果是一樣的
左移次數:n % len                n原始左移次數   len字符串長度

【代碼】
char* Reverse(char* str,int n){
    if(str == NULL || n < 0){
        return "";
    }
    int len = strlen(str);
    int i;
    char temp;
    //對左移次數進行優化
    n = n % len;
    //循環左移n位
    while(n--){
        //左移
        //保存第一個字符
        temp = str[0];
        for(i = 1;i < len;i++){
            str[i-1] = str[i];
        }
        str[len-1] = temp;
    }
    return str;
}

(2)指針翻轉法

 咱們先來看個例子,如下:
 abcdefghi,若要讓abc移動至最後的過程可以是:
abcdefghi->defabcghi->defghiabc

如此,我們可定義倆指針,p1指向ch[0],p2指向ch[m];
一下過程循環m次,交換p1和p2所指元素,然後p1++, p2++;。

第一步,交換abc 和def ,
abcdefghi->defabcghi
第二步,交換abc 和ghi,def
abcghi->defghiabc

整個過程,看起來,就是abc作為一組一步一步向後移動

abcdefghi
defabcghi
defghiabc  
//最後的 複雜度是O(m+n) 

由上述例子九個元素的序列abcdefghi,您已經看到,m=3時,p2恰好指到了數組最後一個元素,每m個一組正好一一對應。於是,上述思路沒有問題。但是每m個一組有剩餘時怎麼辦?假設abcde,還能如上麵的方法解決嗎?這樣就不可以了,那樣就數組越界了。所以我們要特殊處理尾部不夠m個一組的元素。
就舉這個例子,對abcdefghij序列進行左旋轉操作:

如果abcdefghij要變成defghijabc:
1. def
abcghij
2. defghi
abcj      //最後一個j不夠一組m個要單獨處理
3. defghi
abjc      //我們需要一次一次交換移至前麵
4. defghi
ajbc
5. defghijabc 

下麵,再針對上述過程,畫個圖清晰說明下,如下所示:


總結:
1、首先讓p1=ch[0],p2=ch[m],即讓p1,p2相隔m的距離,可以理解為每m個一組,第一組和後每組對應交換;
2、判斷p2+m-1是否越界,即判斷最後一組是不是構成一組。如果不能構成一組就不能一一對應交換,如果沒有越界轉到3,否則轉到4。
3、不斷交換*p1與*p2,然後p1++,p2++,循環m次,然後轉到2。
4、此時p2+m-1已經越界,即最後一組不能構成一組,不能一一對應交換,需處理尾巴。
過程如下:
   4.1 通過n-p2得到p2與尾部之間元素個數r,即我們要前移的元素個數。
   4.2 以下過程執行r次:
       ch[p2]<->ch[p2-1],ch[p2-1]<->ch[p2-2],....,ch[p1+1]<->ch[p1];p1++;p2++;
所以,之前最初的那個左旋轉九個元素abcdefghi的思路在末尾會出現問題的(如果p2後麵有元素就不能這麼變,例如,如果是處理十個元素,abcdefghij?對的,就是這個意思),解決方法:
def ghi abc jk
當p1指向a,p2指向j時,由於p2+m越界,那麼此時p1,p2不要變
這裏p1之後(abcjk)就是尾巴,處理尾巴隻需將j,k移到abc之前,得到最終序列,代碼編寫如下:

#include <stdio.h>
#include <malloc.h>
#include <string.h>

char *str;

void Reverse(char* str,int n){
    if(str == NULL || n <= 0){
        return;
    }
    int len = strlen(str);
    int pre = 0;
    int p = n;
    char temp;
    //len % n  n個一組,不夠一組的個數
    //len - n - len % n  表示總個數減去最前一組減去最後不夠一組的個數
    //交換次數
    int count = len - n - len % n;
    while(count--){
        //交換
        temp = str[pre];
        str[pre] = str[p];
        str[p] = temp;
        pre++;
        p++;
    }
    //處理最後尾部不夠一組的元素
    //rear為尾部左移元素個數
    int rear = len - p;
    while(rear--){
        //左移
        int i = p;
        while(i > pre){
            temp = str[i];
            str[i] = str[i-1];
            str[i-1] = temp;
            i--;
        }
        p++;
        pre++;
    }
}


int main() {
    int i,n;
    str = (char*)malloc(sizeof(char)*1001);
    while(scanf("%s",str) != EOF){
        scanf("%d",&n);
        //左旋轉
        //注意一下n需要取餘就可以了,不能超過字符串自身的長度
        n = n % strlen(str);
        Reverse(str,n);
        //輸出
        for(i = 0;i < strlen(str);i++){
            printf("%c",str[i]);
        }
        printf("\n");
    }//while
    return 0;
}

(3)三步翻轉法


/*********************************
*   日期:2013-12-02
*   作者:SJF0115
*   題號: 題目1362:左旋轉字符串(Move!Move!!Move!!!)
*   來源:https://ac.jobdu.com/problem.php?pid=1362
*   結果:AC
*   來源:劍指Offer
*   總結:
**********************************/
#include <stdio.h>
#include <malloc.h>
#include <string.h>

char *str;

//反轉單詞
void ReverseWord(char* words,int begin,int end){
    int temp;
    if(words == NULL || begin > end || begin < 0){
        return;
    }
    //反轉
    while(begin < end){
        temp = words[begin];
        words[begin] = words[end];
        words[end] = temp;
        begin ++;
        end --;
    }
}

char* Reverse(char* str,int n){
    if(str == NULL || n < 0){
        return "";
    }
    int len = strlen(str);
    //分成兩部分(1)前n項(2)剩餘項
    //反轉前n個
    ReverseWord(str,0,n-1);
    //反轉剩餘
    ReverseWord(str,n,len-1);
    //全部反轉
    ReverseWord(str,0,len-1);
    return str;
}


int main() {
    int i,n;
    str = (char*)malloc(sizeof(char)*1001);
    while(scanf("%s",str) != EOF){
        scanf("%d",&n);
        //左旋轉
        //注意一下n需要取餘就可以了,不能超過字符串自身的長度
        n = n % strlen(str);
        str = Reverse(str,n);
        //輸出
        for(i = 0;i < strlen(str);i++){
            printf("%c",str[i]);
        }
        printf("\n");
    }//while
    return 0;
}






轉載: 程序員編程藝術:第一章、左旋轉字符串

自己按自己思路有改動。


最後更新:2017-04-03 14:54:29

  上一篇:go 實用正則表達式掃描android SDcard的文件
  下一篇:go 手機衛士03-下載app並安裝