2.17 数组循环移位
对长度为 n 的数组 (ab) 左移 k 位,最直接的方法,就是用 stl 的 rotate 函数( stl 针对三种不同的迭代器,提供了三个版本的 rotate )。但在某些情况下,用 stl 的 rotate 效率极差。对数组循环,可以采用:
① 动态分配一个同样长度的数组,将数据复制到该数组并改变次序,再复制回原数组。
② 利用 ba=(br )r (ar )r =(ar br )r ,通过三次反转字符串。
③ 分组交换(尽可能使数组的前面连续几个数为所要结果):
若 a 长度大于 b ,将 ab 分成 a0 a1 b ,交换 a0 和 b ,得 ba1 a0 ,只需再交换 a1 和 a0 。
若 a 长度小于 b ,将 ab 分成 ab0 b1 ,交换 a 和 b0 ,得 b0 ab1 ,只需再交换 a 和 b0 。
通过不断将数组划分,和交换,直到不能再划分为止。分组过程与求最大公约数很相似。
④ 所有序号为 (i+t*k) % n (i 为指定整数, t 为任意整数 ) ,会构成一个循环链(共有 **(n,k) 个, ** 为n 、 k 的最大公约数),每个循环链上的元素只要移动一个位置即可,总共交换了 n 次。
stl 的 rotate 的三种迭代器,分别使用了后三种方法。
多数情况下,前三种方法效率都比较高,第一种方法过分要求内存,第四种方法,元素间平均交换次数最少,理论上效率应该是最高的,但在 n 和 k 都很大时,其效率相当差(由于每次交换访问的内存不连续,在 n 和 k 比较大时,内存访问开销很大( cache line 命中率很低,又不断跨页访问内存)。)。方法三的平均交换次数要少于方法二,但判断次数相对要多,效率相差不是太大,在大数组时方法三效率比较高,但同一个小数组左移几十万次,应该是方法二效率略高,毕竟整个数组都可以被 cache ,内存访问开销小,判断次数对效率影响较大。
- // 2.17_rotate.cpp by flyinghearts#qq.com
- #include <iostream>
- #include <vector>
- #include <algorithm>
- #include <ctime>
- #include <cstdlib>
- using namespace std ;
- /*
- left_shift_0 通过复制到临时数组
- left_shift_1 三次反转
- left_shift_2 分组交换
- left_shift_3 循环交换
- left_shift_4 循环交换(先求最大公约数,确定次数)
- left_shift_5 stl 的rotate
- test 函数:
- 数组ff 存放要测试的函数
- count 测试次数
- arr_len 动态生成的测试数组的长度
- show 是否显示每次测试用时
- */
- typedef void MT ( int * , unsigned , int );
- MT left_shift_0 , left_shift_1 , left_shift_2 , left_shift_3 ;
- MT left_shift_4 , left_shift_5 ;
- void test ( MT * ff [], unsigned sz , unsigned count = 1 , unsigned arr_len = 1e6 , bool show = 0 );
- int main ()
- {
- MT * ff []={ left_shift_0 , left_shift_1 , left_shift_2 , left_shift_3 , left_shift_4 , left_shift_5 };
- unsigned sz = sizeof ( ff )/ sizeof ( ff [ 0 ]);
- test ( ff , sz , 4e4 , 1e3 );
- test ( ff , sz , 4 , 2e7 , 1 );
- test ( ff , sz , 4e6 , 100 );
- }
- void left_shift_0 ( int * arr , unsigned n , int k )
- {
- if ( n == 0 ) return ;
- k %= n ;
- if ( k == 0 ) return ;
- if ( k < 0 ) k += n ;
- vector < int > a ( n );
- int * const p =& a [ 0 ];
- copy ( arr + k , arr + n , p );
- copy ( arr , arr + k , p + n - k );
- copy ( p , p + n , arr );
- }
- void my_reserve ( int * arr , int n )
- {
- int * p = arr , * q = arr + n - 1 ;
- int tmp ;
- while ( p < q ){
- tmp = * p ;
- * p ++ = * q ;
- * q -- = tmp ;
- }
- }
- void left_shift_1 ( int * arr , unsigned n , int k )
- {
- if ( n == 0 ) return ;
- k %= n ;
- if ( k == 0 ) return ;
- if ( k < 0 ) k += n ;
- my_reserve ( arr , k );
- my_reserve ( arr + k , n - k );
- my_reserve ( arr , n );
- }
- void left_shift_2 ( int * arr , unsigned n , int k )
- {
- if ( n == 0 ) return ;
- k %= n ;
- if ( k == 0 ) return ;
- if ( k < 0 ) k += n ;
- int tmp ;
- int * low = arr , * last = arr + n , * mid = arr + k , * high = mid ;
- while ( mid != last ){
- tmp = * low ;
- * low ++ = * high ;
- * high ++ = tmp ;
- if ( low == mid ) mid = high ;
- else if ( high == last ) high = mid ;
- }
- }
- void left_shift_3 ( int * arr , unsigned n , int k )
- {
- if ( n == 0 ) return ;
- k %= n ;
- if ( k == 0 ) return ;
- if ( k < 0 ) k += n ;
- int tmp , count = n ;
- int * first = arr , * last = arr + n , * cur = first , * next = first ;
- tmp = * first ;
- while ( count --){
- cur = next ;
- next += k ;
- if ( next >= last ) next -= n ;
- if ( next != first ) * cur =* next ;
- else {
- * cur = tmp ;
- tmp = * ++ first ;
- next = first ;
- }
- }
- }
- unsigned my_** ( unsigned a , unsigned b )
- {
- unsigned min = a , max = b , tmp ;
- if ( a > b ) { min = b ; max = a ;}
- while ( min ){
- tmp = min ;
- min =( max - min )% min ;
- max = tmp ;
- }
- return max ;
- }
- void left_shift_4 ( int * arr , unsigned n , int k )
- {
- if ( n == 0 ) return ;
- k %= n ;
- if ( k == 0 ) return ;
- if ( k < 0 ) k += n ;
- int tmp ;
- unsigned i , j ;
- int * first = arr , * last = arr + n , * cur = first , * next = first ;
- unsigned count1 = my_** ( k , n );
- unsigned count2 = n / count1 ;
- for ( i = 0 ; i < count1 ; ++ i , ++ first ){
- tmp =* first ;
- next = first ;
- for ( j = 0 ; j < count2 ; ++ j ){
- cur = next ;
- next += k ;
- if ( next >= last ) next -= n ;
- * cur =* next ;
- }
- * cur = tmp ;
- }
- }
- void left_shift_5 ( int * arr , unsigned n , int k )
- {
- if ( n == 0 ) return ;
- k %= n ;
- if ( k == 0 ) return ;
- if ( k < 0 ) k += n ;
- rotate ( arr , arr + k , arr + n );
- }
- void test ( MT * ff [], unsigned sz , unsigned count , unsigned arr_len , bool show )
- {
- clock_t tb , te ;
- unsigned i , j ;
- int k ;
- vector < int > arr ( arr_len );
- vector < clock_t > total ( sz );
- srand ( time ( 0 ));
- for ( i = 0 ; i < count ; ++ i ){
- k = rand ();
- k -= rand ();
- if ( show ) cout << "K: " << k << "/n" ;
- for ( j = 0 ; j < sz ; ++ j ){
- tb = clock ();
- ff [ j ](& arr [ 0 ], arr_len , k );
- te = clock ();
- total [ j ] += te - tb ;
- if ( show ) cout << j << " : " << te - tb << " ms/n" ;
- }
- if ( show ) cout << endl ;
- }
- cout << "/nTotal: /n" ;
- for ( j = 0 ; j < sz ; ++ j ){
- cout << j << " " << total [ j ]<< " ms/n" ;
- }
- cout << endl ;
- }
原贴地址:http://blog.csdn.net/flyinghearts/archive/2010/05/18/5605928.aspx
延伸阅读: