如何旋转数组?

时间:2015-07-02 02:47:42

标签: java arrays sorting optimization time-complexity

我有以下问题需要测试:

  

向右旋转n个元素的数组,步长为k步。

     

例如,当n = 7且k = 3时,数组[1,2,3,4,5,6,7]被旋转到   [5,6,7,1,2,3,4]。您知道解决此问题的方式有多少种?

我在中间阵列中的解决方案:

如果Space为O(n)且时间为O(n),我可以创建一个新数组,然后将元素复制到新数组中。然后使用System.arraycopy()更改原始数组。

public void rotate(int[] nums, int k) {
    if(k > nums.length) 
        k=k%nums.length;

    int[] result = new int[nums.length];

    for(int i=0; i < k; i++){
        result[i] = nums[nums.length-k+i];
    }

    int j=0;
    for(int i=k; i<nums.length; i++){
        result[i] = nums[j];
        j++;
    }

    System.arraycopy( result, 0, nums, 0, nums.length );
}

但是,在O(1)空间中使用气泡旋转(如冒泡排序)是否有更好的方法呢?

23 个答案:

答案 0 :(得分:6)

您不需要for - 循环。

public int[] rotate(int[] nums, int k) {
     if(k > nums.length) 
          k=k%nums.length;

     int[] result = new int[nums.length];
     System.arraycopy( nums, k+1, result, 0, k );
     System.arraycopy( nums, 0, result, k+1, nums.length-1 );

     //Case 1: The rotated array will be assigned to the given array "nums"
     nums = result;

     return result; //Case 2: method returns the rotated array
}

可以在http://docs.oracle.com/javase/7/docs/api/java/lang/System.html

找到arraycopy的规范

不是测试。方法的整体复杂性旋转O(1)。

如果您的问题是关于所有可能的排列,请查看此处 Java Code for permutations of a list of numbers

另一个建议是查看提供许多复杂数据结构和常见排序算法的Java Collection API,它们都以非常有效的方式实现。

由于评论而编辑。

该方法返回一个旋转的数组。您可以在外部方法中使用此方法,如下所示:(只是伪代码)

void rotator(int[] nums) {        
       int rotated[] = nums;
       //Can be invoked iteraritve or within a loop like this
       rotated = rotate(rotated, 3);          
}

答案 1 :(得分:4)

方法1 - 反转算法(好的):

  

算法:

     

rotate(arr [],d,n)

     
    

反转(arr [],l,n);

         

反向(arr [],1,n-d);

         

反转(arr [],n - d + 1,n);

  

设AB是输入数组的两部分,其中A = arr [0..n-d-1],B = arr [n-d..n-1]。该算法的想法是:

  
    

全部反转得到(AB)r = BrAr。

         

反向A获得BrA。 / * Ar与A * /

相反          

反向B获得BA。 / * Br与B * /

相反   

对于arr [] = [1,2,3,4,5,6,7],d = 2且n = 7

A = [1,2,3,4,5]和B = [6,7]

  
    

反转所有,我们得到BrAr = [7,6,5,4,3,2,1]

         

反向A,我们得到ArB = [7,6,1,2,3,4,5]     反向B,我们得到ArBr = [6,7,5,4,3,1,2]

  

以下是代码段:

void righttRotate(int arr[], int d, int n)
{
  reverseArray(arr, 0, n-1);
  reverseArray(arr, 0, n-d-1);
  reverseArray(arr, n-d, n-1);
}

void reverseArray(int arr[], int start, int end)
{
  int i;
  int temp;
  while(start < end)
  {
    temp = arr[start];
    arr[start] = arr[end];
    arr[end] = temp;
    start++;
    end--;
   }
}

方法2 - Juggling算法

将数组分成不同的集合,其中集合的数量等于n和d的GCD,并移动集合中的元素。

如果GCD为1,那么元素将仅在一组内移动,我们只是从temp = arr [0]开始并继续将arr [I + d]移动到arr [I]并最终将temp存储在正确的位置

以下是n = 12且d = 3的示例.GCD为3且

让arr []为{1,2,3,4,5,6,7,8,9,10,11,12}

  1. 元素首先在第一组中移动       arr []在此步骤之后 - &gt; {4 2 3 7 5 6 10 8 9 1 11 12}

  2. 然后在第二组。       arr []在此步骤之后 - &gt; {4 5 3 7 8 6 10 11 9 1 2 12}

  3. 最后在第三集。       arr []在此步骤之后 - &gt; {4 5 6 7 8 9 10 11 12 1 2 3}

  4. 以下是代码:

    void leftRotate(int arr[], int d, int n)
    {
      int i, j, k, temp;
      int gcd = gcd(d, n);
      for (i = 0; i < gcd; i++)
      {
        /* move i-th values of blocks */
        temp = arr[i];
        j = i;
        while(1)
        {
          k = j + d;
          if (k >= n)
            k = k - n;
          if (k == i)
            break;
          arr[j] = arr[k];
          j = k;
        }
        arr[j] = temp;
      }
    }
    
    int gcd(int a,int b)
    {
       if(b==0)
         return a;
       else
         return gcd(b, a%b);
    }
    
      

    时间复杂度:O(n)

         

    辅助空间:O(1)

    方法3 - 逐个旋转

      

    righttRotate(arr [],d,n)

         

    启动

         
        

    对于i = 0到i&lt; d

             

    将arr []的所有元素右移一个

      
         

    要旋转1,将arr [n-1]存储在临时变量temp中,将arr [1]移动到arr [2],将arr [2]移动到arr [3] ...最后将temp移动到arr [0]

    让我们采用相同的例子arr [] = [1,2,3,4,5,6,7],d = 2,将arr []旋转一次2次。我们在第一次旋转后获得[7,1,2,3,4,5,6],在第二次旋转后获得[6,7,1,2,3,4,5]。

    她是Code Snippet:

    void leftRotate(int arr[], int d, int n)
    {
      int i;
      for (i = 0; i < d; i++)
        leftRotatebyOne(arr, n);
    }
    
    void leftRotatebyOne(int arr[], int n)
    {
      int i, temp;
      temp = arr[n-n];
      for (i = 0; i < n-1; i++)
         arr[i] = arr[i+1];
      arr[n - 1] = temp;
    }
    
      

    时间复杂度:O(n * d)

         

    辅助空间:O(1)

答案 2 :(得分:2)

以下代码将完成您的工作。这是为了正确旋转。

public void rightrotate(int[] nums, int k) {
    k %= nums.length;
    reverse(nums, 0, nums.length - 1);
    reverse(nums, 0, k - 1);
    reverse(nums, k, nums.length - 1);
}

public void reverse(int[] nums, int start, int end) {
    while (start < end) {
        int temp = nums[start];
        nums[start] = nums[end];
        nums[end] = temp;
        start++;
        end--;
    }
}

如果您想左旋转,请使用以下

 public void leftrotate(int[] nums, int k) {
    k %= nums.length;
    reverse(nums, 0, k - 1);
    reverse(nums, k, nums.length - 1);
    reverse(nums, 0, nums.length - 1);
}

答案 3 :(得分:2)

空格为O(1),时间为O(n)

static void rotate(int[] array, int k) {
    int size = array.length;
    if (size <= 1) return;
    k = k % size;
    if (k == 0) return;
    for (int i = 0, start = 0, from = 0, to = -1, move = array[0]; i < size; ++i, from = to) {
        to = (from + k) % size;
        int temp = array[to];
        array[to] = move;
        move = to == start ? array[to = ++start] : temp;
    }
}

答案 4 :(得分:1)

Python代码:

def reverse(arr,start , end):   
    while(start <= end):
        arr[start] , arr[end] = arr[end] , arr[start]
        start = start+1
        end = end-1

arr = [1,2,3,4,5,6,7]
n = 7
k = 2
reverse(arr,0,n-1)
# [7,6,5,4,3,2,1]
reverse(arr,0,n-1-k)
# [3,4,5,6,7,2,1]
reverse(arr,n-k,n-1)
# [3,4,5,6,7,1,2]

print arr
# [3, 4, 5, 6, 7, 8, 9, 1, 2]

答案 5 :(得分:1)

在Ruby中非常简单,请看一看。

def array_rotate(arr)
    i, j = arr.length - 1, 0
    arr[j],arr[i], i, j = arr[i], arr[j], i - 1, j + 1 while(j<arr.length/2)
    puts "#{arr}"
end

输入:[1、2、3、4、5、6、7、8、9、10、11、12、13、14、15、16、17、18、19 ,20]

输出:[20、19、18、17、16、15、14、13、11、10、9、8、7、6、5、4、3、2 ,1]

答案 6 :(得分:1)

以上解决方案讨论通过反转它们或任何其他替代方案来移动阵列元素。

我有独特的解决方案。如何在n次旋转后确定元素的起始位置。一旦我们知道了,那么只需插入该索引中的元素并使用模数运算递增计数器。使用这种方法,我们可以避免使用额外的数组操作等。

这是我的代码:

#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

void rotateLeft(int n,int r) {
    vector<long int> vec(n);
    int j = n;
    // get the position of starting index after r left rotations.
    while(r!=0) {
        --j;
        if(j==0)
            j = n;
        --r;
    }
    for(long int i=0;i<n;++i) {
        // simply read the input from there and increment j using modulus operator.
        cin>>vec[j];
        j = (j+1)%n;
    }
    // print the array
    for(long int i=0;i<n;++i) 
        cout<<vec[i]<<" ";
}
int rotateRight (int n,int r) {
    // get the position of starting index after r left rotations.
    int j = r % n;

    vector<long int> vec(n);
    for(int i=0;i<n;i++) {
        cin>>vec[j];
        j=(j+1)%n;
    }
    for(int i=0;i<n;i++)
        cout<<vec[i]<<" ";

}
int main() {

    long int n,r;   // n stands from number of elements in array and r stands for rotations.
    cin>>n>>r;
    // Time Complexity: O(n+r) Space Complexity: O(1)
    rotateLeft(n,r);
    // Time Complexity: O(n) Space Complexity: O(1)
    rotateRight(n,r);
    return 0;

}

答案 7 :(得分:1)

ArrayUtil类用于在基本数组中提供以下实用程序

  1. 交换数组元素
  2. startIndex和endIndex之间的反向数组
  3. leftRotate array by shift
  4. 按班次旋转数组的算法 -

    1. 如果我们必须按移位值反转数组,那么请使用数组长度的mod(%),以便移位小于数组长度。
    2. 索引0和shift-1之间的反向数组
    3. 索引移位和长度-1之间的反向数组。
    4. 在索引0和长度-1之间反转完整数组。
    5. 空间复杂度:就地算法,无需额外空间,因此O(1)。

      时间复杂度:大小k的数组反转取O(k / 2),即交换k / 2对元素。

      对于k大小的数组,数组反转时间-O(k)。

      轮换时间 -

      • O(1).......... for step 1
      • O(班次)......第2步
      • O(n - shift)... for the step 3
      • O(n)........... for the step 4

      阵列旋转的总时间:O(1)+ O(移位)+ O(n移位)+ O(n)= O(n)

      public class Solution {
      
          public static void main(String[] args) {
              int k = 3;
              int a[] = {1,2,3,4,5,6,7};
      
              ArrayUtil.leftRotate(a, k);
      
              for (int i : a)
                  System.out.println(i);
          }
      }
      
      class ArrayUtil {
      
          public static final boolean checkIndexOutOfRange(int[] array, int index) {
              if (index < 0 || index > array.length)
                  return true;
              return false;
          }
      
          public static final void swap(int[] array, int i, int j) {
              if (checkIndexOutOfRange(array, i) || checkIndexOutOfRange(array, j))
                  return;
              int t = array[i];
              array[i] = array[j];
              array[j] = t;
          }
      
          public static final void reverse(int[] array, int startIndex, int endIndex) {
              if (checkIndexOutOfRange(array, startIndex) || checkIndexOutOfRange(array, endIndex))
                  return;
              while (startIndex < endIndex) {
                  swap(array, startIndex, endIndex);
                  startIndex++;
                  endIndex--;
              }
          }
      
          public static final void reverse(int[] array) {
              reverse(array, 0, array.length - 1);
          }
      
          public static final void leftRotate(int[] array, int shift) {
              int arrayLength = array.length;
              if (shift >= arrayLength)
                  shift %= arrayLength;
              reverse(array, 0, shift - 1);
              reverse(array, shift, arrayLength - 1);
              reverse(array);
          }
      }
      

答案 8 :(得分:1)

一次性阵列旋转的部分代码

       last=number_holder[n-1];
       first=number_holder[0];
        //rotation 

        number_holder[0]=last;

        for(i=1;i<n;i++)
        {
            last=number_holder[i];
            number_holder[i]=first;
            first=last;
        }

显示数组

        for(i=1;i<n;i++)
        {
          System.out.println(number_holder[i]);
        }

答案 9 :(得分:0)

AFAIK,有三种方法可以使用O(1)额外空间旋转数组,或换句话说,交换两个连续的子数组。

  • 反向接近。反转两部分,然后全部反转。最容易编码。
  • 先后交换两个连续的区块,直到所有项目都到位。
  • 杂耍旋转,贝壳类似。 - 缓存性能更差。

C ++内置函数std::rotate(),需要三个迭代器first, middle, last, 并返回new_middle,这是旧的第一个元素位于旋转的位置 序列

我已经检查了我的计算机上的实现,它使用了我上面列出的第二种方法。 (/usr/lib/gcc/i686-pc-cygwin/5.4.0/include/c++/bits/stl_algo.h中的第1246行)。

以下是我使用测试程序实现的旋转。

#include <iostream>
#include <vector>

// same logic with STL implementation, but simpler, since no return value needed.
template <typename Iterator>
void rotate_by_gcd_like_swap(Iterator first, Iterator mid, Iterator last) {
    if (first == mid) return;
    Iterator old = mid;
    for (; mid != last;) {
        std::iter_swap(first, mid);
        ++first, ++mid;
        if (first == old) old = mid; // left half exhausted
        else if (mid == last) mid = old;
    }
}

// same logic with STL implementation
template <typename Iterator>
Iterator rotate_by_gcd_like_swap_then_return_new_mid(Iterator first, Iterator mid, Iterator last) {
    if (first == mid) return last;
    if (mid == last) return first;
    Iterator old = mid;
    for(;;) {
        std::iter_swap(first, mid);
        ++first, ++mid;
        if (first == old) old = mid;
        if (mid == last) break;
    }
    Iterator result = first; // when first time `mid == last`, the position of `first` is the new `mid`.
    for (mid = old; mid != last;) {
        std::iter_swap(first, mid);
        ++first, ++mid;
        if (first == old) old = mid;
        else if (mid == last) mid = old;
    }
    return result;
}

int main() {
    using std::cout;
    std::vector<int> v {0,1,2,3,4,5,6,7,8,9};
    cout << "before rotate: ";
    for (auto x: v) cout << x << ' '; cout << '\n';
    int k = 7;
    rotate_by_gcd_like_swap(v.begin(), v.begin() + k, v.end());
    cout << " after rotate: ";
    for (auto x: v) cout << x << ' '; cout << '\n';
    cout << "sz = " << v.size() << ", k = " << k << '\n';
}

答案 10 :(得分:0)

#include <stdio.h>

int
main(void)
{
    int arr[7] = {1,2,3,4,5,6,7};
    int new_arr[7] = {0};
    int k = 3;
    int len = 7;
    int i=0;

    for (i = (len-1); i>=0; i--) {
        if ((i+k) >= len) {
            new_arr[(i+k-len)] = arr[i];
        } else {
            new_arr[(i+k)] = arr[i];
        }
    }

    for (i=0;i<len;i++) {
        printf("%d ", new_arr[i]);
    }

    return 0;
}

时间复杂度O(n) 空间复杂度O(2 * n)。

感谢。

答案 11 :(得分:0)

该解决方案是O(1)空间和O(N)时间。它在C#中,接受一个数组参数并将其旋转到位。该算法遍历第一个s(移位)元素,从第一个元素开始移动到第s个位置,然后将s_th移动到第2个位置等。如果每个第一个元素旋转回自身,那么将会有(arrayLength / s)* s = arrayLength循环,最后数组将旋转s。如果前面的s元素不会自行旋转,那么仍然会有循环,比如s = 4,可能有一个循环是1-3-1,第二个2-4-2,行 - 如果( ind == indAtBeg),检查一个循环并终止while循环。当从第一个s元素中的任何一个开始旋转时,变量loopCount递增。

    public static void rotateArrayByS(int[] ar, int s)
    {
        int len = ar.Length, ind = 0, temp1 = ar[0], 
            temp2 /*temp1 and temp2 for switching elements*/, 
            loopCount /*rotations starting at the first s elemtns of ar*/ = 0;

        s %= len;

        while (loopCount < s)
        {
            int indAtBeg = ind;
            temp1 = ar[ind];

            bool done = false;
            while (!done)
            {
                if (ind < s)
                    loopCount++;

                ind = (ind + s) % len;

                //cycle detected
                if (ind == indAtBeg)
                    done = true;

                //switch the elements
                temp2 = ar[ind];
                ar[ind] = temp1;
                temp1 = temp2;
            }

            ++ind;
        }
    }

答案 12 :(得分:0)

以下是通过k步

进行左右数组旋转的完整Java代码
import java.util.*;

public class ArrayRotation {
    private static Scanner sc;

    public static void main(String[] args) {
        int n,k;
        sc = new Scanner(System.in);
        System.out.print("Enter the size of array: ");
        n = sc.nextInt();

        int[] a = new int[n];
        System.out.print("Enter the "+n+" elements in the list: ");
        for(int i=0;i<n;i++)
            a[i] = sc.nextInt();

        System.out.print("Enter the number of left shifts to array: ");
        k = sc.nextInt();

        System.out.print("Array before "+k+" shifts: ");
        display(a);

        leftRoation(a,k);
        System.out.println();

        System.out.print("Array after "+k+" left shifts: ");
        display(a);

        rightRoation(a,k);
        System.out.println();

        System.out.print("Array after "+k+" right shifts: ");
        display(a);
    }

    public static void leftRoation(int[] a, int k){
        int temp=0, j;
        for(int i=0;i<k;i++){
            temp = a[0];
//          j=0;                    // both codes work i.e. for loop and while loop as well
//          while(j<a.length-1){
//              a[j]=a[j+1];
//              j++;
//          }   
            for(j=0;j<a.length-1;j++)
                a[j]=a[j+1];
            a[j]=temp;
        }           
    }

    public static void rightRoation(int[] a, int k){
        int temp=0, j;
        for(int i=0;i<k;i++){
            temp = a[a.length-1];
            for(j=a.length-1;j>0;j--)
                a[j]=a[j-1];
            a[j]=temp;
        }           
    }

    public static void display(int[] a){
        for(int i=0;i<a.length;i++)
            System.out.print(a[i]+" ");
    }
}

/****************** Output ********************
    Enter the size of array: 5
    Enter the 5 elements in the list: 1 2 3 4 5
    Enter the number of left and right shifts to array: 2
    Array before 2 shifts: 1 2 3 4 5 
    Array after 2 left shifts: 3 4 5 1 2 
    Array after 2 right shifts: 1 2 3 4 5  // here the left shifted array is taken as input and hence after right shift it looks same as original array.
 **********************************************/

答案 13 :(得分:0)

我的解决方案......(a:数组,n:数组大小,k:移位数):

 public static int[] arrayLeftRotation(int[] a, int n, int k) {

    if (k == 0) return a;

    for (int i = 0; i < k; i++) {
        int retenue = a[0];
        int[] copie = java.util.Arrays.copyOfRange(a, 1, n );
        for (int y = 0; y <= copie.length - 1 ; y++) {
            a[y] = copie[y];
        }
        a[n-1] = retenue;
    }
    return a;
}

答案 14 :(得分:0)

这是旋转数组的简单解决方案。

public class ArrayRotate {
    public int[] rotateArray(int array[], int k) {

        int newArray[] = new int[array.length];
        for (int i = 0; i < array.length; i++) {
            newArray[(i + k) % array.length] = array[i];
        }
        System.arraycopy(newArray, 0, array, 0, array.length);
        return newArray;

    }

    public static void main(String[] args) {
        int array[] = { 1, 2, 3, 4, 5, 6, 7 };
        ArrayRotate rotate = new ArrayRotate();
        rotate.display(rotate.rotateArray(array, 3));

    }

    public void display(int array[]) {
        for (int i : array) {
            System.out.print(i + ",");
        }
    }
}

运行时复杂性为O(n)

还有其他几种算法可以达到同样的目的。

  • 使用临时数组
  • 逐一旋转
  • 杂耍算法
  • 逆转方法

答案 15 :(得分:0)

Java实现右旋转

            public int[] solution(int[] A, int K) {
                int len = A.length;
                //Create an empty array with same length as A
                int arr[] = new int[len];

                for (int i = 0; i < len; i++) {
                    int nextIndex = i + K;
                    if (nextIndex >= len) {
                        // wraps the nextIndex by same number of K steps
                        nextIndex = nextIndex % len;
                    }
                    arr[nextIndex] = A[i];
                }
                return arr;
            }

答案 16 :(得分:0)

>>> k = 3
>>> arr = [1,2,3,4,5,6,7]
>>> actual_rot = k % len(arr)
>>> left_ar = arr[:-actual_rot]
>>> right_ar = arr[-actual_rot:]
>>> result = right_ar + left_ar
>>> result
[5, 6, 7, 1, 2, 3, 4]

答案 17 :(得分:0)

1。使用临时数组和O(n)时间

public static void rotateAnArrayUsingTemp(int arr[], int d, int n) {
    int temp[] = new int[d];
    int tempIndex = 0;
    for (int i = 0; i < d; i++) {
        temp[i] = arr[i];
    }
    for (int i = 0; i < arr.length - d; i++) {
        arr[i] = arr[i + d];
    }
    for (int i = arr.length - d; i < arr.length; i++) {
        arr[i] = temp[tempIndex++];
    }
}

答案 18 :(得分:0)

以k步旋转数组的更好方法是:

a = [1,2,3,4,5,6]
b = a[:]
k = 2
for i in range(len(a)):
    a[(i + k) % len(a)] = b[i]## (rotate right by k steps)
    #a[(i - k) % len(a)] = b[i]## (rotate left by k steps)
print(a)

o / p: [6,5,1,2,3,4]

答案 19 :(得分:0)

如何旋转数组,在此函数中,第一个参数-数组,第二个参数为 一个数字或整数。

 QFont font("times", 25);
 QFontMetrics fm(font);
 int pixelsW{fm.width("HI-SCORE")};
 int pixelsH{fm.height()};

答案 20 :(得分:0)

这会将数组向右旋转k步,其中k为非负数

    for (int i = 0; i < k; i++) { 
      for (int j = nums.length - 1; j > 0; j--) {
         int temp = nums[j];
         nums[j] = nums[j - 1];
         nums[j - 1] = temp;
      }

   }
  return nums;

答案 21 :(得分:0)

    if (k > arr.length) {
        k = k % arr.length;
    }
    int n = arr.length - k;
    int count = 0;
  outer:
    for (int i = arr.length - 1; i >= n; i--) {
         int temp = arr[i];
       inner:
         for (int j = i - 1; j >= 0; j--) {
             arr[j + 1] = arr[j];
             if (j == 0) {
                 int temp2 = arr[j];
                 arr[j] = temp;
                 i = arr.length;
                 count++;
                 if (count == k) {
                     break outer;
                 }
             }
         }
     }
    

答案 22 :(得分:-1)

您似乎可以用这种简单的方式做到这一点:

 public static void main(String[] args) {
        int arr[] = {1, 2, 3, 4, 5, 6, 7,8,9};
        System.out.println();
        System.out.println(Arrays.toString(rotateArray(arr, 5)));
    }


   static int [] rotateArray(int arr [], int k){
        int [] newArr = new int [arr.length];
        int newIndex = 0;
        for(int i  = k; i < arr.length; i ++){
            newArr[newIndex++] = arr[i];
        }
        for(int i = 0; i < k; i++ ){
            newArr[newIndex++] = arr[i];
        }
       return newArr;
    }