题目描述
思路解析
一句话答案:LeetCode 1929 数组串联:把 nums 首尾拼两遍造出长度 2n 的答案,第 i 位就是 nums[i mod n],前半照抄、后半靠取模回绕再抄一遍,时间 O(n)、空间 O(n)。
答案要多长,前后两半各装什么
给一个长度为 n 的数组 nums,让你造一条长度 2n 的新数组 ans:前 n 个位置照抄 nums,后 n 个位置再抄同样的 nums,首尾接起来就行。题面例子 nums=[1,2,1],造出来是 [1,2,1,1,2,1],前三格和后三格一模一样;nums=[1,3,2,1] 则拼成 [1,3,2,1,1,3,2,1]。后半段不做任何反转或加工,就是把原数组原样再来一遍。
前后两半能不能用同一个式子写出来
分两段各填一次当然行,但有个更省心的写法:ans 的第 i 位永远等于 nums[i mod n]。这里的 mod 是取模,也就是拿 i 除以 n 取余数。i 在 0 到 n-1 之间时,余数就是 i 本身,读到的正是 nums[i],前半段自然照抄;i 一旦到了 n、n+1,余数回绕成 0、1,又从头读 nums[0]、nums[1],后半段就这么接着抄下来。一个式子把前后两半都管住,不用再分情况判断当前落在哪一半。
同一件事,三种语言各怎么落地
Python 最省事,一行 nums + nums 就是两个列表首尾拼接,返回即可。想自己控制过程的话,新开一条长度 2n 的数组,i 从 0 走到 2n-1,每一位填 nums[i mod n],靠取模把后半段回绕到前半。C++ 还能更省:先用一个变量记住原长度 n,再往原数组尾部 push_back 补上 n 个副本——这里必须先把 n 存下来,因为边加边读 nums.size(),长度一直在涨,循环条件永远追不上,会没完没了地补。
[1,2,1] 的后半段怎么回绕填出来
n=3,ans 长度 6。前半段先照抄:ans[0]=nums[0]=1,ans[1]=nums[1]=2,ans[2]=nums[2]=1。到第 3 位,i mod n = 3 mod 3 = 0,回绕读 nums[0]=1;第 4 位 4 mod 3 = 1,读 nums[1]=2;第 5 位 5 mod 3 = 2,读 nums[2]=1。六个位置填完是 [1,2,1,1,2,1],和题面输出对得上,后三格正是前三格的复制。
长度开小了,后半段就没处安放
最常见的错是只申请长度 n 的数组,后半段一写就越界——ans 得装下两份 nums,长度必须是 2n。再一个是把后半段想复杂,以为要反转或做点变换,其实逐位照抄原数组即可。还有下标:后半段若直接写 nums[i],i 到了 n 及以后会读到 nums 的越界位置,统一用 nums[i mod n] 才能回绕到前半。复杂度这边很干脆:2n 个位置各赋值一次,时间 O(n);答案数组长度 2n,空间 O(n),若不把返回值计入额外开销,则只用常数个变量,记 O(1)。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记牢这一句:ans[i] 等于 nums[i mod n]。下面把 i 从 0 数到 9,每一步先读源数组的第 (i mod n) 个,再写进 ans 的第 i 格。
开场:上面这条 ans 有 10 个格子,现在全是「·」表示还没填。右边侧栏是源数组 nums,一共 5 个数。接下来 i 从 0 走到 9,一格一格把 ans 填满。
第 0 步,i mod n 还等于 i,就是 0。到侧栏读源数组第 0 个,值是 3。紫色格子是 ans 里马上要填的第 0 位。
把 3 写进 ans 的第 0 格,它变绿点亮。继续往下一格走。
第 1 步,i mod n 还等于 i,就是 1。到侧栏读源数组第 1 个,值是 8。紫色格子是 ans 里马上要填的第 1 位。
把 8 写进 ans 的第 1 格,它变绿点亮。继续往下一格走。
第 2 步,i mod n 还等于 i,就是 2。到侧栏读源数组第 2 个,值是 5。紫色格子是 ans 里马上要填的第 2 位。
把 5 写进 ans 的第 2 格,它变绿点亮。继续往下一格走。
第 3 步,i mod n 还等于 i,就是 3。到侧栏读源数组第 3 个,值是 6。紫色格子是 ans 里马上要填的第 3 位。
把 6 写进 ans 的第 3 格,它变绿点亮。继续往下一格走。
第 4 步,i mod n 还等于 i,就是 4。到侧栏读源数组第 4 个,值是 2。紫色格子是 ans 里马上要填的第 4 位。
把 2 写进 ans 的第 4 格,它变绿点亮。到这里前半段填完了,ans 前 5 格正好是原数组。
第 5 步,i 已经到 5,正好等于 n,来到后半段起点。i mod n 回绕成 0,回头读源数组第 0 个,值还是 3。后半段照抄前半段就从这里开始。
把 3 写进 ans 的第 5 格,它变绿点亮。继续往下一格走。
第 6 步,i 已经到 6,越过了前半段。i mod n 回绕成 1,又回头读源数组第 1 个,值还是 8。这正是后半段照抄前半段的地方。
把 8 写进 ans 的第 6 格,它变绿点亮。继续往下一格走。
第 7 步,i 已经到 7,越过了前半段。i mod n 回绕成 2,又回头读源数组第 2 个,值还是 5。这正是后半段照抄前半段的地方。
把 5 写进 ans 的第 7 格,它变绿点亮。继续往下一格走。
第 8 步,i 已经到 8,越过了前半段。i mod n 回绕成 3,又回头读源数组第 3 个,值还是 6。这正是后半段照抄前半段的地方。
把 6 写进 ans 的第 8 格,它变绿点亮。继续往下一格走。
第 9 步,i 已经到 9,越过了前半段。i mod n 回绕成 4,又回头读源数组第 4 个,值还是 2。这正是后半段照抄前半段的地方。
把 2 写进 ans 的第 9 格,它变绿点亮。最后一格也填好了,整条 ans 全部点亮。
拼完回看一眼:ans 的前 5 格和后 5 格一模一样,都是原数组 nums。这就是数组串联,答案是 [3,8,5,6,2,3,8,5,6,2]。整件事就是把 nums 抄两遍接起来。
边界想清:单元素就写两遍、有重复值照抄不变、两元素的后半段回绕仍是原顺序。
面试重点:ans[i]=nums[i mod n]、三种等价写法、时间 O(n) 空间 O(n)。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class Solution: def getConcatenation(self, nums: List[int]) -> List[int]: return nums + nums复杂度
- 时间:O(n),ans 一共 2n 个位置,每个位置做一次读取加一次写入,都是常数操作,总量随 n 线性增长
- 空间:O(n),需要一条长度 2n 的答案数组,规模就是 n 的量级;若把返回值本身不计入额外空间,则额外只用常数个变量,记 O(1)
易错点
面试追问把动画讲成自己的话
追问这道题的核心思路一句话怎么说?
追问有几种等价写法?
追问时间和空间复杂度分别是多少?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
句子中的最多单词数
LeetCode 2114 · 简单 · 沿着 数组套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题