如何旋转数组?
Posted
技术标签:
【中文标题】如何旋转数组?【英文标题】:How to rotate an array? 【发布时间】:2015-09-19 09:48:12 【问题描述】:我有以下问题要测试:
将
n
元素数组向右旋转k
步。例如,对于
n = 7
和k = 3
,数组[1,2,3,4,5,6,7]
被旋转为[5,6,7,1,2,3,4]
。你知道有多少种不同的方法可以解决这个问题?
我在中间数组中的解决方案:
空间是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
) 空间中使用冒泡旋转(如冒泡排序)来实现?
【问题讨论】:
你确定“在n = 5和k = 2的情况下,数组[1,2,3,4,5,6,7]旋转到[5,6,7,1 ,2,3,4]" 正确吗?该数组中的元素似乎超过 5 个? ***.com/questions/26610309/java-rotating-array 我不知道n = 5
和k = 2
如何给出您在问题中显示的输出。
不应该是“n = 7 and k = 3”吗?
我最喜欢解决这个问题的方法是编程珍珠中描述的方法,您可以反转整个数组,然后反转子部分。更多信息在这里:articles.leetcode.com/2010/04/rotating-array-in-place.html
【参考方案1】:
方法 1 - 反转算法(好一个):
算法:
旋转(arr[], d, n)
反向(arr[], l, n);
reverse(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得到胸罩。 /* 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 - 杂耍算法
将数组划分为不同的集合,其中集合的数量等于 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
元素首先在第一组中移动 此步骤后的 arr[] --> 4 2 3 7 5 6 10 8 9 1 11 12
然后在第二组。 此步骤后的 arr[] --> 4 5 3 7 8 6 10 11 9 1 2 12
终于进入第三组了。 此步骤后的 arr[] --> 4 5 6 7 8 9 10 11 12 1 2 3
代码如下:
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
将 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[] 旋转 1 2 次。第一次旋转后得到 [7, 1, 2, 3, 4, 5, 6],第二次旋转后得到 [6, 7, 1, 2, 3, 4, 5]。
她是代码片段:
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)
【讨论】:
我无法检查您的第二个和第三个算法。但是从你的第一个算法我看到你在做左旋转而不是右旋转。你看到问题了吗? 好的,抱歉,但不是向左旋转 d 次,而是执行 n-d 次的算法...我可以解决 需要gcd吗?当数组元素赋值的个数达到数组大小时,我们可以打破循环吗? 在方法1中时间复杂度是O(n * 2) 怎么样?你可以解释吗? BTW 反转数组是 O(n)。调用此反向三次将是 O(n)+O(n)+O(n)。【参考方案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);
【讨论】:
你能解释一下你的解决方案吗?背后的逻辑是什么? @mzeus.bolt 您可能需要为if (nums.length > 0)
和if (k != 0)
添加测试以避免异常和空反转。【参考方案3】:
k
为负数时,向左旋转。
空间是O(1),时间是O(n)
static void rotate(int[] num, int k)
int n = num.length;
k = k % n;
if (k < 0) k += n;
int[] result = new int[n];
System.arraycopy(num, 0, result, k, n - k);
System.arraycopy(num, n - k, result, 0, k);
System.arraycopy(result, 0, num, 0, n);
【讨论】:
【参考方案4】:这是一个旋转数组的简单解决方案。
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)
还有其他几种算法可以实现相同的效果。
使用临时数组 一个接一个旋转 杂耍算法 反转法【讨论】:
System.arraycopy 是额外的 O(n) @nim: O(n) + O(n) = O(n)。真正的缺点是空间复杂度也是 O(n)。【参考方案5】:这个解是 O(1) 空间和 O(N) 时间。它在 C# 中,采用数组参数并将其旋转到位。该算法遍历前 s (移位)元素,从第一个元素开始将其移动到第 s_th 位置,然后将 s_th 移动到第 2s_th 位置等。如果前 s 元素中的每一个都旋转回自身,那么将有(arrayLength / s) * s = arrayLength 循环,最后数组将旋转 s。如果第一个 s 元素没有自己旋转回来,那么仍然会有循环,比如如果 s = 4,可能会有一个循环是 1-3-1,第二个是 2-4-2,即行 - if ( 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;
【讨论】:
【参考方案6】:ONE 时间数组旋转的部分代码
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]);
【讨论】:
【参考方案7】:ArrayUtil 类用于在原始数组中提供以下实用程序
-
交换数组元素
startIndex 和 endIndex 之间的反向数组
leftRotate array by shift
数组旋转算法-
-
如果我们必须按移位值反转数组,则使用数组长度取 mod(%),这样移位将小于数组长度。
索引 0 和 shift-1 之间的反向数组
在索引移位和长度为 1 之间反转数组。
在索引 0 和长度 1 之间反转完整数组。
空间复杂性:就地算法,不需要额外空间,所以 O(1)。
时间复杂度: 大小为 k 的数组反转需要 O(k/2),即交换 k/2 对元素。
数组反转时间 - k 大小数组的 O(k)。
轮换总时间-
O(1) ..........第 1 步 O(shift) ......对于第 2 步 O(n - shift) ...对于第 3 步 O(n) ......对于第 4 步数组旋转的总时间:O(1) + O(shift) + O(n-shift) + 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);
【讨论】:
O(2*n) 总时间【参考方案8】:AFAIK,有三种方法可以使用 O(1) 额外空间旋转数组,或者换一种说法,交换两个连续的子数组。
反向方法。反转两个部分,然后反转全部。最容易编码。 连续交换两个连续块,直到所有项目都到位。 杂耍旋转,shell排序。 -- 缓存性能更差。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';
【讨论】:
第二种和第三种方法都与 gcd(最大公约数)有关。【参考方案9】:上述解决方案讨论了通过反转数组元素或任何其他替代方法来移动数组元素。
我有独特的解决方案。如何在 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;
【讨论】:
【参考方案10】:#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】: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]
【讨论】:
在这个解决方案中 O(n * 2) 时间【参考方案12】:这里是左右数组旋转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】:我的解决方案...(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】:在 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, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2 , 1]
【讨论】:
【参考方案15】:右旋转的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】:>>> 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】: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】:将数组旋转 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】:如何旋转一个数组,在这个函数中第一个参数-数组,第二个参数是 一个数字或整数。
def rotLeft(a, d):
data = a
n = d
get = data[n:len(data)]
remains = data[0:n]
data.clear()
for i in get:
data.append(i)
for x in remains:
data.append(x)
return data
【讨论】:
【参考方案20】:这是将数组向右旋转 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;
【讨论】:
这是将数组向右旋转 k 步,这在上面的问题陈述中明确提到,其中 k 是非负数。希望现在清楚了。谢谢 这更好,它确实向右旋转,但在一般情况下比 OP 提出的解决方案要多得多。【参考方案21】: 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;
【讨论】:
第一个测试应该是if (k >= arr.length)
,int temp2 = arr[j];
这行是没用的,不需要标记内循环。如果将内循环条件更改为j > 0
,则可以删除if (j == 0)
并将if
的主体移出内循环,从而无需outer
标记。实际上,似乎不需要count
变量和测试。【参考方案22】:
在这里我已经解决了同样的问题。 Try to run in go playground...
示例代码。
func rotate(a []int, k int)
for j := 0; j < k ; j++
temp := a[len(a)-1]
for i := len(a) - 1; i > 0; i--
a[i] = a[i-1]
a[0] = temp
【讨论】:
以上是关于如何旋转数组?的主要内容,如果未能解决你的问题,请参考以下文章