数组移动


比如要将数组 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%不到。

posted @ 2013-05-19 09:04  herizai  阅读(636)  评论(0编辑  收藏  举报