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)