版權(quán)聲明:本文為博主原創(chuàng)文章评姨,未經(jīng)博主允許不得轉(zhuǎn)載萤晴。
難度:容易
要求:
給定一個(gè)字符串和一個(gè)偏移量,根據(jù)偏移量旋轉(zhuǎn)字符串(從左向右旋轉(zhuǎn))
樣例
對(duì)于字符串 "abcdefg".
offset=0 => "abcdefg"
offset=1 => "gabcdef"
offset=2 => "fgabcde"
offset=3 => "efgabcd"
思路:
題目描述:
定義字符串的左旋轉(zhuǎn)操作:把字符串前面的若干個(gè)字符移動(dòng)到字符串的尾部嗦枢。
如把字符串a(chǎn)bcdef左旋轉(zhuǎn)2位得到字符串cdefab屯断。
請(qǐng)實(shí)現(xiàn)字符串左旋轉(zhuǎn)的函數(shù)殖演,要求對(duì)長(zhǎng)度為n的字符串操作的時(shí)間復(fù)雜度為O(n),空間復(fù)雜度為O(1)趴久。
編程之美上有這樣一個(gè)類似的問題彼棍,咱們先來看一下:
設(shè)計(jì)一個(gè)算法座硕,把一個(gè)含有N個(gè)元素的數(shù)組循環(huán)右移K位,要求時(shí)間復(fù)雜度為O(N)华匾,
且只允許使用兩個(gè)附加變量蜘拉。
分析:
我們先試驗(yàn)簡(jiǎn)單的辦法,可以每次將數(shù)組中的元素右移一位诸尽,循環(huán)K次。
abcd1234→4abcd123→34abcd12→234abcd1→1234abcd穿肄。
RightShift(int* arr, int N, int K)
{
while(K--)
{
int t = arr[N - 1];
for(int i = N - 1; i > 0; i --)
arr[i] = arr[i - 1];
arr[0] = t;
}
}
雖然這個(gè)算法可以實(shí)現(xiàn)數(shù)組的循環(huán)右移际看,但是算法復(fù)雜度為O(K * N)仲闽,不符合題目的要求,要繼續(xù)探索。
假如數(shù)組為abcd1234验庙,循環(huán)右移4位的話社牲,我們希望到達(dá)的狀態(tài)是1234abcd。
不妨設(shè)K是一個(gè)非負(fù)的整數(shù)违寿,當(dāng)K為負(fù)整數(shù)的時(shí)候熟空,右移K位,相當(dāng)于左移(-K)位菌瘪。
左移和右移在本質(zhì)上是一樣的阱当。
解法一:
大家開始可能會(huì)有這樣的潛在假設(shè),K<N录淡。事實(shí)上油坝,很多時(shí)候也的確是這樣的。但嚴(yán)格來說彬檀,我們不能用這樣的“慣性思維”來思考問題瞬女。
尤其在編程的時(shí)候,全面地考慮問題是很重要的坤学,K可能是一個(gè)遠(yuǎn)大于N的整數(shù)报慕,在這個(gè)時(shí)候,上面的解法是需要改進(jìn)的飞苇。
仔細(xì)觀察循環(huán)右移的特點(diǎn),不難發(fā)現(xiàn):每個(gè)元素右移N位后都會(huì)回到自己的位置上雨让。因此羽利,如果K > N刊懈,右移K-N之后的數(shù)組序列跟右移K位的結(jié)果是一樣的。
進(jìn)而可得出一條通用的規(guī)律:
右移K位之后的情形匾浪,跟右移K’= K % N位之后的情形一樣卷哩,如代碼清單2-34所示。
//代碼清單2-34
RightShift(int* arr, int N, int K)
{
K %= N;
while(K--)
{
int t = arr[N - 1];
for(int i = N - 1; i > 0; i --)
arr[i] = arr[i - 1];
arr[0] = t;
}
}
可見冷溶,增加考慮循環(huán)右移的特點(diǎn)之后尊浓,算法復(fù)雜度降為O(N^2),這跟K無關(guān)苗胀,與題目的要求又接近了一步瓦堵。但時(shí)間復(fù)雜度還不夠低,接下來讓我們繼續(xù)挖掘循環(huán)右移前后澜驮,數(shù)組之間的關(guān)聯(lián)惋鸥。
解法二:
假設(shè)原數(shù)組序列為abcd1234,要求變換成的數(shù)組序列為1234abcd亭畜,即循環(huán)右移了4位迎卤。比較之后,不難看出劲藐,其中有兩段的順序是不變的:1234和abcd,可把這兩段看成兩個(gè)整體兄渺。右移K位的過程就是把數(shù)組的兩部分交換一下汰现。
變換的過程通過以下步驟完成:
逆序排列abcd:abcd1234 → dcba1234;
逆序排列1234:dcba1234 → dcba4321口叙;
全部逆序:dcba4321 → 1234abcd嗅战。
偽代碼可以參考清單2-35。
//代碼清單2-35
Reverse(int* arr, int b, int e)
{
for(; b < e; b++, e--)
{
int temp = arr[e];
arr[e] = arr[b];
arr[b] = temp;
}
}
RightShift(int* arr, int N, int k)
{
K %= N;
Reverse(arr, 0, N – K - 1);
Reverse(arr, N - K, N - 1);
Reverse(arr, 0, N - 1);
}
這樣疟呐,我們就可以在線性時(shí)間內(nèi)實(shí)現(xiàn)右移操作了萨醒。
public class Solution {
/**
* @param str: an array of char
* @param offset: an integer
* @return: nothing
*/
public void rotateString(char[] str, int offset) {
if(str == null || str.length == 0){
return;
}
int len = str.length;
offset = offset % len;
reverse(str, 0, len - offset - 1);
reverse(str, len - offset, len - 1);
reverse(str, 0, len - 1);
}
private void reverse(char[] str,int start,int end){
for( ;start < end; start++,end--){
char tmp = str[start];
str[start] = str[end];
str[end] = tmp;
}
}
}