stl中的rotate算法

《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.csdn.net/sicofield/article/details/8730197


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值