zoukankan      html  css  js  c++  java
  • 数组循环移位中的学问

      始终相信能听明白和能说明白是两种不同的境界,如果对于一件问题,你说你明白了,但是你却不能说明白,那在我眼里就会有这样的一个事实,那就是:你并没有真正地明白。当然,也存在这种情况,那就是说是说明白了,但是未必对,这正是我担心的情况,所以还请阅读过我所有文章的朋友们指出我文章中的错误。谢谢你们……

    1.问题定义

      设计一个算法,把一个含有n个元素的数组A循环右移k位,要求时间复杂度是O(n)的。举个例子说明一下算法要达到的效果:数组中包含的元素为123456,现在要循环右移2位,则变成了561234 。

    2.解决方案

    2.1 朴素的思路

      往往是这样,朴素的思路虽不能满足题目的要求,但是确可能是优化的依据。对于这个问题朴素的解法是,每次完成一次右移,并且完成一次右移的时间复杂度是O(n),这样的操作要进行k次,所以时间复杂度是O(n*k)。但是现在想一想,既然已经知道要右移的位数k,那么要是还一步一步的移是不是显得有点笨了呢。为什么不一步到位呢?

    2.2 空间换时间

      对于算法上的优化,以空间换时间是一种比较常规的办法了。可以借助一个辅助数组T,先把数组A的n-k+1到n位数组中的元素存储到T中,然后再把数组A中的1到n-k位数组元素存储到辅助数组T中,然后将数组T中的元素复制回数组A,这样就完成了数组的循环右移,时间复杂度为O(n),但是呢,却增加了O(n)的空间复杂度。如果要求最多只能允许使用两个辅助空间呢?这可就要想一想能不能进一步优化了。

    2.3 利用“翻转”完成优化

      考虑一下数组A中元素123456循环右移2位到底是怎么个情况!!!可不可以这样实现呢?将数组A分成两个部分:A[0~n-k-1] 和 A[n-k~n-1] ,将这两个部分分别翻转,然后放在一起在翻转(逆序)。具体是这样的:

    (1)翻转1234:123456 ---> 432156

    (2)翻转56:     432156 ---> 432165

    (3)翻转432165:432165 ---> 561234

    看看吧,确实完成了翻转的操作。现在来看一下代码(灰常简单):

     1 //逆序
     2 void Reverse(int A[],int b,int e)
     3 {
     4     for(;b < e;b++,e--)
     5     {
     6         int temp = A[b];
     7         A[b] = A[e];
     8         A[e] = temp;
     9     }
    10 }
    11 //循环右移
    12 void RightShift(int A[],int,int n,int k)
    13 {
    14     Reverse(A,0,n-k-1);
    15     Reverse(A,n-k,n-1);
    16     Reverse(A,0,n-1);
    17 }

    现在可以分析一下时空复杂度了,时间复杂度显然是O(n),主要是完成翻转(逆序)操作,并且只用了一个辅助空间。显然这是一个不错算法哦。。。

    注:

    细心的读者可能就发现,这里的右移并没有说小于n呀,那么一个k>n那就会出现问题,经过简单的分析,我们发现如果A[]={1,2,3,4,5,6},数组长度是6,那么循环右移7位和循环右移1位是一样的,所以只需要把函数RightShift()中的k取值为k=k%n。如此一来问题就解决了。

    学习中的一点总结,欢迎拍砖哦^^

  • 相关阅读:
    内存问题定位与解决
    CPU问题定位与解决
    数据库优化案例——————某市中心医院HIS系统
    系统隐形杀手——阻塞与等待
    SQL Server常见问题介绍及快速解决建议
    CVTE实习感想--2014.10秋招
    Spring AOP的理解
    一些Java面试问题
    举几个大数据的例子
    Java中乐观锁与悲观锁的实现
  • 原文地址:https://www.cnblogs.com/BeyondAnyTime/p/2585789.html
Copyright © 2011-2022 走看看