编程珠玑:n元一维向量左旋移i个位置

问题描述:将一个n元一维向量左旋移i歌个位置。例如:当n=8且i=3时,向量abcdefgh旋转为defghabc。使用一个n元的中间向量在n步内完成该工作。能否仅用数十字节的额外存储空间,在正比于n的时间内完成该旋移?


解答思路:向量的旋移,其实就是数组的移动。像a[] = {1,2,3},左旋移一位后就变成了a[] = {2,3,1}。
该问题可以通过以下方式解决:

  • 首先将n的前i个元素复制到一个临时数组中
  • 然后将剩余的n - i个元素左移i个位置
  • 最后将最初的i个元素从临时数组中复制到n中剩余的位置即可

    代码如下:

void moveLeft(char a[], int m)//左移m位
{
    int len = strlen(a);
    for(int i = 0; i < m; i++)//前m位装入temp数组
        char temp[i] = a[i];
    for(int i = m; i < len; i++)//剩余部分左移m位
        a[i - m] = a[i];
    for(int i = len - m, j = 0; i < len; i++, j++)//最初的m个元素复制到剩余的位置
        a[i] = temp[j];
}

该种方法使用了额外的m个空间,存储空间消耗过大。另一种消耗较小存储空间的方法是定义一个函数,将n向左旋移一个位置,然后调用该函数i次即可,但该方法运行时间过长。

给出该方法的代码:

void move(char a[])//每次只移动一位
{
    char temp = a[0];
    for(int i = 1; i < sizeof(a); i++)
        a[i - 1] = a[i];
    a[i] = temp;
}

void moveLeft(char a[], int m)//向左移动m次
{
    for(int i = 0; i < m; i++)
        move(a, m);
}
以上两种方法虽然都解决了移动问题,但多少都有些不足。方法一使用了过多的存储空间,方法二多次调用函数,导致运行时间长。二者分别向时间复杂度和空间复杂度做了相应的妥协。

下面给出最高效的方法。书上是这样描述的:该方法有点像精巧的杂技动作,移动x[0]到临时变量temp,然后移动x[i]至x[0], x[2i]至x[i],以此类推(即所有下标对n取模),直至返回到取x[0]中的元素,此时改为从temp取值然后终止过程。

下面给出代码:

void moveLeft(char a[], int m)
{
    int len = strlen(a);
    for(int i = 0; i < gcd(len, m); i++)//gcd()是求出两数的最大公约数
    {
        char temp = a[i];
        int j = i;
        for(;;)
        {
            int k = j + m;
            if(k >= len)
                k -= len;
            if(k == i)
                break;
            a[j] = a[k];
            j = k; 
        }
        a[j] = temp;
    }
}

int gcd(int m, int n)//求两数的最大公约数
{
    while(m != n)
    {
        if(m > n)
            m -= n;
        else
            n -= m;
    }
    return m;
}

总结:该方法只使用了一个额外的存储空间,而且还将移动次数控制到了最小。时间复杂度为向量总长度与移动位数的最大公约数。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值