日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

旋转字符串算法由浅入深

發布時間:2025/6/15 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 旋转字符串算法由浅入深 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

昨天在寫一個旋轉字符串的函數時,寫著寫著發現有好多種方法,最簡單的莫過于替換然后覆蓋再插入。不要小看這種小的算法,其實這其中蘊含著很多容易忽略的編程的細節。下面就跟隨著我的文字來由淺入深進行鞏固和再學習。總結下來此問題的算法大約有五個,這是在分得很細的情況下,前面的兩個是自己想的,后面的三個參考了一個叫July的大神的思路。其實這些算法總體的思路大同小異,但這些細節問題也讓我的思維有了很大的開闊。下面就由淺入深一一分析:

?

思路一:

此思路是最容易想到的,就是進行簡單的替換,覆蓋和插入操作。不好描述,直接見代碼:其中需要注意的地方都已標注出來。

?

1 /* 思路一:正常思路,循環左移 2 * 注意K的處理,K有可能比N大,K 等價于 K %= N; 3 * 算法的時間復雜度為O(N^2); 4 */ 5 void RightShift(char * pArr, int N, int K) 6 { 7 assert(NULL != pArr); //斷言判斷 8 if(NULL == pArr) 9 return; 10 11 K %= N; //K有可能比N大,考慮周到了 12 while(K--){ 13 char pTemp = pArr[0]; 14 for(int i = 0; i < N; i ++){ 15 pArr[i] = pArr[i + 1]; 16 } 17 pArr[N - 1] = pTemp; 18 } 19 }

?

當然你也可以C++的String庫來寫,建議以后編程多用C++的string庫,至少不會出現(char *)中出現的很多令人蛋疼的指針問題,不過各有各的好處,因人而異。

上面的思路最簡單,但時間復雜度卻不是很理想。下面是改進的算法,實現三次交換,而不是雙重循環。交換的時間復雜度是線性的。

?

思路二:

這個也是比較容易想到的,E.g:"abcd1234" ,將之分為兩部分,"abcd"和"1234",將兩者交換-->"dcba"和"4321",在對整體交換-->"1234abcd",OK!是不是很簡單,大部分人想到這里就應該會放棄了,包括我也是這樣,但解決問題的方式永遠不止一兩種,只有少部分人相信了這種話,所以,相信的現在都變大神了,大神July就是這樣的,下面的幾種思路保證讓你大開眼界,所以,以后思考問題應該多多抱著一種批判的思想,層層深入,如此方能鑿到金子。看思路二的代碼:

?

1 /* 思路二:三次反轉 2 * e.g: "abcd1234" 3 * 第一次反轉:"dcba",第二次反轉:"4321",第三次反轉:“1234abcd” 4 * 算法的時間復雜度降到線性級為O(N); 5 */ 6 void Reverse(char *pArr, int M, int N) //反轉函數 7 { 8 //M、N代表字符串區域邊界上的兩個點 9 while(M < N){ 10 char pTemp = pArr[N]; 11 pArr[N] = pArr[M]; 12 pArr[M] = pTemp; 13 ++ M; 14 -- N; 15 } 16 } 17 //三次反轉 18 char * ThreeReverse(char * pArr, int N, int K) 19 { 20 K %= N; //同樣對K進行處理 21 Reverse(pArr, 0, K - 1); 22 Reverse(pArr, N - K, N - 1); 23 Reverse(pArr, 0, N - 1); 24 return pArr; 25 }

?

上面N表示字符串的長度,K表示要循環移動的位數,注意對K的處理上,K有可能比N大,如果K == N,剛好回到原來的字符串,即沒有移動,所以,我們可以用K %= N來代替K,效果是一樣的。

?

思路三:

將所要旋轉的字符串當做一個整體,然后集體移動,如果是左循環,就進行右移動,右循環就左移動。舉個例子,E.g:“abcdefghijk”實行左循環,將“abc”移動最后,則有:

abcdefghijk” --> "defabcghijk" --> "defghiabcjk",到這里,就沒法再移動了,這個時候,剛好反過來,將"jk"前移 --> "defghijkabc",這其中會用到交換Swap函數,如下:

?

1 void Swap(char *pArr, int M, int N) //交換函數 2 { 3 char pTemp = pArr[N]; 4 pArr[N] = pArr[M]; 5 pArr[M] = pTemp; 6 }

?

那么如何來控制待處理的串(如"abc")的移動呢?用兩個臨界指針不久解決了嗎,保證P2 - P1 = K即可,移動中要對P2進行判斷,如果(P2 + K - 1)超過了 N(串長),就停止。對于"abcdefghijk",停止時 P1-->'a' , P2 --> 'j',因為這個時候(P2 + K1 - 1)> N,控制P2的停止,這個地方有個小技巧,就是設一個變量 Index = (N - K) - (N % K),當Index == 0時,P2不在移動。這個很好理解,比判斷P2是否越界要好處理得多。見代碼:

?

1 /* 思路三:將要循環左移的字符串當做一個整體(兩個指針控制),依次右移 2 * e.g:“abcdefghijk”,將abc移到最右邊-->"defghijkabc" 3 * 第一次移動-->"defabcghijk",第二次移動-->"defghiabcjk" 4 * 再將jk往前移K位-->"defghijkabc" 5 * 算法的時間復雜度也是線性的 6 */ 7 void pConReverseFirst(char *pArr, int N, int K) 8 { 9 assert(NULL != pArr); //斷言判斷 10 if(NULL == pArr) 11 return; 12 13 K %= N; 14 if(K == 0) 15 return; 16 17 //將待處理的串往后移 18 int p1 = 0, p2 = K; 19 int pIndex = (N - K) - (N % K); //小技巧:pIndex表示p2所能指示的最大區域 20 21 while(pIndex --){ 22 Swap(pArr, p1, p2); 23 ++ p1; 24 ++ p2; 25 } 26 27 //將剩余的串往前移 28 int pR = N % K; //計算剩余的單出來的數,將這些數統一向前移,pR也可以= N - p2; 29 while(pR --){ 30 char pTemp = pArr[p2]; 31 for(int i = p2; i > p1; i --) 32 pArr[i] = pArr[i - 1]; 33 pArr[p1] = pTemp; 34 ++ p2; 35 ++ p1; 36 } 37 }

?

思路四:

前面部分的算法和思路三一樣,在后面剩余串的處理上,本思路是將待處理串中剩余的部分往后移,E.g:"abcdefghijk" -- > "defghiabcjk" -- > "defghi?j?bc?a?k" -- > "defghi?j k?c?a b",將'c'往后移 -- > "defghijk abc"。見代碼:

?

1 /* 思路四:和思路三一樣,只在后面多余數據的處理上不一樣,剛好和思路三相反 2 * 只要 *p2 != '\0',就交換p1和p2;然后將前面多余的數單獨移到最后 3 * e.g:"defghiabcjk" --> "defghijkcab" --> "defghijkabc" 4 * 同樣的時間復雜度為線性的 5 */ 6 void pConReverseSecond(char * pArr, int N, int K) 7 { 8 assert(NULL != pArr); //斷言判斷 9 if(NULL == pArr) 10 return; 11 12 K %= N; 13 if(K == 0) 14 return; 15 16 int p1 = 0, p2 = K; 17 while(p2 < N){ 18 Swap(pArr, p1,p2); 19 ++ p1; 20 ++ p2; 21 } 22 23 int pR = K - (N % K); //計算前面p1所指范圍內剩余的數,e.g:"defghijkcab"剩余'c' 24 while(pR --){ 25 for(int i = p1; i < p2 - 1; i ++) 26 Swap(pArr, i, i + 1); 27 } 28 }

?

思路五:

和思路三前面部分的算法也是一樣的,后面的部分則采用遞歸處理。代碼中有說明,相見代碼:

?

1 /* 思路五:遞歸求解,前面的思路和思路三是一樣的,只是對于后面的要遞歸處理 2 * e.g:"abcdefghijk" --> "defghiabcjk" 此時,對于"abcjk" 3 * N = K + N % K = 5; K = N % K = 2; 將"jk"左移 --> "ajkbc",此時,對于"ajk" 4 * N = K + N % K = 3; K = N % K = 1; 將'a' 右移 --> "jka"; 5 * 算法的時間復雜度也是線性的 6 */ 7 void RecurReverse(char * pArr, int N, int K, int pHead, int pTail, bool pFlag) 8 { 9 /* pHead = 待處理的頭元素,pTail = 待處理的尾元素 10 * pFlag = 左循還是右循的標志 11 */ 12 assert(NULL != pArr); //斷言判斷 13 if(NULL == pArr) 14 return; 15 16 K %= N; 17 if(pHead == pTail || K == 0) //遞歸出口 18 return; 19 20 //左循右移 21 if(pFlag == true){ 22 int p1 = pHead, p2 = pHead + K; 23 int pLeft = N - K - (N % K); 24 25 for(; pLeft > 0; -- pLeft, ++ p1, ++p2) 26 Swap(pArr, p1, p2); 27 28 //遞歸,pFLag == FALSE 29 RecurReverse(pArr, K + N % K, N % K, p1, pTail, false); 30 } 31 32 //右循左移 33 //p2指向最右邊第一個 34 else{ 35 int p2 = pTail, p1 = pTail - K; 36 int pRight = N - K - (N % K); 37 38 for(; pRight > 0; -- pRight, -- p1, -- p2) 39 Swap(pArr, p1, p2); 40 //遞歸,pFLag == TRUE 41 RecurReverse(pArr, K + N % K, N % K, p1, p2, true); //"ajk" , p1指向'a',p2指向'k' 42 } 43 }

?

OK,以上所有代碼都嚴格經過測試成立。

以上的算法思想,是非常低級的,一切沒有涉及數據結構的算法都是非常低級的算法,但這些算法或多或少在不同的程度上打開了我們的思維,對以后的學習會有很多的幫助。以上的代碼有好多種寫法,每個人的寫法都不一樣,關鍵是懂得這種思想,學會層層深入地思考問題。

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的旋转字符串算法由浅入深的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。