leetcode 150 | 数组/字符串

25 阅读5分钟

合并两个有序数组

题目

88. 合并两个有序数组 - 力扣(LeetCode)

解题方法

合并为一个数组

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)
}

移除元素

题目

27. 移除元素 - 力扣(LeetCode)

解题方法

双指针法

双指针法是一种高效的编程技巧,核心是用两个指针(或索引)在数据结构(如数组、链表、字符串)中协同移动,减少时间复杂度(通常从 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
}

双指针法的核心适用条件总结

  1. 数据结构是线性的(数组、链表、字符串),支持指针 / 索引移动;
  2. 问题可通过「两个指针的协同移动」减少遍历次数(如利用有序性、单调性、对称性);
  3. 需优化时间复杂度(从 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

题目

删除有序数组中的重复项 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]
}

轮转数组

题目

189. 轮转数组 - 力扣(LeetCode)

解题方法

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 中的目标数据,直接写入「外层的原底层数组」