🔥 代码随想录笔记-数组篇

567 阅读6分钟

数组.jpg

1.二分查找

思路

1.1 二分查找的前提条件

有序数组、无重复元素

1.2 二分查找的两种写法

第一种:定义target在[left,right],即左闭右闭的区间。
for left<=right{
    middle:=left+(right-left)/2
    if nums[middle]==target{
        //
    }else if nums[middle]>target{ // 左闭,nums[middle]不等于target,下次比较左边的时候,应该不比较nums[middle],从middle+1 开始比较
        left=middle+1 
    }else{ // 右闭,nums[middle]不等于target,下次比较右边的时候,从middle-1 开始比较
        right=middle-1
    }
}
第二种:定义target在[left,right),即左闭右开的区间。for left<right{    middle:=left+(right-left)/2    if nums[middle]==target{
        //
    }else if nums[middle]>target{ // 左闭,nums[middle]不等于target,下次比较左边的时候,应该不比较nums[middle],从middle+1 开始比较
        left=middle+1
    }else{ // 右开,nums[middle]不等于target,下次比较右边的时候,从middle开始比较
        right=middle 
    }
}

704. 二分查找

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例1

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例2

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

解法

func search(nums []int, target int) int {
    left,right:=0,len(nums)-1
    for left<=right{
        middle:=left+(right-left)/2
        if nums[middle]==target{
            return middle
        }else if nums[middle]>target{
            right=middle-1
        }else{
            left=middle+1
        }
    }
    return -1
}

相关题目

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例1

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例2

输入: nums = [1,3,5,6], target = 2
输出: 1

解法一

func searchInsert(nums []int, target int) int {
    left,right:=0,len(nums)
    for left<right{
        middle:=left+(right-left)/2
        if nums[middle]==target{
            return middle
        }else if nums[middle]>target{
            right=middle
        }else{
            left=middle+1
        }
    }
    return right
}

解法二

func searchInsert(nums []int, target int) int {
    left,right:=0,len(nums)-1
    for left<=right{
        middle:=left+(right-left)/2
        if nums[middle]==target{
            return middle
        }else if nums[middle]>target{
            right=middle-1
        }else{
            left=middle+1
        }
    }
    return left
}

34. 在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

进阶:

你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

示例1

输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

示例2

输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]

解法

func searchRange(nums []int, target int) []int {
    left:=searchLeft(nums,target) // 向左寻找左边界
    right:=searchRight(nums,target) // 向右寻找右边界
    return []int{left,right}
}

func searchLeft(nums []int,target int)int{
    left,right:=0,len(nums)-1
    for left<=right{
        middle:=left+(right-left)/2
        if nums[middle]>=target{ // 向左寻找左边界,因此先定义右边界right
            right=middle-1
        }else{
            left=middle+1
        }
    }
    if left>=len(nums)||nums[left]!=target{
        return -1
    }
    return left // 左寻找,返回左指针
}

func searchRight(nums []int,target int)int{
    left,right:=0,len(nums)-1
    for left<=right{
        middle:=left+(right-left)/2
        if nums[middle]<=target{ // 向右寻找左边界,因此先定义左边界left
            left=middle+1
        }else{
            right=middle-1
        }
    }
    if right<0 || nums[right]!=target{
        return -1
    }
    return right // 右寻找,返回右指针
}

69. x 的平方根

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。

由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。

注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

示例1

输入: x = 4
输出: 2

示例2

输入: x = 8
输出: 2
解释: 8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。

解法

func mySqrt(x int) int {
    left,right:=0,x
    for left<=right{
        middle:=left+(right-left)/2
        if middle*middle==x{ // 求x的算数平方根,即返回某个数的平方等于x
            return middle
        }else if middle*middle>x{
            right=middle-1
        }else{
            left=middle+1
        }
    }
    return right // 最后结束循环的条件:[right,left]。因为向下取整,返回的是left
}

367. 有效的完全平方数

给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。

进阶:不要 使用任何内置的库函数,如  sqrt 。

示例1

输入: num = 16
输出: true

示例2

输入: num = 14
输出: false

解法

func isPerfectSquare(num int) bool {
    left,right:=0,num
    for left<=right{
        middle:=left+(right-left)/2
        if middle*middle==num{ // 判断是否为有效的平方数,相当于判断是否有数的平方等于num
            return true
        }else if middle*middle<num{
            left=middle+1
        }else{
            right=middle-1
        }
    }
    return false
}

2.移除元素

思路

2.1 移除元素的常用原则:

删除数组中的某一个元素:数组的元素在内存中是连续存储的,不能直接删除,只能覆盖

2.2 移除元素的常用方法:

使用双指针的方法:
1. 快慢指针,即同向
2. 左右指针,即双向
移除元素使用的是同向指针,定义快慢指针,使用慢指针覆盖快指针存储的数据

27. 移除元素

给你一个数组nums和一个值val,你需要 原地 移除所有数值等于val的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

示例1

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

示例2

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

解法

func removeElement(nums []int, val int) int {
    index:=0 // 慢指针,用慢指针存储数据,覆盖原来的数据
    for i:=0;i<len(nums);i++{ // 快指针
        if nums[i]!=val{ // 如果右指针的元素和目标元素不等,则赋值给慢指针,若相等,则停止赋值
            nums[index]=nums[i]
            index++
        }
    }
    return index
}

相关题目

26. 删除有序数组中的重复项

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插入 nums 的前 k 个位置后返回 k 。

不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例1

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

示例2

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

解法

func removeDuplicates(nums []int) int {
    if len(nums)==0{
        return 0
    }
    index:=1 // 慢指针,存储返回的数据
    for i:=1;i<len(nums);i++{ // 快指针
        if nums[i]!=nums[i-1]{ // 如果当前元素和上一个元素不相等,则赋值给慢指针,反之,则停止赋值
            nums[index]=nums[i]
            index++
        }
    }
    return index
}

283. 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例1

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

示例2

输入: nums = [0]
输出: [0]

解法一

func moveZeroes(nums []int)  {
    left,right:=0,0 // 快慢指针,left指针左边是非0,left指针和right指针之间都是0
    for right<len(nums){
        if nums[right]!=0{ 
            nums[left],nums[right]=nums[right],nums[left] // left指向0,right指向非0,则交换
            left++
        }
        right++
    }
}

解法二

func moveZeroes(nums []int)  {
    index:=0 // 慢指针
    for i:=0;i<len(nums);i++{ // 快指针
        if nums[i]!=0{
            nums[index]=nums[i] // 慢指针存储非0的数据
            index++
        }
    }
    for i:=index;i<len(nums);i++{ // 补充慢指针后面的元素0
        nums[i]=0
    }
}

844. 比较含退格的字符串

给定 s 和 t 两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true 。# 代表退格字符。

注意:如果对空文本输入退格字符,文本继续为空。

示例1

输入: s = "ab#c", t = "ad#c"
输出: true
解释: s 和 t 都会变成 "ac"

示例2

输入: s = "ab##", t = "c#d#"
输出: true
解释: s 和 t 都会变成 ""

解法

func backspaceCompare(s string, t string) bool {
    sres:=backSpace(s)
    tres:=backSpace(t)
    return sres==tres
}

func backSpace(str string)string{
    temp:=[]byte{}
    for i:=0;i<len(str);i++{
        if str[i]!='#'{
            temp=append(temp,str[i])
        }else if len(temp)>0{
            temp=temp[:len(temp)-1]
        }
    }
    return string(temp)
}

3.有序数组的平方

思路

3.1 有序数据的平方常用方法:

使用双指针的方法:
1. 快慢指针,即同向
2. 左右指针,即双向
有序数组的平方使用的是双向指针,定义左右指针,左右指针双向逼近

977. 有序数组的平方

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例1

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例2

输入: nums = [-7,-3,2,3,11]
输出: [4,9,9,49,121]

解法

func sortedSquares(nums []int) []int {
    left,right:=0,len(nums)-1 // 因为平方的最大值在数组的两端,因此定义左右指针
    temp:=make([]int,len(nums))
    index:=len(nums)-1 // 定义指针,存放当前最大元素
    for left<=right{
        if nums[left]*nums[left]<nums[right]*nums[right]{
            temp[index]=nums[right]*nums[right]
            index--
            right--
        }else {
            temp[index]=nums[left]*nums[left]
            index--
            left++
        }
    }
    return temp
}

4.长度最小的数组

思路

4.1 滑动窗口的定义:

定义两个同向的快慢指针,维护滑动窗口。
滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。

4.2 滑动窗口的模板:

for right<len(nums){
    sum=sum+nums[right] // 扩容滑动窗口
    for 滑动窗口条件{ // 定义窗口
        sum=sum-nums[left] // 不断的调节子序列的起始位置left
        left++
    }
    right++ // 扩容滑动窗口,调节right
}

209. 长度最小的子数组

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例1

输入: target = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的子数组。

示例2

输入: target = 4, nums = [1,4,4]
输出: 1

解法

func minSubArrayLen(target int, nums []int) int {
    left,right:=0,0 // 快慢指针
    sum:=0 
    minValue:=math.MaxInt64 // 存储最小值
    for right<len(nums){ // 右指针范围
        sum=sum+nums[right] // 扩张滑动窗口,即增加右边元素
        for left<len(nums) && sum>=target{ // 窗口就是满足其和≥target的长度最小的 连续 子数组;注意这里使用for,每次更新left(起始位置),并不断比较子序列是否符合条件
            minValue=min(minValue,right-left+1) // 取子序列的长度
            sum=sum-nums[left] // 左指针右移,缩小滑动窗口
            left++ 
        }
        right++
    }
    if minValue==math.MaxInt64{
        return 0
    }
    return minValue
}

func min(a,b int)int{
    if a<b{
        return a
    }
    return b
}

相关题目

904. 水果成篮

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

你只有两个篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。 给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

示例1

输入: fruits = [1,2,1]
输出: 3
解释: 可以采摘全部 3 棵树。

示例2

输入: fruits = [0,1,2,2]
输出: 3
解释: 可以采摘 [1,2,2] 这三棵树。
如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。

解法

func totalFruit(fruits []int) int {
    fruitsMap:=make(map[int]int)
    left:=0
    maxValue:=math.MinInt64
    for i:=0;i<len(fruits);i++{
        fruitsMap[fruits[i]]++
        for len(fruitsMap)>=3{
            fruitsMap[fruits[left]]=fruitsMap[fruits[left]]-1
            if fruitsMap[fruits[left]]==0{
                delete(fruitsMap,fruits[left])
            }
            left++
        }
        maxValue=max(maxValue,i-left+1)
    }
    return maxValue
}

func max(a,b int)int{
    if a>b{
        return a
    }
    return b
}

76. 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例1

输入: s = "ADOBECODEBANC", t = "ABC"
输出: "BANC"

示例2

输入: s = "a", t = "a"
输出: "a"

解法

func minWindow(s string, t string) string {
    tmap:=make(map[byte]int)
    for i:=0;i<len(t);i++{
        tmap[t[i]]++
    }
    smap:=make(map[byte]int)
    minValue:=math.MaxInt64
    res:=""
    for i,j:=0,0;j<=len(s)-1;j++{
        smap[s[j]]++
        for compareMap(smap,tmap){ // 滑动窗口的指,s子序列和t序列中元素的个数一样
            if j-i+1<minValue{
                minValue=j-i+1
                res=s[i:j+1]
            }
            smap[s[i]]=smap[s[i]]-1 // 缩小
            i++
        }
    }
    return res
}

func compareMap(s map[byte]int,t map[byte]int)bool{
    for k,v:=range t{
        if _,ok:=s[k];!ok || v>s[k]{
            return false
        }
    }
    return true
}

5.螺旋矩阵

思路

定义多个指针模拟遍历

59. 螺旋矩阵 II

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

示例1

输入: n = 3
输出: [[1,2,3],[8,9,4],[7,6,5]]

示例2

输入: n = 1
输出: [[1]]

解法

func generateMatrix(n int) [][]int {
    left,right:=0,n-1
    up,down:=0,n-1
    matrix:=make([][]int,n)
    for i:=0;i<n;i++{
        matrix[i]=make([]int,n)
    }
    index:=1
    for left<=right && up<=down{
        for i:=left;i<=right;i++{
            matrix[up][i]=index
            index++
        }
        up++
        for i:=up;i<=down;i++{
            matrix[i][right]=index
            index++
        }
        right--
        for i:=right;i>=left;i--{
            matrix[down][i]=index
            index++
        }
        down--
        for i:=down;i>=up;i--{
            matrix[i][left]=index
            index++
        }
        left++
    }
    return matrix
}

相关题目

54. 螺旋矩阵

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

示例1

输入: matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出: [1,2,3,6,9,8,7,4,5]

示例2

输入: matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
输出: [1,2,3,4,8,12,11,10,9,5,6,7]

解法

func spiralOrder(matrix [][]int) []int {
    res:=[]int{}
    left,right:=0,len(matrix[0])-1
    up,down:=0,len(matrix)-1
    for left<=right && up<=down{
        for i:=left;i<=right;i++{
            res=append(res,matrix[up][i])
        }
        up++
        for i:=up;i<=down;i++{
            res=append(res,matrix[i][right])
        }
        right--
        if left>right || up>down{ // 因为长宽不等的矩阵,因此需要多做个判断,因为有一种情况:left和right还没达到临界,但是up和down已经临界,不跳出,可能会导致重复遍历
            break
        }
        for i:=right;i>=left;i--{
            res=append(res,matrix[down][i])
        }
        down--
        for i:=down;i>=up;i--{
            res=append(res,matrix[i][left])
        }
        left++
    }
    return res
}