小哆啦与数组轮转的冒险
小哆啦开始力扣每日一题的第五天
在一个阳光明媚的早晨,小哆啦猫收到了一位老教授的请求:帮助他将一个数字阵列向右移动 kk 步。老教授说:“这可不是普通的任务,需要既简单又高效的方法!”
小哆啦的第一步尝试:手动轮转
小哆啦思索片刻,决定用最直观的方法解决这个问题。他拿出百宝袋,取出一个“推拉机关”,将数组的最后一个数字取出,然后放到数组的最前面。他开心地说道:
“这样一来,每次移动一步,重复 kk 次,肯定能搞定啦!”
他写下了自己的方法:
function rotate(nums: number[], k: number): void {
for (let i = 0; i < k; i++) {
nums.unshift(nums.pop()!);
}
}
然后小哆啦测试了一下:
const nums = [1, 2, 3, 4, 5, 6, 7];
const k = 3;
rotate(nums, k);
console.log(nums); // 输出:[5, 6, 7, 1, 2, 3, 4]
果然成功了!小哆啦拍了拍手,心满意足。不过,他很快发现了问题:
当数组变得很长,或者 kk 特别大的时候,他需要重复操作很多次,耗时越来越久。教授皱了皱眉:“小哆啦,这方法虽然可爱,但能不能再优化一点?”
优化后的高效方法:反转法
小哆啦拿起放大镜,仔细观察这个问题,突然灵机一动:
“其实我不需要每次移动一步,只要一次性调整位置就可以啦!”
他决定使用“切片法”,将数组分为两部分:
- 取出最后 kk 个数字,称为“尾巴”。
- 取出前面剩下的部分,称为“头部”。
- 重新拼接尾巴和头部,顺序调整后即可完成。
小哆啦开心地记录下这个优化的思路:
function rotate(nums: number[], k: number): void {
k = k % nums.length; // 防止 k 超过数组长度
const tailPart = nums.slice(-k); // 最后 k 个元素
const headPart = nums.slice(0, nums.length - k); // 剩余部分
nums.splice(0, nums.length, ...tailPart, ...headPart); // 拼接赋值
}
他再次测试:
const nums = [1, 2, 3, 4, 5, 6, 7];
const k = 3;
rotate(nums, k);
console.log(nums); // 输出:[5, 6, 7, 1, 2, 3, 4]
小哆啦的结论
教授看了看两种方法,点了点头:“你的第一种方法虽然直观,但在处理大数组时效率不高。而优化后的切片法,却能一次性完成任务,时间复杂度从 O(k×n)降到了 O(n)。”
小哆啦笑眯眯地说道:
“方法的选择要看场景,效率和直观性其实并不矛盾,只要多动脑筋,就能找到最适合的解决方案!”
从此,小哆啦的聪明才智被更多人知晓,他也更爱解开一个个编程难题,为小镇带来智慧和欢乐!