C++實現一維向量旋轉演算法
C++實現一維向量旋轉的演算法的怎麼程式設計的呢?下面內容由小編為大家介紹C++實現一維向量旋轉演算法,供大家參考!
在《程式設計珠璣》一書的第二章提到了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_
> Author: SongLee
************************************************************************/
#include
#include
using namespace std;
int main()
{
string s = "abcdefghijklmn";
cout << "The origin is: " << s << endl;
// 左移個數
int i;
cin >> i;
if(i > ())
{
i = i%();
}
// 將前i個元素臨時儲存
string tmp(s, 0, i);
// 將剩餘的左移i個位置
for(int j=i; j
{
s[j-i] = s[j];
}
s = tr(0, ()-i) + tmp;
cout << "The result is: "<< s << endl;
return 0;
}
思路二:定義一個函式將x向左旋轉一個位置(其時間正比於n),然後呼叫該函式i次。
效能:這種方法雖然空間複雜度為O(1),但產生了過多的執行時間消耗。
C++程式碼實現如下:
/*************************************************************************
> File Name: vector_rotate_
> Author: SongLee
************************************************************************/
#include
#include
using namespace std;
void rotateOnce(string &s)
{
char tmp = s[0];
int i;
for(i=1; 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 > ())
{
i = i%();
}
// 呼叫函式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_
> Author: SongLee
************************************************************************/
#include
#include
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 > ())
{
i = i%();
}
// 移動
char tmp;
int times = gcd((), i);
for(int j=0; j
{
tmp = s[j];
int pre = j; // 記錄上一次的位置
while(1)
{
int t = pre+i;
if(t >= ())
t = ();
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。
?
1
2
3
reverse(0, i-1) /*cbadefgh*/
reverse(i, n-1) /*cbahgfed*/
reverse(0, n-1) /*defghabc*/
效能:求逆序的方法在時間和空間上都很高效,而且程式碼非常簡短,很難出錯。
C++程式碼實現如下:
/*************************************************************************
> File Name: vector_
> Author: SongLee
************************************************************************/
#include
#include
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 > ())
{
i = i%();
}
reverse(s, 0, i-1);
reverse(s, i, ()-1);
reverse(s, 0, ()-1);
cout << "The result is: "<< s << endl;
return 0;
}
三、擴充套件延伸
如何將向量abc旋轉變成cba?
和前面的問題類似,此向量旋轉對應著非相鄰記憶體塊的交換模型。解法很相似,即利用恆等式:cba = (arbrcr)r