zoukankan      html  css  js  c++  java
  • 数组移动


    比如要将数组 int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    的元素循环右移动4
    那么 结果为 {9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8};
    显然最高效的方法是
    int temp=a[1];a[1]=a[9];a[9]=a[5];a[5]=temp;
    temp=a[2];a[2]=a[10];a[10]=a[6];a[6]=temp;
    temp=a[3];a[3]=a[11];a[11]=a[7];a[7]=temp;
    temp=a[4];a[4]=a[12];a[12]=a[8];a[8]=temp;
    数据总拷贝次数(3+1)*4;

    又 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12
    -> 5, 6, 7, 8, 9,10,11,12, 1, 2, 3, 4,

    n=12;move_right=8;

    同理 temp=a[1];a[1]=a[5];a[5]=[9];a[9]=temp;
    ...


    分析不难得出将一个数组右移动m(m%=n)最少数据拷贝次数为(n/MaxCommonDivisor(n,m)+1)*MaxCommonDivisor(n,m);
    即n+MaxCommonDivisor(n,m);
    MaxCommonDivisor(n,m)为n,m的最大公倍数

    void move_array(int data[], int n, int m)
    {
    int i, j;
    m = m % n;
    if(m == 0) return;
    for(i = 0; i < n - m; i += m)
    {
    for(j = i; j < i + m && j < n - m; j++)
    {
    Swap( data[j], data[j + m]);
    }
    }
    move_array(data + n - m, m, m - n % m);
    }

    群论:
    每个置换都可以表示成不相交的轮换的乘积;
    每个置换都可以表示成一些对换的乘积;
    每个偶置换都可以表示成一些长度为3的轮换的乘积.
    专访邓凡平:Android开发路上的快速学习之道对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理

    //额,貌似得开个辅助数组
    #include <iostream>
    #include <iterator>
    #include <algorithm>
    using namespace std;

    void move_array(int data[], int n, int m)
    {
    char* flag = new char[n];
    memset(flag, 0, n);
    for (int i = 0; i < n; ++i)
    {
    int last = i, j = (i + m + n) % n, t = data[i];
    if (flag[i]) continue;
    for (; j != i; last = j, j = (j + m + n) % n)
    {
    data[last] = data[j];
    flag[last] = 1;
    }
    data[last] = t;
    flag[last] = 1;
    }
    delete [] flag;
    }

    int main(void)
    {

    int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    int n = sizeof(a)/sizeof(a[0]);

    for (int i = 0; i < 12; ++i)
    {
    move_array(a, n, 1);
    copy(a, a+n, ostream_iterator<int>(cout, " "));
    cout << endl;
    }
    move_array(a, n, 4);
    copy(a, a+n, ostream_iterator<int>(cout, " "));
    cout << endl;
    move_array(a, n, -1);
    copy(a, a+n, ostream_iterator<int>(cout, " "));
    cout << endl;

    return 0;
    }

    //这个不用...
    void move_array(int data[], int n, int m)
    {
    for (int i = 0; i < m; ++i)
    {
    int last = i, j = (i + m + n) % n, t = data[i];
    for (; j != i; last = j, j = (j + m + n) % n)
    {
    data[last] = data[j];
    }
    data[last] = t;
    }
    }
    对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理

    waizqfor
    waizqfor
    等级:
    #6 得分:0 回复于: 2009-01-19 19:13:58
    引用 2 楼 baihacker 的回复:
    群论:
    每个置换都可以表示成不相交的轮换的乘积;
    每个置换都可以表示成一些对换的乘积;
    每个偶置换都可以表示成一些长度为3的轮换的乘积.

    貌似不是最优解啊

    貌似我移动了n+abs(m)次(m!=0)

    int main()
    {
    int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    size_t n=sizeof(a)/sizeof(a[0]);
    std::copy(a,a+n,ostream_iterator<int>(cout," "));
    std::cout<<std::endl;
    std::rotate(a,a+8,a+n);
    std::copy(a,a+n,ostream_iterator<int>(cout," "));
    }

    //这个不用...
    void move_array(int data[], int n, int m)
    {
    for (int i = 0; i < m; ++i)
    {
    int last = i, j = (i + m + n) % n, t = data[i];
    for (; j != i; last = j, j = (j + m + n) % n)
    {
    data[last] = data[j];
    }
    data[last] = t;
    }
    }

    #include <iostream>
    #include <iterator>
    #include <algorithm>
    using namespace std;
    int counts;
    void move_array(int data[], int n, int m)
    {
    for (int i = 0; i < m; ++i)
    {
    int last = i, j = (i + m + n) % n, t = data[i];counts++;
    for (; j != i; last = j, j = (j + m + n) % n)
    {
    data[last] = data[j];counts++;
    }
    data[last] = t;counts++;
    }
    }


    int main(void)
    {

    int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,13};
    int n = sizeof(a)/sizeof(a[0]);

    move_array(a, n, 4);
    copy(a, a+n, ostream_iterator<int>(cout, " "));
    cout << endl;
    cout<<counts<<endl;

    getchar();

    return 0;
    }

    运行结果counts=56
    不是13+MaxCommonDivisor(13,4)

    #include <iostream>

    template <class T>
    class LoopArray
    {
    public:
    LoopArray(unsigned int num_elements)
    :mpData(new T[num_elements])
    ,mNumElements(num_elements)
    ,mCurrentIndex(0)
    {}

    ~LoopArray()
    {if ( NULL != mpData ) delete []mpData;}

    T& operator[](unsigned int index)
    { return mpData[(index+mCurrentIndex)%mNumElements]; }

    const T& operator[](unsigned int index) const
    { return mpData[(index+mCurrentIndex)%mNumElements]; }

    void RightShift(unsigned int num_shifts)
    { mCurrentIndex += num_shifts;}

    unsigned int Size() const
    { return mNumElements;}

    private:
    // no implementations
    LoopArray(const LoopArray& rhs);
    LoopArray& operator=(const LoopArray& rhs);

    T* mpData;
    int mNumElements;
    int mCurrentIndex;
    };

    //LoopArray<> Tests
    int main(int argc, char* argv[])
    {
    using namespace std;

    const unsigned int nShifts = 4;
    const unsigned int nElements = 12;

    LoopArray<int> anArray(nElements);
    for (int i=0; i<nElements; ++i) anArray[i]=i+1;

    cout << "Before shift: ";
    for (unsigned int i=0; i<anArray.Size(); ++i) cout<<anArray[i]<<" ";
    cout <<endl;

    anArray.RightShift(nShifts);
    cout << "After shift "<<nShifts<<": ";
    for (unsigned int i=0; i<anArray.Size(); ++i) cout<<anArray[i]<<" ";
    cout <<endl;

    }

    //临走前发一个代码,m >= 0
    //楼主测试一下...
    int gcd(int a, int b)
    {return b ? gcd(b, a%b) : a;}

    void move_array(int data[], int n, int m)
    {
    int cnt = gcd(n, m);
    for (int i = 0; i < cnt; ++i)
    {
    int last = i, j = (i - m + n) % n, t = data[i];
    for (; j != i; last = j, j = (j - m + n) % n)
    {
    data[last] = data[j];
    }
    data[last] = t;
    }
    }

    对了...15楼的代码在m=0的时候要直接返回...

    再作简单处理就能适合左移和右移的了...我以前的发成了左移了...
    对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理

    cqqqq
    cqqqq

    #include <iostream>
    #include <iterator>

    using namespace std;
    int counts;
    //最大公约数
    int MaxCommonDivisor(int a,int b)
    {
    int temp;
    while(b)
    {
    temp=a%b;
    a=b;
    b=temp;
    }
    return a;
    }
    void move_array(int arr[], int n, int m)
    {
    int i,j,mcd,temp,k;
    mcd=MaxCommonDivisor(n,m);
    for ( i = 0; i < mcd; ++i)
    {
    temp=arr[i];k=i;counts++;
    while(1)
    {
    j=i+n-m;
    j%=n;
    if(j==k){arr[i]=temp;i=j;counts++;break;}
    arr[i]=arr[j];counts++;
    i=j;
    }
    }
    }

    int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15};
    int main(void)
    {


    int n = sizeof(a)/sizeof(a[0]);

    move_array(a, n, 10);
    copy(a, a+n, ostream_iterator<int>(cout, " "));
    cout << endl;
    cout<<counts<<endl;

    getchar();

    return 0;
    }

    终于搞定了n+MaxCommonDivisor(n,m)次数据拷贝的算法
    对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理

    把函数设计为循环向左移动m个位置.

    m > 0时向左循环移动m个单位
    m < 0时向右循环移动-m个单位
    m = 0时无动作

    n <= 1时无动作

    对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理

    wjdlt_1997
    wjdlt_1997
    等级:
    #21 得分:0 回复于: 2009-01-19 21:31:07
    这么深奥的问题。。
    允许用指针不?

    你可以直接用STL里的rotate,原理可以自己看源码


    C/C++ codeint main()
    {
    int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    size_t n=sizeof(a)/sizeof(a[0]);
    std::copy(a,a+n,ostream_iterator<int>(cout," "));
    std::cout<<std::endl;
    std::rotate(a,a+8,a+n);
    std::copy(a,a+n,ostream_iterator<int>(cout," "));
    }

    STL里面的rotate做了非常多的检查,且效率不高

    C/C++ codeint main()
    {
    int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    size_t n=sizeof(a)/sizeof(a[0]);
    std::copy(a,a+n,ostream_iterator <int>(cout," "));
    std::cout < <std::endl;
    std::rotate(a,a+8,a+n);
    std::copy(a,a+n,ostream_iterator <int>(cout," "));
    }

    STL里面的rotate做了非常多的检查,且效率不高

    你看的是什么版本的?如果STL效率不高还有哪个效率高?
    STL检查大多是静态类型检查和Debug状态下的检查,绝对不会影响Release的性能。
    对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理

    加减互为逆运算

    最高效的是移动指针
    对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理

    cqqqq
    cqqqq
    等级:
    #27 得分:0 回复于: 2009-01-20 03:18:01
    题目就是移动数组数据。不是移动指针。
    STL中的数据拷贝次数,我查了下,远大于n+gcd(n,m);

    #include <stdio.h>
    //数据拷贝次数为n+gcd(n,m)的算法

    //最大公约数GreatestCommonDivisor
    int gcd(int a, int b)
    {
    return b ?gcd(b, a%b) : a;
    }
    //m左移单位量
    void move_array(int arr[], int n, int m)
    {
    int i,j,temp,k;
    m%=n;m+=n;
    k=gcd(n,m);
    while(k--)
    {
    j=k;temp=arr[j];
    while(1)
    {
    i=j;j=(j+m)%n;
    if(j==k)
    {
    arr[i]=temp;break;
    }
    arr[i]=arr[j];
    }
    }
    }
    int main()
    {
    int a[]={0,1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14};
    size_t n=sizeof(a)/sizeof(a[0]);
    for (int i=0;i<n;++i)printf("%02d ",a[i]);printf("\n");
    move_array(a,n,5);
    for (int i=0;i<n;++i)printf("%02d ",a[i]);printf("\n");
    getchar();
    }

    int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    的元素循环右移动4
    结果为{9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8};

    用memmove一次移动多个数组元素:
    int tmp[4];
    memmove(tmp,a+8,sizeof(int)*4);
    memmove(a+4,a,sizeof(int)*8);
    memmove(a,tmp,sizeof(int)*4);
    //注意不能用memcpy

    public class MainClass {
    /**
    * 求最大公约数
    */
    public static int gcd(int a, int b){
    return b == 0 ? a : gcd(b, a % b);
    }
    /**
    * 将数组num内元素左移step位
    */
    public static void move(int[] num, int step){
    int len = num.length;
    // 左移-(n*len+m)格相当于左移len-m格 (n为任意非负整数,m为小于len的非负整数)
    while (step < 0) step += len;
    int part = gcd(num.length, step);
    int count = num.length / part;
    int i, j, p_to, p_from, temp;
    for (i = 0; i < part; ++i){
    p_to = i;
    temp = num[p_to];
    for (j = 0; j < count - 1; ++j){
    p_from = (p_to + step) % num.length;
    num[p_to] = num[p_from];
    p_to = p_from;
    }
    num[p_to] = temp;
    }
    }
    public static void main(String[] args) {
    int[] num = new int[]{1,2,3,4,5,6,7,8,9,10,11,12};
    move(num, -3);
    for (int a: num)
    System.out.print(" " + a);
    }
    }

    题目就是移动数组数据。不是移动指针。
    STL中的数据拷贝次数,我查了下,远大于n+gcd(n,m);

    不知道你是测试得出的结论还是查标准得到的规定?
    对我有用[0] 丢个板砖[0] 引用 | 举报 | 管理

    比如要将数组 int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    的元素循环右移动4
    那么 结果为 {9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8};
    显然最高效的方法是
    int temp=a[1];a[1]=a[9];a[9]=a[5];a[5]=temp;
    temp=a[2];a[2]=a[10];a[10]=a[6];a[6]=temp;
    temp=a[3];a[3]=a[11];a[11]=a[7];a[7]=temp;
    temp=a[4];a[4]=a[12];a[12]=a[8];a[8]=temp;
    数据总拷贝次数(3+1)*4;


    数组应该是a[0]开始,没有a[12]吧? 该方法的效率比较高
    对我有用[0] 丢个板砖[1] 引用 | 举报 | 管理

    usr_src
    usr_src
    等级:
    #36 得分:0 回复于: 2009-07-28 22:38:27
    引用 34 楼 zhao4zhong1 的回复:
    cqqqq你没给我50分说明你的编程水平一般。





    比如要将数组 int a[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
    的元素循环右移动4
    那么 结果为 {9, 10, 11, 12, 1, 2, 3, 4, 5, 6, 7, 8};
    显然最高效的方法是
    int temp=a[1];a[1]=a[9];a[9]=a[5];a[5]=temp;
    temp=a[2];a[2]=a[10];a[10]=a[6];a[6]=temp;
    temp=a[3];a[3]=a[11];a[11]=a[7];a[7]=temp;
    temp=a[4];a[4]=a[12];a[12]=a[8];a[8]=temp;
    数据总拷贝次数(3+1)*4;


    数组应该是a[0]开始,没有a[12]吧? 该方法的效率比较高

    一步移到位了!!~效率不错~!!!

    谁不服,把你的代码放在一个1000000次循环里面实际运行同时用秒表掐一下。
    我敢说我的只比用汇编写的慢10%不到。

  • 相关阅读:
    [CSAPP笔记][第九章虚拟存储器][吐血1500行]
    [CSAPP笔记][第六章存储器层次结构]
    [CSAPP笔记][第八章异常控制流][呕心沥血千行笔记]
    好吧,刚把CSDN搬家到博客园。。记录一发
    [CSAPP笔记][第二章信息的表示和处理]
    综合练习:词频统计
    组合数据类型综合练习:英文词频统计
    熟悉常用的Linux操作
    1.大数据概述
    语义分析
  • 原文地址:https://www.cnblogs.com/herizai/p/3086502.html
Copyright © 2011-2022 走看看