C/C++面试之算法系列--整数数组的循环右移

来源:互联网 发布:虚无世界2java 编辑:程序博客网 时间:2024/04/29 05:11

【转摘序】方法四:整体翻转,再局部翻转,没有申请额外的空间,从两头往中间互换,移动的次数少,时间效率也高。算法新颖,在面试中可以算最好的答案,关键是要能体现你的与众不同,当然了通俗的算法你也要知道,这样才能让自己不落入俗套。

 

整体和局部翻转的思想在“算法系列--以单词为最小单位翻转字符串 ”算法四以及“从"反转32 位数"算法题分析面试策略 ”都有应用,由此可见对于算法题,要充分理解其精髓,便可举一反三,以一推十,以不变应万变!

×××××××××××××××××××××××××

【题目】有一个整数数组,现要求实现这个整数数组的循环右移。如:

 12345 则循环右移两位后结果是:45123

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

void RightCircleShift_00(int buffer[],int shift)

{

    int i,j,tt;

   

    for(i=0;i<shift;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;

 // 上面实现右移一位

 

// 每移动一次减1,未移完接着调用

    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;

   

// 循环的圈数可能大于1

    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_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 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 提出的:

<!--[if !supportLists]-->1<!--[endif]-->先将整个数组反转。

<!--[if !supportLists]-->2<!--[endif]-->然后再反转前shift个元素。

<!--[if !supportLists]-->3<!--[endif]-->接着再反转后N-shift个元素。

 

参考资料:

http://blog.csdn.net/ammana_babi/archive/2007/06/21/1660973.aspx