力扣189. 旋转数组--两法两种语言

189. 旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

示例 1:

输入: [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:

输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]

说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。

方法一:使用栈申请额外数组

由于题目中为同时移动,所以我们不能直接单纯的交换,这样会使值出现混乱,所以我们可以先使用一个临时数组来接收一下正确位置应该有的值。
且对于给定的平移步数k我们可以先给他对numsSize取余数,这样的话即对于重复操作不用执行了。
且若平移步数不足以使其发生“越界”行为时,可以直接平移。
若是会发生“越界”操作,则对于右移直接变为左移即可避免越界。

代码:

void rotate(int* nums, int numsSize, int k){
    
    
    int *temp=(int*)malloc(numsSize*sizeof(int));
    k=k%numsSize;
    for(int i=0;i<numsSize;i++)
    {
    
    
        if(numsSize-1-i>=k)//不会越界时
        {
    
    
            temp[i+k]=nums[i];
            continue;
        }
        else//会越界时
        {
    
    
            int temp1=k-(numsSize-1-i);//此为先把他移到尾部后还剩余的步数
            temp1=numsSize-temp1;//此为需要向左移动的步数
            int index = numsSize-1-temp1;//此为移动之后对应的新下标
            temp[index]=nums[i];
            continue;
        }
    }
    for(int i=0;i<numsSize;i++)
    {
    
    
        nums[i]=temp[i];//覆盖
    }
    free(temp);
}

方法二:反转数组

由于上面申请了额外数组,所以空间复杂度为O(n)。
所以我们可以先观察一下原数组与旋转后的数组。
举个例子:
对于1,2,3,4,5,6,7
其向右移动3个单位后变为5,6,7,1,2,3,4
我们可以发现5,6,7由后面变为了前面。
1,2,3,4由前面变成了后面。
并且我们可以发现这个所谓的“前面”与“后面”,其实是由于步数k影响的,即通过步数k分成的前面与后面
且原数组处于后面位置的当再移动时其实就是从后面变到前面,原数组处于前面位置的再移动时其实就是从前面变到后面而已。
因此我们可以先将整个数组反转一下,因为反转就是前变后,后变前的操作,所以先帮助他们完成“从前到后”和“从后到前”操作。之后由于你对整个数组使用了反转,你改变了他们互相的相对位置,所以还需要对前面和后面部分分别再反转回来,恢复他们的相对位置即可。

–注:
为什么代码中是reverse(nums,0,k-1)与reverse(nums,k,numsSize-1)?
法一:几何法
因为我们是挪移k步,那么从最后一个开始计数的话只有倒数k个可以通过移动k步从后面到达前面。所以我们可以知道原来的“后面”为k个。既然如此,他变成“前面”后,也是k个,所以对此部分进行恢复位置操作时,下标到k-1即可。自然剩余的是k到numsSize-1。
法二:数学法
设下标为i时其移动k步刚好到达数组末尾。
所以有i+k=numsSize-1
所以可以知道原先“前面”的个数为i+1,即numsSize-k.
所以后面个数可知为numsSize-(numsSize-k)=k
余下同理上法。

代码:

void reverse(int*a,int start,int end)
{
    
    
    while(start<=end)
    {
    
    
        int temp;
        temp=a[end];
        a[end]=a[start];
        a[start]=temp;
        start++;
        end--;
    }
}
void rotate(int* nums, int numsSize, int k){
    
    
    k=k%numsSize;
    reverse(nums,0,numsSize-1);
    reverse(nums,0,k-1);//此为对“后变前”的部分进行恢复顺序
    reverse(nums,k,numsSize-1);//此为对“前变后”的部分进行恢复顺序
}

附上Python代码:

对于Python直接将我们分成的前面与后面切片即可,切出来后直接重新前后拼接一下即可。

class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        k=k%len(nums)
        nums_len = len(nums)
        num1 = nums[:nums_len-k]
        del nums[:nums_len-k]
        nums.extend(num1)

猜你喜欢

转载自blog.csdn.net/xiangguang_fight/article/details/112363407