[LeetCode] 189. Rotate Array

博客围绕LeetCode上数组右移k位的题目展开,给出四种解题思路。包括整体右移k步、使用额外数组、循环替代和反转数组。详细分析各方法的实现过程、时间和空间复杂度,如循环替代和反转数组时间复杂度为O(n),空间复杂度为O(1)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接: https://leetcode.com/problems/rotate-array/

1. 题目介绍

Given an array, rotate the array to the right by k steps, where k is non-negative.

给出一个一维数组和一个非负整数k,将数组向右循环移动 k 位。

输出移动后的数组。

注意,题目中有一个隐含的条件没有说出来,那就是k可能大于数组的长度。

Example 1:

Input: [1,2,3,4,5,6,7] and k = 3
Output: [5,6,7,1,2,3,4]
Explanation:
rotate 1 steps to the right: [7,1,2,3,4,5,6]
rotate 2 steps to the right: [6,7,1,2,3,4,5]
rotate 3 steps to the right: [5,6,7,1,2,3,4]

Example 2:

Input: [-1,-100,3,99] and k = 2
Output: [3,99,-1,-100]
Explanation: 
rotate 1 steps to the right: [99,-1,-100,3]
rotate 2 steps to the right: [3,99,-1,-100]

Note:

Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.
Could you do it in-place with O(1) extra space?
至少有三种方法可以解决这个问题。可以尝试空间复杂度为O(1)的方法吗?

2. 解题思路

方法1 - 整体右移k步

首先,k可能大于数组的长度,因此需要取 k = k % length,length是数组的长度。

如果想要采用空间复杂度为 O(1) 的做法,可以将最右边的元素保留下来,然后将所有元素右移一位,再把保留下的值赋值给最左边的元素。这样重复 k 步,就相当于全体右移了k位。
这样虽然空间复杂度为 O(1) ,但是时间复杂度却是 O(n*k)

实现代码

class Solution {
    public void rotate(int[] nums, int k) {
		int length = nums.length;
		if(length == 0) {
			return;
		}
		
		k = k%length;
	
		int temp = 0;
		for(int i = 1 ; i<=k ; i++) {
			temp = nums[length-1];
			for(int j = length-1 ; j>=1 ; j-- ) {
				nums[j] = nums[j-1];
			}
			nums[0] = temp;
			
		}
		return;
	}
}

在LeetCode的官方题解 中,还给出了一些其他的解法,下面对他们进行一个总结:
注: 以下的思路来自于 https://leetcode.com/problems/rotate-array/solution/

方法2 - 使用额外的数组

下面实现的代码的亮点在于它只用了一个for循环就完成了原来数组循环放入新数组的过程。它的规律在于旧数组的第 i 个元素,总会放到新数组的第 (i + k) % nums.length 位置上。实现的关键语句是

时间复杂度:O(n).
空间复杂度:O(n).

a[(i + k) % nums.length] = nums[i];

实现代码

public class Solution {
    public void rotate(int[] nums, int k) {
        int[] a = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            a[(i + k) % nums.length] = nums[i];
        }
        for (int i = 0; i < nums.length; i++) {
            nums[i] = a[i];
        }
    }
}

方法3 - 循环替代

这个方法非常巧妙,是依靠循环的特性实现的。之前所讲的方法,都是非常“宏观”的方法,是根据整体变换完的结果来改变数组的值。但是如果我们另辟蹊径,完全模拟一个每个数的移动过程,是不是也可以解决这个问题呢?

我们以下面这个例子来说明:
nums: [1, 2, 3, 4, 5, 6]
k = 2
因为k = 2,所以,1到3,3到5,5又会去1。{1、3、5} 这三个数会形成一个循环。我们就可以模拟这个过程。每次我们替换数字的时候,都需要先用一个变量保存被替换的数字,然后再用这个保存起来的变量去替换下一个数字。
首先,把3的值保留下来,然后1替换3
然后,把5的值保留下来,然后利用上一步保留的3,替换5
最后,把1的值保留下来,然后利用上一步保留的4,替换1
这时候,我们发现又回到最初开始的位置。于是结束{1,3,5}这个循环,来到2的位置,开始{2,4,6}的循环。
值得注意的是,{1、3、5}循环中的数字,下标 i % k 都等于0;{2、4、6}循环中的数字,下标 i % k 都等于1;
在这里插入图片描述
( 上图出处: https://leetcode.com/problems/rotate-array/solution/

什么时候结束模拟呢?如果数组中有n个数,那么我们模拟每个数的过程,就需要替换n次。于是每做1次替换,我们都对替换的次数+1,当替换的次数为 n 的话,就说明所有的数都已经移动完了,可以结束模拟了。

再举一个例子,
nums: [1, 2, 3, 4, 5, 6,7]
k = 2

模拟每一个数的移动过程,可以发现大概是这样的一个过程:
在这里插入图片描述
这次只有一个大的循环{1,3,5,7,2,4,6}。当替换的次数为7时,此时模拟结束。同时也恰好回到了最初开始的位置(nums[0]的位置)。

这样的方法,时间复杂度O(n),空间复杂度O(1)
为什么时间复杂度会是O(n)呢?因为这个方法的本质是模拟每一个数的移动,一共只有n个数,那就只会模拟n次,因此时间复杂度就是O(n)。

实现代码

class Solution {
    public void rotate(int[] nums, int k) {
		int length = nums.length;
		if(length == 0) {
			return ;
		}
		
		k = k % length;
		
		int Count = 0;
		
		for(int i = 0 ; i < length ; i++) {
			int current  = i;
			int templeft = nums[i];
			int tempright = 0;
			do {
				int next = ( current + k ) % length;
				tempright = nums[next];
				nums[next] = templeft;
				templeft = tempright;
				current = next;
				Count ++;
				
			}while(i != current);
			
			if( Count == length) {
				break;
			}
		}
		return;
	}
}

方法4 - 反转数组

首先让 k = k % length。 当我们将数组右移 k 步的时候,数组最后的 k 个数字会移到数组的最前面来。
于是我们可以首先反转数组中的全部元素,然后反转前 k 个数字,再反转后面的 n-k 个数字。

比如 n=7 , k=3 时:
最初的数组: 1 2 3 4 5 6 7
反转全部元素: 7 6 5 4 3 2 1
将前k个元素反转: 5 6 7 4 3 2 1
将后面n-k个元素反转: 5 6 7 1 2 3 4 这就是最终的结果了。

时间复杂度: O(n)
空间复杂度: O(1)

实现代码

public class Solution {
    public void rotate(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--;
        }
    }
}

参考资料

https://leetcode.com/problems/rotate-array/solution/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值