1. トピックの説明と要件
トピックの説明
integer の配列を指定して nums
、配列内の要素を位置だけ右に k
k
回転します。ここで、は負ではない数値です 。
例
例 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 <= 105
-231 <= nums[i] <= 231 - 1
0 <= k <= 105
2. 問題解決のアイデア
一般的なアイデア:
トピックの分析によると、回転量 k に応じて k 個の要素を配列の後ろに元の順序で配置し、配列の前の要素を順番に移動する必要があります。たとえば、k=1 の場合、配列要素は 1234567 であり、回転後の結果は 5671234 に変更される必要があります。
この処理を実現するには、別の配列を直接使用し、元の配列の要素を回転後の順序で補助配列に格納し、その配列に補助配列の値を代入するのが最も簡単です。要素の回転に対応する位置は、(i+k)%numsSize で取得できます。この方法の時間計算量は O(n) であり、空間計算量も O(n) です。使用されるスペースの複雑さを軽減する方法はありますか? 配列要素が大きすぎると、大きな補助スペースが必要になるためです。
1234 567 と 567 1234 を観察すると、回転は k の値に従って配列の後ろにある k 個の要素を前に置くのと同じであることがわかります。そのため、配列を直接 2 つの部分に分割することもできます。 2 つの部分に分割した後、対応する順序に従う さて、最初に 2 つの部分に分割して対応する順序を達成するのは明らかに困難です。次に、次の k 個の要素を前に配置し、前の要素を後ろに配置する必要があると考えます。その場合は、最初に配列を反転するとよいでしょう。つまり、「逆順」になります。次に、配列を 2 つの部分に分割します。この時点では、各部分に含まれる要素は正しいですが、反転ソートを実行したため順序が間違っています。そのため、2 つの部分を再度反転するだけで済みます。
具体的な手順:
補助配列を使用します。
①補助配列を定義する
②補助配列に対象に合わせて値を代入する
③補助配列を利用して元の配列を更新する
配列の反転: [元の配列に変更を加える]
①回転量が配列要素より大きいかどうかを判断し、大きい場合は余りを取る(回転させるビット数が少ないため)
②配列全体を反転します(ダブルポインタを使用して最初と最後の要素を交換します)
③ 反転した配列の最初の k 要素を反転します。
④反転した配列の後半を反転する
3. 具体的なコード【C言語】
① 補助配列を使用する [時間 O(n) 空間 O(n)]
void rotate(int* nums, int numsSize, int k){
if( k ==0 || numsSize == 1) return;
int tem[numsSize];//定义辅助数组
for(int i=0;i<numsSize;i++){
tem[(i+k)%numsSize]=nums[i];//在辅助数组中将每一个元素放到对应位置
}
for(int i=0;i<numsSize;i++){//复制数组
nums[i]=tem[i];
}
}
②配列反転:[元の配列を変更] [時間 O(n) 空間 O(1)]
void swap(int* a, int* b) //传址
{
int t = *a;
*a = *b;
*b = t;
}
void reverse(int* nums, int left, int right) //对数组进行翻转
{
while (left < right)
{
swap(&nums[left], &nums[right]);//将首尾两个数进行交换
left += 1;
right -= 1;
}
}
void rotate(int* nums, int numsSize, int k){
k%=numsSize; //避免出现轮转量大于数组元素的情况
if( k ==0 || numsSize == 1) return;
//翻转整个数组
reverse(nums, 0, numsSize - 1);
//将翻转后的前面部分变回原始顺序
reverse(nums, 0, k - 1);
//将翻转后的后面部分变回原始顺序
reverse(nums, k, numsSize - 1);
}