【问题标题】:circular left shift of an array by n positions in javajava中数组的循环左移n个位置
【发布时间】:2012-08-07 05:56:18
【问题描述】:

我正在尝试仅使用单个一维数组将数组循环左移 n 个位置。我可以在两个数组中做到这一点,但我还没有弄清楚如何使用一个来做到这一点。请提出您的建议

【问题讨论】:

标签: java arrays bit-shift


【解决方案1】:

您可以通过迭代和复制来移动数据,这将是 O(n)。另一种方法是创建一个List 实现,该实现包装您的数组并将其公开为循环移位。这样做的好处是,当 get 或执行迭代时,实际的移位是延迟完成的。

【讨论】:

    【解决方案2】:
    for (int i = 0; i < n; i++)
        array[array.length - n + i] = array[i];
    for (int i = 0; i < array.length - n; i++)
        array[i] = array[i + n];
    

    【讨论】:

    • 但这并没有改变一切。
    • 是的,我没有看到“圆形”部分。现已修复。
    【解决方案3】:

    我会一次将其移动 1 个元素,使用单个临时变量来保存元素,同时将元素移动 1 个位置。然后我会重复这个n 次以实现n 转变。

    public static void main( String[] args ) {
        int[] array = {1,2,3,4,5,6,7,8};
        leftShift( array, 3);
        System.out.println( Arrays.toString( array));
    }
    
    public static void leftShift(int[] array, int n) {
        for (int shift = 0; shift < n; shift++) {
            int first = array[0];
            System.arraycopy( array, 1, array, 0, array.length - 1 );
            array[array.length - 1] = first;
        }
    }
    

    输出:

    [4, 5, 6, 7, 8, 1, 2, 3]
    

    不会太低效,因为System.arraycopy() 已经过高度优化。

    【讨论】:

      【解决方案4】:

      另一种选择是包装您自己的结构,其中包括数组和虚拟零的索引。

      【讨论】:

        【解决方案5】:

        我相信System.arraycopy 实际上只会从一个数组中取出所有数据,然后将其放入另一个相同长度的数组中。

        无论如何,思考这个问题是一项非常有趣的任务。我现在唯一能想到的解决方案就是一个一个地把它拉屎。 如果不使用另一个数组,它看起来像这样:

        for(int i = 0; i < shift;i++)
                {
                    tmp = array[0];
                    for(int j = 0;j<array.length-1;j++)
                        array[j]=array[j+1];
                    array[array.length-1]=tmp;
                }
        

        对于超过 30 个项目的数组,使用它会更有效:

        for (int i = 0; i < shift; i++) {
                    tmp = array[0];
                    System.arraycopy( array, 1, array, 0, array.length - 1 );
                    array[array.length - 1] = tmp;
                }
        

        但是对于接近数组大小的大数组和大移位以及短数组和小移位,这种方法赢得了比赛:

            int[] array2 = new int[shift];
            for (int i = 0; i < shift; i++)
            {
                array2[i] = array[i];
            }
            System.arraycopy(array, shift, array, 0, array.length - shift);
            for (int i = array.length - shift; i < array.length; i++)
            {
                array[i] = array2[shift + i - array.length];
            }
        

        我已经用一些数组大小和移位测试了以下是

        的结果
            int[] array = new int[100000];
            int shift = 99999;
        

        以纳秒为单位: 第一种方法:5663109208 方法二:4047735536 第三种方法:6085690 所以你真的应该使用第三种方法。希望有帮助

        【讨论】:

          【解决方案6】:

          实际上有一个聪明的算法。我们将使用A 表示数组,N 表示数组大小,n 表示要移动的位置数。移位后,您希望 i-th 元素移动到 ((i + n) mod N)-th 位置,因此我们可以通过以下映射定义新位置:

          f(j) := (j + n) mod N  (j = 0,...,N - 1)
          

          这个算法背后的总体思路是这样的:我们不希望移动元素超出必要的范围,所以理想情况下,我们希望在第一次尝试时简单地将每个元素放置在正确的(移动的)位置。假设我们从位置i 的元素开始。我们要做的是将位置i的元素移动到位置f(i),但是我们会覆盖该位置的元素,所以我们需要先保存位置f(i)的元素,然后执行移位.一旦我们移动了第一个元素,我们需要选择另一个元素来移动。由于我们想节省空间,显而易见的候选者是我们刚刚保存的元素(位于 f(i) 位置的元素)。像以前一样,我们将元素保存在位置f(f(i)),然后将保存的元素复制到该位置。我们不断重复这个过程(通过位置i, f(i), f(f(i)), f(f(f(i))), ...),直到我们到达一个我们已经移动的元素(我们保证会这样做,因为位置有限)。如果我们通过了所有元素,那么我们就完成了,如果没有,那么我们选择另一个元素(还没有移动),比如说在位置j,然后重复这个过程(通过j, f(j), f(f(j)), f(f(f(j))), ...)。而已。但在我们实现这样的算法之前,甚至在我们决定这是否确实是一个好的算法之前,我们需要回答几个问题:

          1. 假设我们遍历位置i, f(i), f(f(i)), ...。我们如何判断我们到达了一个已经转移的位置?我们需要保存我们通过的每个位置吗?如果我们这样做,那么这意味着我们需要保存一个大小为 N 的数组(以覆盖所有位置),并且我们还需要在每次移动元素时执行查找。这将使算法非常低效。幸运的是,这不是必需的,因为序列i, f(i), f(f(i)), ... 必须在位置i 处环绕自身,所以我们只需要等到到达那个位置。我们可以如下证明这个断言:假设我们遇到的第一个重复元素不是i。那么我们必须有 2 个不同的元素,它们在移动时会到达相同的位置 - 矛盾。

          2. 假设我们完成了i, f(i), f(f(i)), ...,但仍有未移动的元素(我们可以通过计算移动了多少元素来判断)。我们现在如何找到包含这样一个元素的位置j?而且,一旦我们完成了第二次迭代(通过j, f(j), f(f(j)), ...),我们如何找到具有未移位元素的第三个位置k?等等..这也可能表明我们需要保存一个数组来说明使用过的\未使用过的元素,并在每次需要查找未使用的元素时执行查找。但是,我们可以再次放松,因为正如我们很快将展示的那样,所有起始位置(我们用ijk 表示)都是相邻的。这意味着,如果我们从位置i 开始,接下来我们将选择i + 1,然后选择i + 2,依此类推……

          3. 序列i, f(i), f(f(i)), ...j, f(j), f(f(j)), ...(其中ij 不同)可能包含共同元素吗?如果他们这样做,则意味着该算法是无用的,因为它可能会移动相同的元素两次 - 导致它最终处于错误的位置。那么答案(当然)是它们不能包含共同的元素。我们将说明原因。

          让我们表示d := gcd(N, n)。对于每一个整数:i = 0,...,d - 1,我们定义以下集合:

          S(i) := { kd + i | k = 0,...,N/d - 1}
          

          很容易看出集合S(0),...,S(d - 1) 一起覆盖了集合{0,...,N - 1}。我们还观察到,当将集合S(i) 中的一个元素除以d 时,我们会得到余数i,并且将来自不同集合S(j) 的元素除以d 会留下不同的余数(j)。因此,没有两个集合包含一个共同的元素。有了这个我们已经确定集合S(0),...,S(d - 1)形成{0,...,N - 1}的分区

          现在,对于每个i = 0,...,d - 1,我们将集合T(i) 定义为i, f(i), f(f(i)), ...。根据f的定义我们可以写成T(i)如下:

          T(i) = {(kn + i) mod N | k is an integer}
          

          我们观察到如果xT(i)中的一个元素,那么我们可以写一些k

          x = (kn + i) mod N = (k(n/d)d + i) mod N
          

          让我们表示z := k(n/d) mod N/d,然后乘以d,我们有:

          kn mod N = zd
          

          因此:

          x = (kn + i) mod N = zd + i
          

          因此,x 也在S(i) 中。同样,如果我们从S(i) 中取出一些y,我们会观察到对于一些k

          y = kd + i
          

          由于gcd(n/d, N/d) = 1 存在q 使得q(n/d) mod N/d = 1(模逆),因此我们可以写(乘以kd):

          kd = kqn mod N
          

          因此:

          y = kd + i = ((kq)n + i) mod N
          

          因此,y 也在T(i) 中。我们得出结论T(i) = S(i)。从这个事实我们可以很容易地展示我们之前的断言。首先,由于集合形成{0,...,N - 1} 的分区,因此满足第三个断言(没有两个序列包含公共元素)。其次,通过集合S(i) 的定义,我们可以在{0,...N - 1} 中取任意一组d 相邻元素,并且每个元素都将放置在不同的集合中。这满足第二个断言。

          这意味着我们可以旋转位置0, d, 2d, ..., (N/d - 1)d的所有元素,只需将n mod N位置的元素替换为0位置的元素,@987654403位置的元素@ 与位置n mod N 的元素,依此类推……直到我们返回位置0 的元素(我们确信会发生这种情况)。这是一个伪代码示例:

          temp <- A[0]
          j <- N - (n mod N)
          while j != 0 do
              A[(j + n) mod N] <- A[j];
              j <- (j - n) mod N
          A[n mod N] <- temp;
          

          这涵盖了整个集合S(0)。为了覆盖其余的集合,即S(1), … ,S(d-1),我们将简单地迭代每个集合,就像我们对第一个集合所做的那样:

          for i <- 0 to d - 1
              temp <- A[i]
              j <- N - ((n - i) mod N)
              while j != i do
                  A[(j + n) mod N] <- A[j];
                  j <- (j - n) mod N
              A[(i + n) mod N] <- temp;
          

          请注意,虽然我们有两个嵌套循环,但每个元素只移动一次,我们使用O(1) 空格。 Java 中的实现示例:

          public static int gcd(int a, int b) {
              while(b != 0) {
                  int c = a;
                  a = b;
                  b = c % a;
              }
              return a;
          }
          
          public static void shift_array(int[] A, int n) {
              int N = A.length;
              n %= N;
              if(n < 0)
                  n = N + n;
              int d = gcd(N, n);
              for(int i = 0; i < d; i++) {
                  int temp = A[i];
                  for(int j = i - n + N; j != i; j = (j - n + N) % N)
                      A[(j + n) % N] = A[j];
                  A[i + n] = temp;
              }
          }
          

          【讨论】:

            【解决方案7】:
            【解决方案8】:

            这个怎么样?

                // Left shift the array in O(n) with O(1) space.
            
            public static void leftShift(int[] array, int n) {
                int temp;
                int len = array.length;
                for (int i = 0; i < n; i++) {
                    temp = array[len - n + i];
                    array[len - n + i] = array[i];
                    array[i] = array[n + i];
                    array[n + i] = temp;
                }
            }
            

            【讨论】:

              【解决方案9】:

              这是一个非常简单的算法,在 O(n) 中有 O(1) 个空间
              算法

              • 将数组从 0 反转到 n (numberOfPositions) 个位置
              • 将数组从 n+1 反转为数组长度 - 1 个位置
              • 将整个数组从 0 反转为长度 - 1 个位置


               public class ArrayRotator {
              
               private final int[] target;
               private final int length;
              
               public ArrayRotator(int[] seed) {
                  this.target = seed;
                  this.length = seed.length;
               }
              
               public void rotateInline(int numberOfPositions) {
                  reverse(0, numberOfPositions);
                  reverse(numberOfPositions + 1, length-1);       
                  reverse(0, length-1);
               }
              
               private void reverse(int start, int end) {
                  for (int i = start; i <= (start + end)/2; i++) {
                      swap(i, start + end - i);
                  }
               }
              
               private void swap(int first, int second) {
                  int temp = this.target[second];
                  this.target[second] = this.target[first];
                  this.target[first] = temp;
               }
              }
              


              例如,假设数组是 [1,2,3,4]n2
              第一步之后,你最终会得到[2,1,3,4]
              第二步之后,您最终会得到[2,1,4,3]
              第三步之后,你会得到[3,4,1,2]

              【讨论】:

                【解决方案10】:

                也许是一个旧帖子.. 但这是我的解决方案(A 显然是数组,K 是位置数)。

                public int[] solution(int[] A, int K){
                    int[] result = new int[A.length];
                
                    for (int i = 0; i<A.length; i++){
                        result[(i+K)%A.length] = A[i];
                    }
                    return result;
                }
                

                【讨论】:

                  【解决方案11】:

                  Java 8 版本:

                  public class Sample {
                     public static void main(String[] args) {
                       int[] answer = solution(new int[] {1,2,3,4}, 2);
                       Arrays.stream(answer).forEach(System.out::print);
                     }
                  
                     public static int[] solution(int[] A, int K) {
                       List<Integer> numbers = 
                       IntStream.of(A).boxed().collect(Collectors.toList());
                       Collections.rotate(numbers, K);
                       return numbers.stream().mapToInt(n -> n).toArray();
                    }
                  }
                  

                  【讨论】:

                    【解决方案12】:

                    下面我实现了一个将数组左移或右移n个元素的示例解决方案。

                    class RotateArrayByN {
                    
                        public void leftRotate(int arr[], int d, int n)
                        {
                            for (int i = 0; i < d; i++)
                                leftRotatebyOne(arr, n);
                        }
                    
                        public void rightRotate(int[] arr,int d, int n){
                            for(int i=0;i<d;i++)
                                rightRotatebyOne(arr,n);
                        }
                    
                        public void leftRotatebyOne(int arr[], int n)
                        {
                            int i, temp;
                            temp = arr[0];
                            for (i = 0; i < n - 1; i++)
                                arr[i] = arr[i + 1];
                            arr[i] = temp;
                        }
                    
                        public void rightRotatebyOne(int[] arr,int n){
                    
                            int temp=arr[n-1];
                            for (int i=n-1;i>0;i--) {
                                arr[i] = arr[i - 1];
                            }
                            arr[0]=temp;
                    
                        }
                    
                      public void printArray(int arr[], int n)
                        {
                            for (int i = 0; i < n; i++)
                                System.out.print(arr[i] + " ");
                    
                            System.out.println();
                        }
                    
                    
                        public static void main(String[] args)
                        {
                            RotateArrayByN rotate = new RotateArrayByN();
                            int arr[] = { 1, 2, 3, 4, 5, 6, 7 };
                            System.out.println("Left Rotate");
                            rotate.leftRotate(arr, 2, 7);
                            rotate.printArray(arr, 7);
                    
                           //System.out.println("Right Rotate");
                            //rotate.rightRotate(arr,2,7);
                           // rotate.printArray(arr,7);
                        }
                    } 
                    

                    我已经注释掉了右移。

                    【讨论】:

                      【解决方案13】:

                      我知道这是一篇旧帖子,但我没有在任何地方看到此帖子,所以:

                      d 是我们想要向左移动多少个位置。

                          int[] array = {1,2,3,4,5,6,7,8};
                          int length = array.length;
                          int d = 3;
                          int[] ans = new int[length];        
                      
                          for (int i = 0; i < length; i++){
                              ans[i] = array[(i + d)%length];
                          }
                          System.out.println(Arrays.toString(ans));
                      

                      它将输出:[4, 5, 6, 7, 8, 1, 2, 3]

                      您可以在此处查看正在运行的代码:http://tpcg.io/8cS6GIKI

                      已编辑:没关系...我无法阅读。我刚刚看到 OP 只要求 1 个数组。我的错。我会留下我的答案,以防万一它可以帮助别人。

                      【讨论】:

                        【解决方案14】:
                        import java.util.Scanner;
                        
                        public class ArrayMoveByGivenSize {
                        
                            public static void main(String[] args) {
                                // TODO Auto-generated method stub
                        
                                
                                int[] A = new int[] {100,200,300,400,500,600};
                                //movement = 2
                                //output {500,600,100,200,300,400}
                        
                                System.out.println("Please enter the movement size");
                                int s;
                                Scanner sc = new Scanner(System.in);
                                s= sc.nextInt();
                                System.out.println(s);
                                int[] X = new int[A.length];
                                for (int i=0;i<A.length;i++)
                                {
                                    if((i+s)<A.length)
                                    {
                                        X[i+s]=A[i];
                                    }
                                    else
                                    {
                                        X[(i+s) - A.length]=A[i] ;  
                                    }
                                    
                                }
                                for(int i =0;i<X.length ; i++)
                                System.out.println(X[i]);
                                
                            }
                        }
                        

                        【讨论】:

                          【解决方案15】:

                          我知道这是一篇旧文章,但是这里有一个 O(n) 中的最佳解决方案:每个元素只移动一次,不需要额外的空间。它与@SomeStrangeUser 提出的解决方案非常相似,但不需要 gcd 计算。

                          public static void shiftArray(int[] A, int k) {
                              if (A.length == 0) {
                                  return;
                              }
                              k = k % A.length;
                              k = (k + A.length) % A.length; // ensure k is positive
                              if (k == 0) {
                                  return;
                              }
                              int i = 0, i0 = 0;
                              int x = A[0];
                              for (int u = 0; u < A.length; u++) { // count number of shifted elements
                                  int j = (i - k + A.length) % A.length; // ensure modulo is positive
                                  if (j == i0) { // end of a (sub-)cycle, advance to next one
                                      A[i] = x;
                                      x = A[i = ++i0];
                                  } else {
                                      A[i] = A[j];
                                      i = j;
                                  }
                              }
                          }
                          

                          【讨论】:

                            猜你喜欢
                            • 2017-11-23
                            • 2022-06-11
                            • 2021-05-24
                            • 1970-01-01
                            • 2014-05-08
                            • 2022-01-23
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多