stl中的rotate算法

STL Rotate函数解析
本文详细解析了STL中Rotate函数的三种实现方式:forwardIterator, bidirectionalIterator 和 randomAccessIterator,并探讨了它们各自的效率及背后的数学原理。

《stl源码解析》中rotate函数为了追求效率,将其分了三种情况进行讨论:forwardIterator, bidirectionalIterator, randomInterator


forwardIterator:

template <class ForwardIterator, class Distance>
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)bidirectional iterator

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)random Iterator

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--)
        // 需要执行__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;
}



这里涉及到一个定理(1)(《具体数学》4.8节):

如下的m个数:0 mod m, n mod m, 2n mod m, ... , (m - 1)n mod m 正好包含d次的m/d个数:0,d,2d,..., m - d,其中d = gcd(m, n)。比如当m = 12, n = 8时,d = 4,这个排列就为0,8,4,0,8,4,0,8,4,0,8,4


从数学意义上将要将一个数组进行rotate,每个元素应该满足如下公式:

i := (i - len(A) + len(X)) % len(X) (1)

或者是:

i := (i + len(B)) % len(X)              (2)

其中A表示数组前段,B表述数组后段,X表示整个数组。


从第一个公式看,实际上就是将每个元素往前移len(A)个位置。根据定理1可知,移动了m / d个元素后就回到原点了,这时候需要再选择下一个未移动的数作为起始点。所以一共需要有d次的移动才能达到m个数全部移动一遍。


这三个算法的效率粗略地说都是O(n)算法。

forward iterator大致需要进行n次swap操作,可看成是O(3n),但是在其循环体内还有一些判断。

bidirectional iterator也需要n次swap操作,可看成是O(3n),除了循环条件外没有判断。

random iterator只需要n次赋值即可,可看成是O(n)。

赋值比交换快。。


最后感慨一下stl为了效率真是无所不用其极。


参考文献:

 [1] http://blog.chinaunix.net/uid-10647744-id-3274208.html

 [2] http://blog.youkuaiyun.com/sicofield/article/details/8730197


### C++ STL 常用算法列表及示例 #### 1. **reverse** `reverse` 是一种用于反转序列的算法。它接受两个迭代器参数,分别表示要操作范围的起始位置和终止位置。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v = {1, 2, 3, 4, 5}; reverse(v.begin(), v.end()); for (int i : v) { cout << i << " "; } cout << endl; } ``` 此代码展示了如何通过 `reverse` 函数将向量中的元素顺序颠倒[^1]。 --- #### 2. **rotate** `rotate` 算法可以实现循环移位功能。该函数同样接收三个迭代器作为输入参数,第三个参数指定旋转点的位置。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { vector<int> v = {1, 2, 3, 4, 5}; rotate(v.begin(), v.begin()+2, v.end()); for (int i : v) { cout << i << " "; } cout << endl; } ``` 上述例子中,`rotate` 将从索引为 2 的位置开始的部分移动到前面。 --- #### 3. **for_each** `for_each` 可以对容器内的每一个元素执行特定的操作。它可以配合普通函数或仿函数一起使用。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; void print01(int val) { cout << val << " "; } class Print02 { public: void operator()(int val) const { cout << val << " "; } }; int main() { vector<int> v = {1, 2, 3, 4, 5}; // 使用普通函数 for_each(v.begin(), v.end(), print01); cout << endl; // 使用仿函数 for_each(v.begin(), v.end(), Print02()); cout << endl; } ``` 这段代码演示了两种方式调用 `for_each` 来打印容器的内容[^3]。 --- #### 4. **transform** `transform` 能够基于某种转换逻辑生成新的数据集合。它的基本形式如下: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; // 定义一个简单的加一运算 int addOne(int x) { return x + 1; } int main() { vector<int> input = {1, 2, 3, 4, 5}; vector<int> output(input.size()); transform(input.begin(), input.end(), output.begin(), addOne); for (auto num : output) { cout << num << " "; } cout << endl; } ``` 这里定义了一个简单的一元变换函数来增加数值,并将其应用于整个数组。 --- #### 5. **copy** `copy` 把源区间的数据复制到目标区域中去。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; class MyPrint { public: void operator()(int val) { cout << val << " "; } }; void testCopy() { vector<int> source = {1, 2, 3, 4, 5}; vector<int> destination(source.size()); copy(source.begin(), source.end(), destination.begin()); for_each(destination.begin(), destination.end(), MyPrint()); cout << endl; } int main() { testCopy(); return 0; } ``` 这个实例说明了怎样利用 `copy` 实现两组数据之间的拷贝过程[^4]。 --- #### 6. **fill 和 fill_n** 这两个方法用来快速填充一段连续内存空间为固定值。 ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; void myPrint(int val) { cout << val << " "; } void testFill() { vector<int> vec(10); fill(vec.begin(), vec.end(), 99); for_each(vec.begin(), vec.end(), myPrint); cout << endl; } int main() { testFill(); return 0; } ``` 在此处,我们看到的是如何运用 `fill` 方法把所有的整数都设置成相同的初始值[^5]。 --- #### 总结 以上列举了几种常见的 STL 算法以及它们的应用场景。每种算法都有各自的特点与适用场合,在实际开发过程中可以根据需求灵活选用合适的工具完成任务。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值