关于序列旋转(辗转相除求取最大公约数)
2015-10-30 18:44
351 查看
看STL看到 rotate ,关于序列旋转学习到很多东西,先说一下旋转,旋转的意思如下:
输入:1 2 3
4 5 6 7,翻转点为 4
输出:4 5 6 7
1 2 3
即要求将序列从D开始反转。在《编程珠玑》中说过关于 [ A B ] 转到 [ B A ] 的情况,采用的方法类似翻手掌的方法,方法如下:
1.先将 A ,B 分别逆序。得到: 3 2 1
7 6 5 4
2.再将整个序列逆序。得到:4 5 6 7 1 2 3。即可
STL的实现中对序列的翻转问题根据迭代器的不同类型进行了区别处理,分为以下三种情况:
我个人觉得最大公约数法属于黑科技,用理论解释不清楚。但实际测试下来证明是可行的。也不知道写STL 的大牛们是怎么想出来的。。汗颜。。。
输入:1 2 3
4 5 6 7,翻转点为 4
输出:4 5 6 7
1 2 3
即要求将序列从D开始反转。在《编程珠玑》中说过关于 [ A B ] 转到 [ B A ] 的情况,采用的方法类似翻手掌的方法,方法如下:
1.先将 A ,B 分别逆序。得到: 3 2 1
7 6 5 4
2.再将整个序列逆序。得到:4 5 6 7 1 2 3。即可
STL的实现中对序列的翻转问题根据迭代器的不同类型进行了区别处理,分为以下三种情况:
1.对于前向迭代器:
采用逐部分替换的方法,先对序列开始处于翻转分界处(如上述的4)逐个进行替换,直到某一个部分替换完成后,再进行适当处理,知道全部替换完成,语言表达不清楚,见如下代码:template<class ForwardIterator,class Distance> void __rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last, forward_iterator_tag,Distance *) { for (ForwardIterator i = middle;;) //从中心分界处开始逐个替换 { _LXX::iter_swap(first,i); ++first; //同时向后移动 ++i; if (first == middle) //前半段先结束 { if (i == last) return; //若此时后半段也结束,则旋转完成 else middle = i; //否则,更新中介点,继续旋转 } else { if (i == last) //若后半段先旋转结束 i = middle; } } }
2.双向迭代器
此时迭代器支持前向与后向移动,可采用分别逆序的办法来实现,此时调用 STL 的 reverse 来实现,注:STL 的reverse 实现方法是从第一个元素与最后一个元素开始进行交换,并向中心收拢。算法复杂度为O(N);此时STL实现旋转比较简单清晰,代码如下:template<class BidirectionIterator,class Distance> void __rotate(BidirectionIterator first, BidirectionIterator middle, BidirectionIterator last, bidirectional_iterator_tag, Distance *) { _LXX::reverse(first, middle); _LXX::reverse(middle, last); _LXX::reverse(first, last); }
3.随机访问迭代器
其实通过上面两种方法, 我们都将旋转的复杂度控制在了 O(N)上, 但是事实上很多元素都不能通过一次交换直接换到目的地,比如双向迭代器的复杂度为 2N。对于随机访问迭代器,STL使用了一些精巧的办法,使得我们能够对每个元素进行一次交换即可放入目的地,此时即将复杂度降到了N,具体代码如下:template<class RandomAcccessIterator,class Distance> void __rotate(RandomAcccessIterator first, RandomAcccessIterator middle, RandomAcccessIterator last, random_access_iterator_tag, Distance *) { Distance n = _LXX::__gcd(last - first, middle - first); //找到总元素数目与前半段元素数目的最大公约数 while (n--) { _LXX::__rotate_cycle(first, last, first + n, middle - first, _LXX::value_type(first)); } } template<class EuclideanRingElement> EuclideanRingElement __gcd(EuclideanRingElement m, EuclideanRingElement n) //使用辗转相除法获取最大公约数 { while (n != 0) { EuclideanRingElement t = m % n; m = n; n = t; } return m; } 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 = initial + shift; while (ptr2 != initial) { <span style="white-space:pre"> </span>//在本段代码中能准确计算出每个元素的目的地址并直接进行一次替换,具体实现如下: *ptr1 = *ptr2; ptr1 = ptr2; if (last - ptr2 > shift) ptr2 += shift; else ptr2 = first + (shift - (last - ptr2)); } *ptr1 = value; }
我个人觉得最大公约数法属于黑科技,用理论解释不清楚。但实际测试下来证明是可行的。也不知道写STL 的大牛们是怎么想出来的。。汗颜。。。
相关文章推荐
- ui router digest 10 time
- python学习笔记(05)
- Candies(差数约分系统-最短路)
- 如何向OpenStack社区贡献代码
- cloudstack内部的VM HA和Host HA
- (java)leetcode Excel Sheet Column Number
- RHEL/CentOS 7安装 PowerDNS(MariaDB) 、PowerAdmin
- 什么是缓存
- Linux关机命令详解
- CF 466C Number of Ways
- Redis简介和简单应用
- Xcode7 上传报错
- OC学习 第二章便利初始化 便利构造器 getter/setter方法
- 乐动力 步数上传 核心分析
- ubuntu下设置网络
- 带图片的Toast提示信息框
- 学习前端比较好的网站
- Swift继承(Inheritance)
- XMPP 服务器 Openfire 的 Emoji 支持问题(进行部分修改)
- 【已解决】Ubuntu输入密码后无法登陆闪屏问题