【LeetCode---189】旋转数组---双指针
【LeetCode---189】旋转数组---双指针
一、题目
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
给定一个数组,将数组中的元素向右移动 k
个位置,其中 k
是非负数。
进阶:
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
- 你可以使用空间复杂度为 O(1) 的 原地 算法解决这个问题吗?
示例1:
输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例2:
输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
提示:
1 <= nums.length <= 2 * 104
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
二、我的错误解
1、我的思路---跟着范例来的
2、代码
class Solution {
public void rotate(int[] nums, int k) {
int sourcePos = 0;//当前的下标,从0开始。
int targetPos = 0;//目标移动到的地方
int i=0;//计数器,一共要循环做多少次
int nextValue=0;//目标移动到的地方的值
int sourceValue=nums[0];//当前的值,从第一个开始。
int nextPos = 0;
for (i=0;i<nums.length;i++){
//寻找到目标移动到的地方
targetPos = getTargetPos(sourcePos,k,nums.length);
//将当前的目标移动到目标位置,并记录目标位置的值。
nextValue = move(nums,sourceValue,targetPos);
//记录下一次的sourcePos,该做哪个下标了
sourcePos = targetPos;
//sourceValue更新
sourceValue = nextValue;
}
}
public int getTargetPos(int sourcePos,int step,int length){
int targetPos = sourcePos + step;
if(targetPos > length-1)
targetPos = (targetPos % (length-1))-1;
return targetPos;
}
//移动到目标位置,并返回目标位置的值
public int move(int[] a,int value,int target){
int temp = 0;
temp = a[target];
a[target] = value;
return temp;
}
}
3、测试用例通过
4、没有通过的案例
自己debug测试:
发现按照7个的长度,3的步长去移动成功。继续深入debug,是对于判断结果有错误,先跳过我的错误解。下面看官方的解。
三、官方解
1、使用额外的数组
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
int[] newArr = new int[n];
for (int i = 0; i < n; ++i) {
newArr[(i + k) % n] = nums[i];
}
System.arraycopy(newArr, 0, nums, 0, n);
}
}
2、环状替代
这种方法和我的思路类似,下面看一下官方的解清晰的思路。
①思路
②解释与图例
-
方式一:增加一个计数变量Count,每次一个位置移动成功就加1,当Count=len时候,即可结束循环。
-
方式二:
-
思考从0开始便利最终回到0,我们遍历了多少个元素。
-
③代码实现
public class Solution2 {
public static void rotate(int [] nums,int k){
int n = nums.length;
k = k % n;//移动的step,做处理
int count = gcd(k,n);//需要多少轮
for (int start=0;start < count;++start){
int current = start;
int prev = nums[start];
do {
int next = (current + k) %n;
int temp = nums[next];
nums[next] = prev;
prev = temp;//下一次循环需要用到
current = next;
}while (start != current);
}
}
//如果q和r分别是m除以n的商及余数,即m=nq + r,那么m和n的最大公约数等于n和r的最大公约数。
public static int gcd(int a,int b){
if (b==0)
return a;
return gcd(b,a%b);
}
}
3、数组反转
①算法解释
②代码实现
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 += 1;
end -= 1;
}
}
}
四、思考与反思
可以在我的程序中看到,写了一堆,声明了好多变量,最后自己都不知道该用哪个,以后在设计变量的时候,要在伪代码中写好,尽可能的少声明一些变量来解决问题。并且,写了好多函数,但其实没有什么实质的作用,可以看出思路还是比较混乱,在处理问题的时候,没有考虑周全。一般比如移动的步数,和数组的长度的关系,这是要考虑的,还有什么时候循环截止,循环停止的条件,如果只按照测试用例来写代码,肯定不行,只能通过一个,而在写代码之前就要把所有的情况都考虑进来,这就是思维的差距了。