STL之rotate

STL对左旋转字符串针对三种不同数据结构进行了不同的实现。

对单向链表采用的是同步位移,双向链表是三次翻转,都很简单,主要看看针对随机存取的数组做的循环位移实现。

STL这个版本的源码如下:

[cpp]  view plain  copy
  1. template <class RandomAccessIterator, class Distance>  
  2. void __rotate(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last, Distance*, random_access_iterator_tag)  
  3. {  
  4.     // gcd是求最大公约数的函数。  
  5.     Distance n = __gcd(last - first, middle - first);  
  6.   
  7.     while (n--)  
  8.         // 需要执行__rotate_cycle n次。  
  9.         __rotate_cycle(first, last, first + n, middle - first, value_type(first));  
  10. }  
  11.   
  12. template <class RandomAccessIterator, class Distance, class T>  
  13. void __rotate_cycle(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator initial, Distance shift, T*)  
  14. {  
  15.     T value = *initial;  
  16.     RandomAccessIterator ptr1 = initial;  
  17.     RandomAccessIterator ptr2 = ptr1 + shift;  
  18.   
  19.     while (ptr2 != initial) {  
  20.         *ptr1 = *ptr2;  
  21.         ptr1 = ptr2;  
  22.         if (last - ptr2 > shift)  
  23.             ptr2 += shift;  
  24.         else  
  25.             ptr2 = first + (shift - (last - ptr2));  
  26.     }  
  27.   
  28.     *ptr1 = value;  
  29. }  
  30.   
  31. template <class EuclideanRingElement>  
  32. EuclideanRingElement __gcd(EuclideanRingElement m, EuclideanRingElement n)  
  33. {  
  34.     while (n != 0) {  
  35.         EuclideanRingElement t = m % n;  
  36.         m = n;  
  37.         n = t;  
  38.     }  
  39.   
  40.     return m;  
  41. }  
__gcd是求两个数的最大公约数,也是循环位移的遍数。

举个例子来说明算法过程,数组123456789,把123翻转到右边,*first=1,*last=9,*middle=4;

要旋转字符串(123)的长度为3,字符串长度为9,3和9的最大公约数为3,因此需要翻转3遍;

第一遍从*(initial+shift)=6开始,6移到3的位置,9移到6的位置,下一个位置是ptr2 = first + (shift - (last - ptr2))=0+(3-(8-8))=3,不满足ptr2 != initial的条件,退出循环,然后*ptr1 = value,即把数字3移动到数字9的位置,从而完成了3,6,9三个数字的位移,下面的2遍循环则分别完成2,5,8和1,4,76个数字的位移,最后得到最终结果456789123。

整个算法过程可用下图表示:


自我实现C++的int版:

[cpp]  view plain  copy
  1. #include <iostream>  
  2. using namespace std;  
  3. int __gcd(int a, int b)  
  4. {  
  5.     while (b)  
  6.     {  
  7.         int tmp = a%b;  
  8.         a = b;  
  9.         b = tmp;  
  10.     }  
  11.     return a;  
  12. }  
  13. void __rotate_cycle(int *data, int first, int last, int initial, int shift)  
  14. {  
  15.     int val = data[initial];  
  16.     int pos1 = initial;  
  17.     int pos2 = initial + shift;  
  18.     while (pos2 != initial)  
  19.     {  
  20.         data[pos1] = data[pos2];  
  21.         pos1 = pos2;  
  22.         pos2 = (pos2 + shift) % (last - first + 1);  
  23.     }  
  24.     data[pos1] = val;  
  25. }  
  26. void __rotate(int *data,int first, int middle, int last)  
  27. {  
  28.     int gcd = __gcd(last - first+1, middle - first);  
  29.     cout << "循环位移" << gcd << "遍" << endl;  
  30.     while (gcd--)  
  31.     {  
  32.         __rotate_cycle(data, first, last, first + gcd, middle - first);  
  33.     }  
  34. }  
  35. void main()  
  36. {  
  37.     int data1[10] = {1,2,3,4,5,6,7,8,9,10};  
  38.     __rotate(data1, 0, 3, 9);  
  39.     for (int i = 0; i < 10; i++)  
  40.     {  
  41.         cout << data1[i] << " ";  
  42.     }  
  43.     cout << endl;  
  44.     int data2[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };  
  45.     __rotate(data2, 0, 4, 9);  
  46.     for (int i = 0; i < 10; i++)  
  47.     {  
  48.         cout << data2[i] << " ";  
  49.     }  
  50.     cout << endl;  
  51.     int data3[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };  
  52.     __rotate(data3, 0, 5, 9);  
  53.     for (int i = 0; i < 10; i++)  
  54.     {  
  55.         cout << data3[i] << " ";  
  56.     }  
  57.     cout << endl;  
  58.     int data4[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };  
  59.     __rotate(data4, 0, 6, 9);  
  60.     for (int i = 0; i < 10; i++)  
  61.     {  
  62.         cout << data4[i] << " ";  
  63.     }  
  64.     int c = 0;  
  65.     cin >> c;  
  66. }  

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值