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
}