STL源码之rotate函数结合图和实例分析
来源:互联网 发布:微信炸群软件 编辑:程序博客网 时间:2024/05/16 03:36
今天看 STL 源码看到 rotate() 函数这一块,该函数就是将 [first, middle) 的元素和 [middle, last) 的元素互换。middle 的元素会成为容器的第一个元素。如果有个数字序列 {1, 2, 3, 4, 5, 6, 7},对元素 3 做旋转操作,会形成 {3, 4, 5, 6, 7, 1, 2}。其实这就是我们平时说的左旋转字符串,只不过泛型化了而已。它可以旋转的内容不止字符串,其他迭代器类型都可以。
三种方法的分析:
算法1(分组交换):(来自网友:雁过无痕)
若a长度大于b,将ab分成a0a1b,交换a0和b,得ba1a0,只需再交换a1 和a0。若a长度小于b,将ab分成ab0b1,交换a和b0,得b0ab1,只需再交换a 和b1。不断将数组划分和交换,直到不能再划分为止。分组过程与求最大公约数很相似。
代码如下:
emplate <class ForwardIterator, class Distance>// Distance类型仅仅对于random iterator的实现版本有意义,但为了便于上层代码便于调用,所以使用// 了同样的签名。void __rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last, Distance*, forward_iterator_tag){ for (ForwardIterator i = middle; ;) { // iter_swap用于交换两个iterator所指向的内容。 // 也可以这样写:swap(*first, *i); iter_swap(first, i); ++first; ++i; if (first == middle) { // first和i同时到达末尾,元素交换结束,返回。 if (i == last) return; // first首先到达末尾,说明A的长度小于B。 middle = i; } // i首先到达末尾,说明A的长度大于B。 else if (i == last) i = middle; }}
算法2 (三次反转)
利用ba=(br)r(ar)r=(arbr)r,先分别反转a、b,最后再对所有元素进行一次反转。
代码如下:
template <class BidirectionalIterator, class Distance>void __rotate(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last, Distance*, bidirectional_iterator_tag){ // 翻转A reverse(first, middle); // 翻转B reverse(middle, last); // 翻转A'B' reverse(first, last);}
算法3 (使用gcd)(分析来自网友:陈覃)
__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++实现
整个算法过程可用下图表示:
代码如下:
template <class RandomAccessIterator, class Distance>void __rotate(RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last, Distance*, random_access_iterator_tag){ // gcd是求最大公约数的函数。 Distance n = __gcd(last - first, middle - first); while (n--) //注意这里是n--,我因为没看见这个n--,时间浪费了半天 // 需要执行__rotate_cycle n次。 __rotate_cycle(first, last, first + n, middle - first, value_type(first));}template <class RandomAccessIterator, class Distance, class T>void __rotate_cycle(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator initial, Distance shift, T*){ T value = *initial; RandomAccessIterator ptr1 = initial; RandomAccessIterator ptr2 = ptr1 + shift; while (ptr2 != initial) { *ptr1 = *ptr2; ptr1 = ptr2; if (last - ptr2 > shift) ptr2 += shift; else ptr2 = first + (shift - (last - ptr2)); } *ptr1 = value;}template <class EuclideanRingElement>EuclideanRingElement __gcd(EuclideanRingElement m, EuclideanRingElement n){ while (n != 0) { EuclideanRingElement t = m % n; m = n; n = t; } return m;}
由于前两种比较简单,在这里我仅实现了第三种作为练习,一次性AC:)
#include <iostream>#include <assert.h>int calc_gcd(int m, int n){ if(m < n) std::swap(m, n); if(n == 0) return m; calc_gcd(n, m%n);}template <typename T>void cycle_rotate(T *arr, int *first, int *last, int *initial, int rotate_num){ T value = *initial; T *ptr1 = initial, *ptr2 = ptr1 + rotate_num; while(ptr2 != initial){ *ptr1 = *ptr2; ptr1 = ptr2; if(last - ptr2 >= rotate_num) //可以等于,因为是下标 ptr2 += rotate_num; else ptr2 = first + (rotate_num - (last - ptr2)) - 1; //注意要减一,因为我这里用的是下标 } *ptr1 = value;}template <typename T>void rotate(T* arr, int start, int end, int rotate_num){ assert(start >= 0 && rotate_num > start && rotate_num <= end+1); int gcd = calc_gcd(end-start+1, rotate_num); while(gcd--) cycle_rotate(arr, arr+start, arr+end, arr+start+gcd, rotate_num);}int main(){ int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; int len = sizeof(array) / sizeof(int); rotate(array, 0, len-1, 5); for(auto i : array) std::cout<<i<<' '; std::cout<<std::endl; return 0;}
输出:
0 0
- STL源码之rotate函数结合图和实例分析
- STL 源码分析 之 rotate()函数分析
- STL的rotate函数分析
- spark sql和catalyst实例结合源码分析
- STL源码分析之vector(三)—其它函数 eraser()、operator[]和operator =
- STL源码分析之感想
- STL源码分析之Vector
- STL源码分析之Vector
- STL之pair源码分析
- STL源码分析之大顶堆
- STL源码分析之hashtable
- STL源码分析之__type_traits
- STL源码分析之literator和triats编程技法
- <STL源码剖析>阅读笔记之 仿函数和适配器
- STL源码分析--仿函数 & 配接器
- C++之STL(九):函数适配器bind2nd 、mem_fun_ref 源码分析、函数适配器应用举例
- 《STL源码剖析》之vector分析
- 《STL源码剖析》之vector分析
- c# delegate 传参
- Excel地址转换
- svn 命令
- /proc/net/tcp
- 百度地图定位功能
- STL源码之rotate函数结合图和实例分析
- logback 配置详解(一)<configuration> and <logger>
- Git 初次使用(4)
- iOS微博个人主页
- Windows下配置mysql5.7.17以及遇到的问题
- win7 忘掉密码
- easyUI combobox 文本框 多项选择 限制选择的个数。
- python数据类型详解
- SVN服务器客户端搭建以及使用