Likou189。回転配列-2つの方法と2つの言語

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) 的 原地 算法。

方法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);
}

方法2:配列を逆にする

上記には追加の配列が適用されるため、スペースの複雑さは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)があるのはなぜですか?
方法1:幾何学的方法
kステップを移動するため、最後のステップから数えると、kステップを移動することで最後のkのみを後ろから前に移動できます。したがって、元の「背後」はkであることがわかります。この場合、彼が「正面」になった後、kもあるので、この部分で復元位置操作を実行するときは、k-​​1の添え字で十分です。当然、残りはkからnumsSize-1です。
方法2:数学的方法
添え字が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