Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1395916
  • 博文数量: 143
  • 博客积分: 10005
  • 博客等级: 上将
  • 技术积分: 1535
  • 用 户 组: 普通用户
  • 注册时间: 2006-10-23 17:25
个人简介

淡泊明志 宁静致远

文章分类

全部博文(143)

文章存档

2011年(2)

2009年(1)

2007年(22)

2006年(118)

我的朋友

分类: C/C++

2007-06-21 17:32:01

【我解C语言面试题系列】004 数组的循环右移问题

数组的循环右移

 

【题目】有一个整数数组,现要求实现这个整数数组的循环右移。如:12345 则循环右移两位后结果是:45123

 

方法一:(最最容易想到的办法)

void RightCircleShift_00(int buffer[],int shift)

{

    int i,j,tt;

   

    for(i=0;i

    {

       tt = buffer[ARRSIZE-1];

       for(j=ARRSIZE-1;j>0;j--)

           buffer[j] = buffer[j-1]; 

       buffer[0] = tt;

    }

}

这个办法是用两个循环来控制,内循环每次向右移动一位,外循环则用来限制移动的位数。算法需要执行 ARRSIZE * ShiftValue次,时间复杂度是O( N2 )。

 

方法二:(由方法一得到的递归程序)

void RightCircleShift_01(int buffer[],int shift)

{

    int *p,tt;

   

    tt = *(buffer+ARRSIZE-1);

 

    for(p = buffer+ARRSIZE-1;p > buffer;p--)

       *p = *(p-1);

   

    *buffer = tt;

 

    shift--;

    if(shift > 0)

        RightCircleShift_00(buffer,shift);

}

这个程序跟方法一类似,区别就是它是用递归来实现的。同样需要执行ARRSIZE * ShiftValue次,时间复杂度也是O( N2 )。

 

方法三 

void RightCircleShift_02(int buffer[],int shift)

{

    int *title,*r,*p;

   

    if(shift == 0)

       return;

   

    shift = shift % ARRSIZE;

   

    title = (int *)malloc(sizeof(int)*shift);

    if( title == NULL )

       return;

   

    r = buffer + (ARRSIZE - shift);

    memcpy(title, r, shift * sizeof(int));

 

    p = buffer + shift;

    memmove(p, buffer, (ARRSIZE - shift) * sizeof(int));

    memcpy(buffer, title, shift * sizeof(int));

   

    free(title);

}

这个算法需要移动位数大小的临时辅助空间。如需移动两位,则申请两个的空间,然后把从右边起的两个元素拷贝的临时辅助空间,然后前面的元素向后移动两位,最后再把临时空间里面的两个元素拷贝到前面的两位即可完成循环右移。需要执行 ARRSIZE次,时间复杂度是O( N )。

 

方法四:

void RightCircleShift_03(int buffer[],int shift)

{  

    if(shift <= 0)

       return;

 

    if( (shift & 1) == 0)

    {

       BufferShiftEvenNumber(buffer,shift-1);

       BufferShiftEvenNumber(buffer,1);

    }

    else

       BufferShiftEvenNumber(buffer,shift);

       

}

void BufferRightShiftEvenNumber(int buffer[],int shift)

{

    int i,j,tt,res;

 

    res = buffer[0];

    for (i=0,j=0; i

    {

       tt = res;

       res = buffer[(j+shift)%ARRSIZE];

       buffer[(j+shift)%ARRSIZE] = tt;

       j = (j+shift) % ARRSIZE;

    }

}

这个算法并不需要开额外的辅助空间,而且时间复杂度同样也是O( N )。BufferRightShiftEvenNumber函数是这个算法的核心,该函数只能实现每次向右移动奇数位元素。如果要移动的位数是偶数的时候就需要把该数分解为两个奇数,n = (n 1) + 1 。

 

方法五:

void RightCircleShift_04(int buffer[],int shift)

{

    shift %= ARRSIZE;

    ReverseArray(buffer,1,ARRSIZE);

    ReverseArray(buffer,1,shift);

    ReverseArray(buffer,shift+1,ARRSIZE);

}

void ReverseArray(int buffer[],int start,int end)

{

    int i,tt;

 

    if(end > ARRSIZE)

       return;

 

    start -= 1;

    end -= 1;

    while(start < end)

    {

       tt = buffer[start];

       buffer[start++] = buffer[end];

       buffer[end--] = tt;

    }

}

这个办法也是很不错,需要两次扫描数组即可,时间复杂度O(N)。这个办法跟 方法四 移动偶数个元素的情况一样,都是需要两次扫描数组。当然如果是移动奇数个元素的话,则不如方法四有效,方法四只需要扫描一次数组就可以了。

算法是网友 luodongshui 提出的:

1、先将整个数组反转。

2、然后再反转前shift个元素。

3、接着再反转后N-shift个元素。


阅读(4539) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

zanget2010-06-17 01:36:53

方法四: 个人觉得不是基数偶数的差别,关键在于j超过最大值后 是否 再次回归到下标0:若是,则下次继续同样的替换导致错误,否则会正确执行。 所以关键字在于判断回归值是否正确。