合并两个有序数组
题目
解题方法
合并为一个数组
func merge(nums1 []int, m int, nums2 []int, n int) {
copy(nums1[m:],nums2)
sort.Ints(nums1)
}
内置函数
copy函数
内置函数 copy() 可以将一个数组切片复制到另一个数组切片中,如果加入的两个数组切片不一样大,就会按照其中较小的那个数组切片的元素个数进行复制。
copy( destSlice, srcSlice []T) int
srcSlice 为数据来源切片,destSlice 为复制的目标(也就是将 srcSlice 复制到 destSlice),目标切片必须分配过空间且足够承载复制的元素个数,并且来源和目标的类型必须一致,copy() 函数的返回值表示实际发生复制的元素个数。
copy(nums1[m:],nums2)
}
在背景len(nums1) = m+n的前提下,nums1[m:]表示nums1中的从m开始的切片,和原切片共享一组底层数据,将nums2的数据填充其中,组成一个切片,进行的合并并没问题。但如果没有背景的条件下会触发panic,因为超出了切片大小。
sort函数
内置函数 sort 支持对整数、浮点数、字符串进行排序,也可以借助接口,实现任意结构体/切片排序,默认为升序排序,对于数组的排序也希望先转换为切片,因为数组为值类型,直接排序会拷贝,效率低,也可以使用sort.Sort(sort.Reverse(sort.IntSlice(nums)))包装切片实现降序。
双指针
func merge(nums1 []int, m int, nums2 []int, n int) {
mergeNum := make([]int,0,m+n)
i,j := 0,0
for{
if i == m{
mergeNum = append(mergeNum,nums2[j:]...)
break
}
if j == n{
mergeNum = append(mergeNum,nums1[i:]...)
break
}
n1 := nums1[i]
n2 := nums2[j]
if n1<=n2{
mergeNum = append(mergeNum,n1)
i++
}else{
mergeNum = append(mergeNum,n2)
j++
}
}
copy(nums1,mergeNum)
}
移除元素
题目
解题方法
双指针法
双指针法是一种高效的编程技巧,核心是用两个指针(或索引)在数据结构(如数组、链表、字符串)中协同移动,减少时间复杂度(通常从 O (n²) 优化到 O (n)) 或降低空间复杂度。
-
数组 / 字符串的「线性遍历优化」
需要遍历数组 / 字符串,且暴力解法需嵌套循环(O (n²)) 时,双指针可通过 “一次遍历” 完成目标,核心是利用元素的有序性或特定性质(如单调性、对称性)。
-
链表的「遍历与操作」
链表无法随机访问,双指针可解决 “遍历效率”“环检测”“中点查找” 等问题,核心是利用指针移动速度差或协同定位。
-
滑动窗口
滑动窗口的本质是「双指针维护的区间 [left, right]」,适用于子数组 / 子串的最值、计数、匹配等问题,核心是通过移动左右指针动态调整窗口,避免重复计算。
func removeElement(nums []int, val int) int {
i := 0
for _,v := range nums{
if v != val{
nums[i] = v
i++
}
}
return i
}
双指针法的核心适用条件总结
- 数据结构是线性的(数组、链表、字符串),支持指针 / 索引移动;
- 问题可通过「两个指针的协同移动」减少遍历次数(如利用有序性、单调性、对称性);
- 需优化时间复杂度(从 O (n²) 到 O (n))或空间复杂度(原地操作,避免额外容器)。
简单记:有序、线性、遍历优化、区间维护,遇到这类问题优先考虑双指针!
删除有序数组中的重复项
题目
26. 删除有序数组中的重复项 - 力扣(LeetCode)
解题方法
依旧是双指针法,很好用
func removeDuplicates(nums []int) int {
i :=0
for j,num := range nums{
if len(nums)-1 == j {
nums[i] = num
i++
break
}
if num == nums[j+1]{
continue
}
nums[i] = num
i++
}
return i
}
删除有序数组中的重复项 II
题目
解题方法
一直写的是nums[j] == nums[j-2]的判断条件,导致发现数组一直在被覆盖,脑子里没有快慢指针的概念,在快慢指针的场景下,两个指针同向移动,快指针负责 “探索”(遍历所有元素),慢指针负责 “记录”(维护目标区间 / 结果)
- 链表问题(环、中点、倒数元素)
- 需 “间接测量” 距离或位置(如倒数 k、环入口)
- 数组存在循环结构(如环形数组)
func removeDuplicates(nums []int) int {
if len(nums) <= 2 {
return len(nums)
}
i := 2
for j := 2; j <len(nums);j++{
if nums[j] == nums[i-2]{
continue
}
nums[i]= nums[j]
i++
}
return i
}
多数元素
题目
解题方法
func majorityElement(nums []int) int {
sort.Ints(nums)
return nums[len(nums)/2]
}
轮转数组
题目
解题方法
func rotate(nums []int, k int) {
newNums := append(nums,nums...)
k = k % len(nums)
copy(nums,newNums[len(nums)-k:len(nums)*2-k])
}
入参为值传递,传递的是切片头的副本,会先复制切片头的数据至函数内,赋值给内部的nums变量,内外共享一个底层数组。之后append会创建一个新的切片,指向新的底层数组,外部其实并没有变,如果如果直接nums=的形式赋值不会真正改变nums外部的数据。需要使用copy函数,实现底层数组的修改。
copy(nums, newNums[...]) 的核心是:利用函数内 nums 的 Data 指针(仍指向外层的原底层数组),把 newNums 中的目标数据,直接写入「外层的原底层数组」