始終相信能聽明白和能說明白是兩種不同的境界,如果對於一件問題,你說你明白了,但是你卻不能說明白,那在我眼里就會有這樣的一個事實,那就是:你並沒有真正地明白。當然,也存在這種情況,那就是說是說明白了,但是未必對,這正是我擔心的情況,所以還請閱讀過我所有文章的朋友們指出我文章中的錯誤。謝謝你們……
1.問題定義
設計一個算法,把一個含有n個元素的數組A循環右移k位,要求時間復雜度是O(n)的。舉個例子說明一下算法要達到的效果:數組中包含的元素為123456,現在要循環右移2位,則變成了561234 。
2.解決方案
2.1 朴素的思路
往往是這樣,朴素的思路雖不能滿足題目的要求,但是確可能是優化的依據。對於這個問題朴素的解法是,每次完成一次右移,並且完成一次右移的時間復雜度是O(n),這樣的操作要進行k次,所以時間復雜度是O(n*k)。但是現在想一想,既然已經知道要右移的位數k,那么要是還一步一步的移是不是顯得有點笨了呢。為什么不一步到位呢?
2.2 空間換時間
對於算法上的優化,以空間換時間是一種比較常規的辦法了。可以借助一個輔助數組T,先把數組A的第n-k+1到n位數組中的元素存儲到T中,然后再把數組A中的第1到n-k位數組元素存儲到輔助數組T中,然后將數組T中的元素復制回數組A,這樣就完成了數組的循環右移,時間復雜度為O(n),但是呢,卻增加了O(n)的空間復雜度。如果要求最多只能允許使用兩個輔助空間呢?這可就要想一想能不能進一步優化了。
2.3 利用“翻轉”完成優化
考慮一下數組A中元素123456循環右移2位到底是怎么個情況!!!可不可以這樣實現呢?將數組A分成兩個部分:A[0~n-k-1] 和 A[n-k~n-1] ,將這兩個部分分別翻轉,然后放在一起在翻轉(逆序)。具體是這樣的:
(1)翻轉1234:123456 ---> 432156
(2)翻轉56: 432156 ---> 432165
(3)翻轉432165:432165 ---> 561234
看看吧,確實完成了翻轉的操作。現在來看一下代碼(灰常簡單):
1 //逆序 2 void Reverse(int A[],int b,int e) 3 { 4 for(;b < e;b++,e--) 5 { 6 int temp = A[b]; 7 A[b] = A[e]; 8 A[e] = temp; 9 } 10 } 11 //循環右移 12 void RightShift(int A[],int,int n,int k) 13 { 14 Reverse(A,0,n-k-1); 15 Reverse(A,n-k,n-1); 16 Reverse(A,0,n-1); 17 }
現在可以分析一下時空復雜度了,時間復雜度顯然是O(n),主要是完成翻轉(逆序)操作,並且只用了一個輔助空間。顯然這是一個不錯算法哦。。。
注:
細心的讀者可能就發現,這里的右移並沒有說小於n呀,那么一個k>n那就會出現問題,經過簡單的分析,我們發現如果A[]={1,2,3,4,5,6},數組長度是6,那么循環右移7位和循環右移1位是一樣的,所以只需要把函數RightShift()中的k取值為k=k%n。如此一來問題就解決了。
學習中的一點總結,歡迎拍磚哦^^