数组的圆形左移位在java中的n个位置

时间:2012-08-09 22:42:08

标签: java arrays bit-shift

我正在尝试使用仅一个1D阵列的n个位置进行数组的圆形左移。我可以在两个数组中完成,但我还没弄明白如何使用它。请提出您的建议

14 个答案:

答案 0 :(得分:19)

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

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

这种算法背后的一般思路是这样的:我们不希望在必要时移动元素,所以理想情况下我们希望简单地将每个元素放在其中的正确(移位)位置上第一次尝试。假设我们从位置i处的元素开始。我们想要做的是将位置i处的元素移动到位置f(i),但是我们将覆盖该位置处的元素,因此我们需要先将元素保存在位置{{ 1}}然后执行班次​​。一旦我们移动了第一个元素,我们需要选择另一个元素来移位。由于我们想要节省空间,明显的候选者是我们刚刚保存的元素(位于f(i)的元素)。像以前一样,我们将元素保存在位置f(i),然后将我们保存的元素复制到该位置。我们不断重复这个过程(通过位置f(f(i))),直到我们到达一个我们已经转移过的元素(我们已经保证这样做,因为有很多位置)。如果我们通过所有元素,那么我们就完成了,如果没有,那么我们选择另一个元素(还没有被移位),比如位置i, f(i), f(f(i)), f(f(f(i))), ...,并重复该过程(通过{ {1}})。就是这样。但在我们实现这样的算法之前,或者甚至在我们确定这是否确实是一个好算法之前,我们需要回答几个问题:

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

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

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

  4. 让我们来表示j, f(j), f(f(j)), ...。对于每个整数:i我们定义以下集合:

    j

    很容易看到集合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)除以{{ 1}}会给我们留下不同的余数(d)。因此,没有两个集合包含共同元素。有了这个,我们已经确定集合i形成S(j)

    的分区

    现在,对于每个d,我们会将集j定义为S(0),...,S(d - 1)。根据{{​​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} ,然后乘以x,我们有:

    T(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)存在yS(i)(模块化反转),因此我们可以写(乘以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)中的所有元素,位置d处的元素,其中元素位于{0,...N - 1}位置,依此类推......直到我们返回位置0, d, 2d, ..., (N/d - 1)d中的元素(我们确信会发生这种情况)。这是一个伪代码示例:

    n mod N

    这涵盖了整套0。为了覆盖其余的集合,即2n mod N,我们将按照与第一集相同的方式迭代每个集合:

    n mod N

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

    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;
    

答案 1 :(得分:2)

这是非常简单的算法,在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]

答案 2 :(得分:1)

您可以通过迭代和复制来移动数据,这将是O(n)。另一种方法是创建一个List实现,它包装您的数组并将其公开为循环移位。这具有以下优点:当执行get或迭代时,实际的移位是懒惰的。

答案 3 :(得分:1)

我会一次将它移动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 :(得分:1)

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

答案 5 :(得分:1)

我确实相信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 :(得分:0)

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];

答案 7 :(得分:0)

答案 8 :(得分:0)

也许是一个旧帖子..但是在这里你是我的解决方案(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;
}

答案 9 :(得分:0)

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();
  }
}

答案 10 :(得分:0)

下面,我实现了一个示例解决方案,将数组左移或右移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);
    }
} 

我已注释掉右移。

答案 11 :(得分:0)

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

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 个数组。我的错。我会留下我的答案,以防万一它可以帮助某人。

答案 12 :(得分:-1)

这个怎么样?

    // 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;
    }
}

答案 13 :(得分:-1)

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]);
        
    }
}