Leetcode189. 旋转数组
题目:
给定一个数组,将数组中的元素向右移动 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]
题解:
方法一:
使用额外的数组
我们可以用一个额外的数组来将每个元素放到正确的位置上,也就是原本数组里下标为 iii 的我们把它放到 (i+k)%数组长度(i+k)\%数组长度(i+k)%数组长度 的位置。然后把新的数组拷贝到原数组中。
scala代码如下:
def rotate(nums: Array[Int], k: Int): Unit = {
val arr = new Array[Int](nums.length)
for (i <- 0 until nums.length) {
arr((i + k) % nums.length) = nums(i)
}
for (i <- 0 until nums.length) {
nums(i) = arr(i)
}
}
方法二:
使用环状替换
如果我们直接把每一个数字放到它最后的位置,但这样的后果是遗失原来的元素。因此,我们需要把被替换的数字保存在变量 temp 里面。然后,我们将被替换数 temp 放到它正确的位置,并继续这个过程 n 次, n 是数组的长度。这是因为我们需要将数组里所有的元素都移动。但是,这种方法可能会有个问题,如果 n%k==0,其中 k=k%n (因为如果 k 大于 n ,移动 k 次实际上相当于移动 k%n 次。这种情况下,我们会发现在没有遍历所有数字的情况下回到出发数字。此时,我们应该从下一个数字开始再重复相同的过程。
现在,我们看看上面方法的证明。假设,数组里我们有 n个元素并且 k 是要求移动的次数。更进一步,n%k=0 。第一轮中,所有移动数字的下标 i满足 i%k == 0 。这是因为我们每跳 k 步,我们只会到达相距为 k 个位置下标的数。每一轮,我们都会移动 n/k 个元素。下一轮中,我们会移动满足 i%k == 1 的位置的数。这样的轮次会一直持续到我们再次遇到 i%k==0 的地方为止,此时 i=k 。此时在正确位置上的数字共有 k * k/n = n 个。因此所有数字都在正确位置上。
让我们看一下接下来的例子,以更好地说明这个过程:
scala代码如下:
def rotate2(nums: Array[Int], k: Int): Unit = {
var k1 = k
k1 = k1 % nums.length
var count = 0
var start = 0
while (count < nums.length) {
var current = start
var pre = nums(start)
do {
val next = (current + k1) % nums.length
val temp = nums(next)
nums(next) = pre
pre = temp
current = next
count = count + 1
} while (start != current)
start = start + 1
}
}
方法三:
反转
例:输入: [1,2,3,4,5,6,7] 和 k = 3
原始数组 : 1 2 3 4 5 6 7
反转所有数字后 : 7 6 5 4 3 2 1
反转前 k 个数字后 : 5 6 7 4 3 2 1
反转后 n-k 个数字后: 5 6 7 1 2 3 4 --> 结果
scala代码如下:
def rotate3(nums: Array[Int], k: Int): Unit = {
var k1 = k
k1 = k1 % nums.length
reverse(nums, 0, nums.length - 1)
reverse(nums, 0, k1 - 1)
reverse(nums, k1, nums.length - 1)
}
def reverse(nums: Array[Int], start: Int, end: Int): Unit = {
var start1 = start
var end1 = end
while (start1 < end1) {
val temp = nums(start1)
nums(start1) = nums(end1)
nums(end1) = temp
start1 = start1 + 1
end1 = end1 - 1
}
}