【问题标题】:Algorithm to rotate an array in linear time以线性时间旋转数组的算法
【发布时间】:2011-05-26 07:55:21
【问题描述】:

如何仅在线性时间内使用swap 函数将整数数组旋转i 次。

【问题讨论】:

  • 请详细说明您的问题。数组有什么维度? “旋转数组”是什么意思?举个例子输入输出。考虑在适当的情况下使用标点符号和大写字母。
  • 你试过什么?它怎么不工作? IOW,在我们提供帮助之前,您需要先尝试(我们不会写给您)
  • @sven 假设输入数组是 {1,2,3,4,5} 一次右旋转后输出数组是 {5,1,2,3,4}。
  • @kevin 将它旋转 i 次,我们可以在 o(n^2) 次内完成,但我想要 o(n) 复杂度。
  • 你应该声明操作应该在in place或者O(1)空间完成,否则,我们只是使用另一个内存块,做3个memmoves。

标签: arrays algorithm


【解决方案1】:

一个简单的伪代码实现:

for (n = 0; n < i; n++) {
    for (j = array.length-1; j > n; j--)
        swap(j, j-1)
}

反复将最后一个元素移动到前面,在它移动之前移动到前面的任何元素之前停止

【讨论】:

  • @prp - 可能在问题中提到过,嗯?
【解决方案2】:

为什么只有交换功能?

时间和空间上的O(n):

var rotateCount = 1;
var arr = new Array(1,2,3,4,5,6,7,8,9,10);

tmp = new Array(arr.length);
for (var i = 0; i<arr.length; i++)
    tmp[(i+rotateCount)%arr.length]=arr[i];
arr = tmp;

alert(arr);

【讨论】:

  • 此代码不起作用。尝试rotateCount = 8,它产生:9,10,2,3,4,5,6,7,8,1。
  • 还是坏了,试试 rotateCount = 3,它会产生:4,5,6,7,8,9,10,2,3,1。
  • 另外,您确实应该在发布代码之前对其进行测试。我花了 3 秒测试了这个,发现它不起作用。
【解决方案3】:

您可以使用 reverse() 助手在线性时间内完成此操作。

// rotate array of size=size, by n positions
void rotate(int array[], int size, int n)
{
  // reverse array[0...size-1]
  reverse(array, 0, size-1);

  // reverse A[0...n-1]
  reverse(array, 0, n-1);

  // reverse A[n...size-1]
  reverse(array, n, size-1);
}

// reverse elements in the array[pos_from ... pos_to]
void reverse(int array[], int pos_from, int pos_to)
{
   ...
}

使用交换实现reverse(int array[], int pos_from, int pos_to) 留给读者作为练习。提示:这可以在线性时间内完成。

【讨论】:

【解决方案4】:

假设我们有一个名为arr_reverse(arr,i,j) 的函数,它使用swap 函数在索引ij 之间反转数组arr 的元素。

例子:

arr = {1,2,3,4,5} 
i = 0
j = 2

然后函数将返回:

{3,2,1,4,5} 
 ^^^^^

实现这个函数很简单,就是O(N)

现在让我们使用这个函数来旋转数组。

arr     = {1,2,3,4,5} // input array
k       = 2 // amount of right rotation
result  = {4,5,1,2,3} // expected result 
l       = 5 // length of array.

Step 1: Call arr_reverse(arr,l-k,l-1) which is arr_reverse(arr,3,4)
we get {1,2,3,5,4} 
              ^^^

Step 2: Call arr_reverse(arr,0,l-k-1) which is arr_reverse(arr,0,2)
we get {3,2,1,5,4}
        ^^^^^     

Step 3: Call arr_reverse(arr,0,l-1) which is arr_reverse(arr,0,4)
we get {4,5,1,2,3} 
        ^^^^^^^^^

整个过程使用arr_reverse 3次,变成O(N)

【讨论】:

  • 我认为您在示例的第 2 步和第 3 步中打错字了,您在其中显示了结果。您已经将 4 和 5 与应有的水平颠倒过来了。
【解决方案5】:

最好用一个直接简单的函数,复杂度N:

int rotate(int* a,int DIM,int rn,int* b) {
    int i; //counter 
    for(i=0;i<DIM;i++){ // looping through the array
        b[(i+rn)%len]=a[i]; // copying the values in the b array=a shifted with rn(+ for right or - for left shifting
}

【讨论】:

    【解决方案6】:

    使用线性时间 O(2N+m) 和恒定空间 O(4)。 m = GCD(n, p)

    它比交换方法快 50%,因为交换需要将 O(N) 次写入临时文件。

    http://www.eis.mdx.ac.uk/staffpages/r_bornat/oldteaching/I2A/slides%209%20circshift.pdf

    for (m=0, count=0; count!=n; m++) {
        type t=A[m];
        for (i=m, j=m+p; j!=m; i=j, j = j+p<n ? j+p : j+p-n, count++)
            A[i]=A[j];
        A[i]=t; count++;
    }
    

    【讨论】:

      【解决方案7】:

      这是一个更好的解决方案,其性质与其他解决方案不同。它涉及的数组交换比其他数组交换少。蟒蛇:

      import fractions
      # rotates an array in-place i positions to the left, in linear time
      def rotate(arr,i):
          n = len(arr)
          reps = fractions.gcd(n,i)
          swaps = n / reps
          for start in xrange(reps):
              ix = start
              tmp = arr[ix]
              for s in xrange(swaps-1):
                  previx = ix
                  ix = (ix + i) % n
                  arr[previx] = arr[ix]
              arr[ix] = tmp
          return arr
      

      【讨论】:

        【解决方案8】:
        /*
         * To change this template, choose Tools | Templates
         * and open the template in the editor.
         */
        package rotateinlineartime;
        
        /**
         *
         * @author Sunshine
         */
        public class Rotator {
        
            void reverse(int a[], int n) {
                for (int i = 0; i <= n - 1; i++) {
                    int temp;
                    temp = a[i];
                    a[i] = a[n - 1];
                    a[n - 1] = temp;
                    n--;
                }
        
                printArray(a);
            }
        
            void printArray(int a[]) {
                for (int i = 0; i < a.length; i++) {
                    System.out.println(a[i]);
                }
            }
        }
        

        【讨论】:

          【解决方案9】:
          /* Q: How can we shift/rotate an array in place?
          A: "in place" means O(1) space complexity, so we need to do some trick
           */
          
          #include <iostream>
          #include <algorithm>
          using namespace std;
          
          void ArrayRotate(int a[], int n, int k)
          {
              if (n < 1 || k % n == 0 ) return;
          
              k %= n;
              if (k < 0) k += n;
          
              reverse(a, a+k);
              reverse(a+k, a+n);
              reverse(a, a+n);
          }
          
          void PrintArray(int a[], int n)
          {
              for ( int i = 0 ; i < n; ++i)
                  cout << a[i] << " ";
              cout << endl;
          }
          
          int main()
          {
              int a[] = { 1, 2 , 3, 4, 5 };
              int n = sizeof(a)/sizeof (a[0]);
          
              PrintArray(a, n);
              ArrayRotate(a, n, 2);
              PrintArray(a, n);
          
              return 0;
          }
          /* Output:
          1 2 3 4 5
          3 4 5 1 2
           */
          

          【讨论】:

            【解决方案10】:

            仅使用交换,以下是 C++ 实现

            template<class T>
            void rotate_array(std::vector<T> *array, int i) {
              int n = array->size();
              i = i % n;
              int gcd_n_i = gcd(i, n);
              for (int j = 0; j < gcd_n_i; j++) {
                T first_element = array->at(j);
                for (int k = j; (k + i) % n != j; k = (k + i) % n) {
                  std::swap(array->at(k), array->at((k + i) % n));
                }
              }
            }
            

            您可以在http://pointer-overloading.blogspot.in/2013/09/algorithms-rotating-one-dimensional.html阅读更多相关信息

            【讨论】:

              【解决方案11】:
              void reverse_array(int a[], int start, int end){
              
                  while(start < end){     
                          int temp =  a[start];
                          a[start] = a[end];
                          a[end] = temp;
                          start++;
                          end--;
                  }
              

              }

              void rotate_array(int a[], int pivot, int len){
                  int i;
                  /*Reverse the whole array */
                  reverse_array(a, 0, len);
              
                  /* Reverse from 0 to pivot and pivot to end */
                  reverse_array(a,0, pivot);
                  reverse_array(a,pivot+1,len);
              

              }

              【讨论】:

              • 这与 3 年前接受的答案相同。 ;)
              【解决方案12】:

              这是一个用 JavaScript 编写的 O(n) 的小 sn-p。 关键概念是,您始终必须使用被替换的项目。

              function swap(arr, a, v) {
                  var old = arr[a];
                  arr[a] = v;
                  return old;
              }
              
              function rotate(arr, n) {
                  var length = arr.length;
                  n = n % length;
                  if(!n) return arr;
              
                  for(var cnt = 0, 
                          index = 0,
                          value = arr[index],
                          startIndex = index; 
                      cnt < length; 
                      cnt++) {
              
                      // Calc next index
                      var nextIndex = mapIndex(index, n, length);
              
                      // Swap value with next
                      value = swap(arr, nextIndex, value)
              
                      if(nextIndex == startIndex) {
                          startIndex = index = mapIndex(index, 1, length);
                          value = arr[index];
                      } else {
                          index = nextIndex;
                      }
                  }
              
                  return arr;
              }
              
              function mapIndex(index, n, length) {
                  return (index - n + length) % length;
              }
              
              console.log(rotate([1,2,3,4,5,6,7,8,9], 5))
              console.log(rotate([1,2,3,4,5,6], 2))
              

              【讨论】:

                【解决方案13】:

                O(1) 在 python 中完成此操作的方法:

                class OffsetList(list):
                    __slots__ = 'offset'
                    def __init__(self, init=[], offset=-1):
                        super(OffsetList, self).__init__(init)
                        self.offset = offset
                    def __getitem__(self, key):
                        return super(OffsetList, self).__getitem__(key + self.offset)
                    def __setitem__(self, key, value):
                        return super(OffsetList, self).__setitem__(key + self.offset, value)
                    def __delitem__(self, key):
                        return super(OffsetList, self).__delitem__(key + self.offset)
                    def index(self, *args):
                        return super(OffsetList, self).index(*args) - self.offset
                

                这是基于this answer about using a 1-based list in python

                这确实有一个小故障,如果您尝试从列表末尾索引项目,它将从(新)开始返回项目,并且小于大小减去偏移量的负索引将不起作用。

                【讨论】:

                  【解决方案14】:
                  public static String rotateKTimes(String str,int k){
                      int n = str.length();
                  
                      //java substring has O(n) complexity
                      return str.substring(n-k) + str.substring(0,n-k);
                  }
                  

                  【讨论】:

                  • 他要求整数数组反转。不是string
                  【解决方案15】:

                  这是我使用 js 的答案希望这会有所帮助 其中 k 是您要执行的旋转次数

                   var arrayRoatate=function(array,k){
                      for(;k>0;k--) {
                       var nextElementValue=undefined;
                          for (var i = 0; i < array.length; i=i+2) {
                              var nextElement = i + 1;
                              if (nextElement >= array.length)
                                  nextElement = nextElement - array.length;
                              var tmp=array[i];
                              if(nextElementValue!==undefined)
                                  array[i]=nextElementValue
                              nextElementValue=array[nextElement];
                              array[nextElement]=tmp;
                  
                          }
                      }
                  return array;
                  

                  【讨论】:

                    【解决方案16】:
                    public int[] shift(int[] A, int K) {
                        int N = A.length;
                        if (N == 0)
                            return A;
                        int mid =  -K % N;
                        if (mid < 0)
                            mid += N;
                        if (mid == 0)
                            return A;
                    
                        reverseSubArray(A, 0 , mid - 1);
                    
                        reverseSubArray(A, mid , N - 1);
                    
                        reverseSubArray(A, 0 , N - 1);
                    
                        return A;
                    }
                    
                    private void reverseSubArray(int[] A, int start , int end){
                        int i = 0;
                        int tmp;
                        while (i < (end - start + 1) / 2) {
                            tmp = A[i + start];
                            A[i + start] = A[end - i];
                            A[end - i] = tmp;
                            i++;
                        }
                    
                    }
                    

                    Doug McIlroy’s Handwaving Description

                    【讨论】:

                      【解决方案17】:
                      Simple Solution in O(n) time and using O(1) space:
                      for e.g 1,2,3,4,5,6,7
                      rotating 2 times 
                      start with index 2, store a[0] as last
                      Iteration 1: 1,2,1,4,3,6,5 (1-->3-->5-->7)
                      Iteration 2: 1,7,1,2,3,4,5 (2-->4-->6)
                      replace 1 with 6 (last value).
                      
                      public int[] roatateArray(int[] a,int k)
                      {
                          int last = a[0];
                          int start = k;
                          for(int j=0;j<k;j++) {
                          for(int i=start;i<a.length;i+=k)
                          {
                              int tmp=a[i];
                              a[i]=last;
                              last=tmp;
                          }
                          start--;
                          if (start<=0) break;
                          }
                          a[0]=last;
                          return a;
                      }
                      

                      【讨论】:

                        【解决方案18】:

                        用于圆形向右旋转。

                        public static void main(String[] args) {
                            Scanner scan = new Scanner(System.in);
                            int n = scan.nextInt();
                            int k = scan.nextInt() % n;
                            int q = scan.nextInt();
                            int arr[] = new int[n];
                        
                            for (int i = 0; i < n; i++) 
                            {
                                int a = i + k;
                                int pos = (a < n) ? a : a - n;
                                arr[pos] = scan.nextInt();
                            }
                            for (int j = 0; j < q; j++) 
                            {
                                System.out.println(arr[scan.nextInt()]);
                            }
                        }
                        

                        【讨论】:

                        • 这里我们按照这样的顺序输入输入,使得输入看起来好像是以右旋方式获得的。
                        【解决方案19】:

                        我的 java 解决方案

                        static int[] rotLeft(int[] a, int d) {
                            for (int i = 0; i < d; i++) {
                                oneRotation(a);
                            }
                            return a;
                        }
                        
                        static void oneRotation(int[] a) {
                            int firstElement = a[0];
                            for (int i = 0; i < a.length - 1; i++) {
                                a[i] = a[i + 1];
                            }
                            a[a.length - 1] = firstElement;
                        }
                        

                        【讨论】:

                          【解决方案20】:

                          此算法(最多)进行len(array)-1 交换,并且适用于正(右)和负(左)旋转量。数组已就地修改。
                          与其他类似方法不同,它不需要计算 GCD。

                          (Python 3)

                          def rotate(array,amount):
                              if amount<0:
                                  amount+=len(array)
                              a=0
                              b=0
                              for _ in range(len(array)-1):
                                  b=(b+amount) % len(array)
                                  if b==a:
                                      a+=1
                                      b=a
                                  if b!=a:
                                      array[a],array[b] = array[b],array[a] #swap array[a],array[b]
                              return array
                          


                          当一个循环不够用时(如果在到达每个索引之前回到起点),从下一个索引开始一个新的循环。

                          注意:旋转项目 a, b, c, d, ... 可以使用
                          swap a,b swap a,c swap a,d ...

                          【讨论】:

                            【解决方案21】:

                            简答(python 代码)

                            def reverse(arr, i, j):
                                for idx in xrange((j - i + 1) / 2):
                                    arr[i+idx], arr[j-idx] = arr[j-idx], arr[i+idx]
                            
                            def solution(A, K):
                                l = len(A)
                                if l == 0:
                                    return []
                                K = K%l
                                reverse(A, l - K, l -1)
                                reverse(A, 0, l - K -1)
                                reverse(A, 0, l - 1)
                                return A
                            

                            长答案(代码说明)

                            让我先谈谈K &lt; N的基本情况,这种情况下的想法是将数组分成两部分ABA是第一个N-K元素数组和@987654335 @ 最后一个 K 元素。算法分别反转AB,最后反转整个数组(两部分分别反转)。要使用K &gt; N 管理这种情况,请认为每次反转数组N 时都会再次获得原始数组,因此我们可以使用模块运算符找到拆分数组的位置(仅反转真正有用的时间避免无用的转移)。

                            图形分步示例有助于更好地理解概念。请注意

                            • 粗线表示数组的分割点(本例中K=3);
                            • 两个红色数组分别表示输入和预期输出。

                            起点:

                            看看我们想要在最终输出前面的最后 3 个字母反转,现在让它反转(算法的第一个反转):

                            现在反转前 N-K 个元素(算法的第二个反转):

                            我们已经有了解决方案,但是在相反的方向上,我们可以通过反转整个数组来解决它(算法的第三个也是最后一个反转):

                            这里是最终输出,原始数组循环旋转K = 3

                            让我们再给出一个使用python代码的逐步示例,从:

                            A = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
                            K = 22
                            N = len(A)
                            

                            我们找到分裂索引:

                            K = K%N
                            #2
                            

                            因为,在这种情况下,前 20 个移位将没有用,现在我们将原始数组的最后一个 K (2) 个元素反转:

                            reverse(A, N-K, N-1)
                            # [1, 2, 3, 4, 5, 6, 7, 8, 10, 9]
                            

                            如您所见,9 和 10 已经移位,现在我们反转前 N-K 个元素:

                            reverse(A, 0, N-K-1)
                            # [8, 7, 6, 5, 4, 3, 2, 1, 10, 9]
                            

                            最后,我们反转整个数组:

                            reverse(A, 0, N-1)
                            # [9, 10, 1, 2, 3, 4, 5, 6, 7, 8]
                            

                            请注意,反转数组的时间复杂度为 O(N)。

                            【讨论】:

                              【解决方案22】:
                              void rotate(int array [], int n, int k)
                              {
                                  int temp1,temp2;     //two variable to hold values
                                  int visited,count,index;   //'visited' to check if cycle 
                                                             // collides with index
                                                             //'count' to check number of loops
                                  visited = n-1;
                                  temp1 = array[n-1];
                                  index = n-1;
                                  count = 0;
                              
                                  while(count<n)
                                  {   
                                      temp2 = temp1;
                                      temp1 = array[(n+index-k)%n];
                                      array[(n+index-k)%n] = temp2;
                                      index = (n+index-k)%n;
                                      if (index == visited)
                                      {   
                                          cout<<"\nindex == visited at index = "<<index;
                                          getch();
                                          index--;
                                          visited=index;
                                          temp1=array[index];
                                      }
                                      count++;
                                      cout<<"\ncount is : "<<count;
                                      cout<<"\narray is : ";
                                      p_all_arr(array,0,n); //display arr 
                                      getch();
                                  }
                              
                              }
                              

                              【讨论】:

                                猜你喜欢
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 1970-01-01
                                • 2016-02-05
                                • 1970-01-01
                                • 2018-08-19
                                • 2013-05-29
                                • 2012-08-09
                                相关资源
                                最近更新 更多