C++實現一維向量旋轉演算法_C 語言

來源:互聯網
上載者:User

在《編程珠璣》一書的第二章提到了n元一維向量旋轉演算法(又稱數組迴圈移位演算法)的五種思路,並且比較了它們在時間和空間效能上的區別和優劣。本文將就這一演算法做較為深入的分析。具體如下所示:

一、問題描述

將一個n元一維向量向左旋轉i個位置。例如,假設n=8,i=3,向量abcdefgh旋轉為向量defghabc。簡單的代碼使用一個n元的中間向量在n步內可完成該工作。你能否僅使用幾十個額外位元組的記憶體空間,在正比於n的時間內完成向量的旋轉?

二、解決方案

思路一:將向量x中的前i個元素複製到一個臨時數組中,接著將餘下的n-i個元素左移i個位置,然後再將前i個元素從臨時數組中複製到x中餘下的位置。

效能:這種方法使用了i個額外的位置,如果i很大則產生了過大的儲存空間的消耗。

C++代碼實現如下:

/*************************************************************************   > File Name: vector_rotate.cpp   > Author: SongLee  ************************************************************************/ #include<iostream> #include<string> using namespace std;  int main() {   string s = "abcdefghijklmn";   cout << "The origin is: " << s << endl;   // 左移個數   int i;   cin >> i;   if(i > s.size())   {     i = i%s.size();   }   // 將前i個元素臨時儲存   string tmp(s, 0, i);   // 將剩餘的左移i個位置   for(int  j=i; j<s.size(); ++j)   {     s[j-i] = s[j];   }   s = s.substr(0, s.size()-i) + tmp;   cout << "The result is: "<< s << endl;   return 0; } 

思路二:定義一個函數將x向左旋轉一個位置(其時間正比於n),然後調用該函數i次。

效能:這種方法雖然空間複雜度為O(1),但產生了過多的已耗用時間消耗。

C++代碼實現如下:

/*************************************************************************   > File Name: vector_rotate_1.cpp   > Author: SongLee  ************************************************************************/ #include<iostream> #include<string> using namespace std;  void rotateOnce(string &s) {   char tmp = s[0];   int i;   for(i=1; i<s.size(); ++i)   {     s[i-1] = s[i];   }   s[i-1] = tmp; }  int main() {   string s = "abcdefghijklmn";   cout << "The origin is: " << s << endl;   // 左移個數   int i;   cin >> i;   if(i > s.size())   {     i = i%s.size();   }   // 調用函數i次   while(i--)   {     rotateOnce(s);   }   cout << "The result is: "<< s << endl;   return 0; } 


思路三:移動x[0]到臨時變數t中,然後移動x[i]到x[0]中,x[2i]到x[i],依次類推,直到我們又回到x[0]的位置提取元素,此時改為從臨時變數t中提取元素,然後結束該過程(當下標大於n時對n模數或者減去n)。如果該過程沒有移動全部的元素,就從x[1]開始再次進行移動,總共移動i和n的最大公約數次。

效能:這種方法非常精巧,像書中所說的一樣堪稱巧妙的雜技表演。空間複雜度為O(1),時間複雜度為線性時間,滿足問題的效能要求,但還不是最佳。

C++代碼實現如下:

/*************************************************************************   > File Name: vector_rotate_2.cpp   > Author: SongLee  ************************************************************************/ #include<iostream> #include<string> using namespace std;  // 歐幾裡德(輾轉相除)演算法求最大公約數 int gcd(int i, int j) {   while(1)   {     if(i > j)     {       i = i%j;       if(i == 0)       {         return j;       }     }     if(j > i)     {       j = j%i;       if(j == 0)       {         return i;       }     }   } }  int main() {   string s = "abcdefghijklmn";   cout << "The origin is: "<< s << endl;   // 左移個數   int i;   cin >> i;   if(i > s.size())   {     i = i%s.size();   }   // 移動   char tmp;   int times = gcd(s.size(), i);   for(int j=0; j<times; ++j)   {     tmp = s[j];     int pre = j; // 記錄上一次的位置     while(1)     {       int t = pre+i;       if(t >= s.size())         t = t-s.size();       if(t == j) // 直到tmp原來的位置j為止         break;       s[pre] = s[t];       pre = t;     }     s[pre] = tmp;   }   cout << "The result is: "<< s << endl;   return 0; } 

思路四:旋轉向量x實際上就是交換向量ab的兩段,得到向量ba,這裡a代表x的前i個元素。假設a比b短。將b分割成bl和br,使br的長度和a的長度一樣。交換a和br,將ablbr轉換成brbla。因為序列a已在它的最終位置了,所以我們可以集中精力交換b的兩個部分了。由於這個新問題和原先的問題是一樣的,所以我們以遞迴的方式進行解決。這種方法可以得到優雅的程式,但是需要巧妙的代碼,並且要進行一些思考才能看出它的效率足夠高。

//實現代碼(略) 

思路五:(最佳)將這個問題看做是把數組ab轉換成ba,同時假定我們擁有一個函數可以將數組中特定部分的元素逆序。從ab開始,首先對a求逆,得到arb,然後對b求逆,得到arbr。最後整體求逆,得到(arbr)r,也就是ba。

reverse(0, i-1)  /*cbadefgh*/reverse(i, n-1) /*cbahgfed*/reverse(0, n-1) /*defghabc*/

效能:求逆序的方法在時間和空間上都很高效,而且代碼非常簡短,很難出錯。

C++代碼實現如下:

/*************************************************************************   > File Name: vector_rotate.cpp   > Author: SongLee  ************************************************************************/ #include<iostream> #include<string> using namespace std;  void reverse(string &s, int begin, int end) {   while(begin < end)   {     char tmp = s[begin];     s[begin] = s[end];     s[end] = tmp;     ++begin;     --end;   } }  int main() {   string s = "abcdefghijklmn";   cout << "The origin is: "<< s << endl;      int i;   cin >> i;   if(i > s.size())   {     i = i%s.size();   }    reverse(s, 0, i-1);   reverse(s, i, s.size()-1);   reverse(s, 0, s.size()-1);    cout << "The result is: "<< s << endl;   return 0; } 

三、擴充延伸

如何將向量abc旋轉變成cba?

和前面的問題類似,此向量旋轉對應著非相鄰記憶體塊的交換模型。解法很相似,即利用恒等式:cba = (arbrcr)r

注意:在面試或筆試時,如若出現向量旋轉(記憶體塊交換)問題,建議最好使用思路五答題,不僅高效而且簡潔。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.