数组相关知识
数组下标都是从0开始的。
数组内存空间的地址是连续的
因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
go声明二维数组时,系统会开辟一片连续的内存空间,依次存储所有的元素
一:二分查找
力扣704https://leetcode.cn/problems/binary-search/
之前学习二分查找的时候基本上靠肌肉记忆凭感觉,有两个小的疑惑点基本上就是碰运气。这次系统学习了这两部分的写法和原理,并对相关题目进行了扩展
二分法在写的过程中会出现的两个疑惑点:边界条件的判断
1.for循环时lefrt<=right 还是 left<right
2.mid取值时,是mid还是mid+1/mid-1
为明确解决这两个问题考虑两种区间的定义方法:
1.左闭右闭 [A,B]
在这种情况下,for lefrt<=right符合区间定义,例如[1,1]
此时lfet=0,right=len(nums)-1
(1)当nums[mid]<target时,例如,target=10。下一步要搜索的区间包含mid,且mid不符合题目要求。所以下次选择时,mid被排除,left=mid+1
(2)当nums[mid]>target时,例如,target=8。下一步要搜索的区间包含mid,且mid不符合题目要求。所以下次选择时,mid被排除,right=mid-1
(3)当nums[mid]=target时,返回mid的值
2.左闭右开 [A,B)
在这种情况下,for lefrt<right符合区间定义,例如[1,2),此时两数相等已经不符合定义
此时lfet=0,right=len(nums)要包含到数组的最后一个值
(1)当nums[mid]<target时,例如,target=10。往右移动,下一步要搜索的区间包含mid。所以下次选择时,mid被排除,left=mid+1
(2)当nums[mid]>target时,例如,target=8。下一步要搜索的区间不包含mid,所以下次选择时,right=mid,但此时由于左闭右开mid不被包含
(3)当nums[mid]=target时,返回mid的值
变形问题:(有重复元素)
第一个的问题都向前看
1.查找第一个等于目标值的下标 重点是看mid前一个元素是否==target
在已知nums[mid]=target的情况下
(1)如果nums[mid-1]=target,说明还要继续向前搜寻
(2)如果nums[mid-1]!=target,则找到了第一个等于的元素
//时间复杂度O(logn)
//空间复杂度O(1)
func contains(nums []int,target int){
if nums==nil || len(nums)==0{
return -1
}
left:=0
right:=len(nums)-1
for left<=right{
mid:=(right-left)/2+left
if target==nums[mid]{
//符合下面的两个条件之一就返回mid
//1.mid是数组的第一个元素
//2.mid前面的元素不等于target
if mid==0 || target!=nums[mid-1]{
return mid
}else{
right=mid-1
}
}else if target<nums[mid]{
right=mid-1
}else{
left=mid+1
}
}
return -1
}
2.查找第一个大于等于目标值的下标
在nums[mid]>=target的情况下
(1)如果nums[mid-1]>=target,说明还要继续向前搜寻
(2)如果nums[mid-1]<target,则找到了第一个等于的元素
func search(nums []int, target int) int {
if nums==nil || len(nums)==0{
return -1
}
left:=0
right:=len(nums)-1
for left<=right{
mid:=(right-left)/2+left
if target<=nums[mid]{
//符合下面的两个条件之一就返回mid
//1.mid是数组的第一个元素
//2.mid前面的元素小于target
if mid==0 || nums[mid-1]<target{
return mid
}else{
right=mid-1
}
}else{
left=mid+1
}
}
return -1
}
最后一个往右边看,后看
3.查找最后一个等于目标值的下标
在已知nums[mid]=target的情况下
(1)如果nums[mid+1]=target,说明还要继续向后搜寻
(2)如果nums[mid+1]!=target,则找到了最后一个等于的元素
func search(nums []int, target int) int {
if nums==nil || len(nums)==0{
return -1
}
left:=0
right:=len(nums)-1
for left<=right{
mid:=(right-left)/2+left
if target==nums[mid]{
//符合下面的两个条件之一就返回mid
//1.mid是数组的最后一个元素
//2.mid后面的元素!=target
if mid==len(nums)-1 || nums[mid+1]!=target{
return mid
}else{
left=mid+1
}
}else if target<nums[mid]{
right=mid-1
}else{
left=mid+1
}
}
return -1
}
4.查找最后一个小于等于目标值的下标
在nums[mid]<=target的情况下
(1)如果nums[mid+1]<=target,说明还要继续向后搜寻
(2)如果nums[mid+1]>target,则找到了最后一个大于等于的元素
func search(nums []int, target int) int {
if nums==nil || len(nums)==0{
return -1
}
left:=0
right:=len(nums)-1
for left<=right{
mid:=(right-left)/2+left
if target>=nums[mid]{
//符合下面的两个条件之一就返回mid
//1.mid是数组的第一个元素
//2.mid前面的元素小于target
if mid==len(nums)-1 || nums[mid+1]>target{
return mid
}else{
left=mid+1
}
}else{
right=mid-1
}
}
return -1
}
相关题目34 35 69是剑指和top100的题目
简单 lc 704 号算法题:二分查找
中等 lc 34 :排序数组中找元素的第一个和最后一个位置【top100】
简单 lc 35 & 剑指 53-1:搜索插入位置【top100】
简单 lc 69 & 剑指 072 :x 的平方根
二、移动元素
方法一:快慢指针
func removeElement(nums []int, val int) int {
if len(nums)==0{
return 0
}
slow,fast:=0,0
for fast<len(nums){
if nums[fast]!=val{
nums[slow]=nums[fast]
slow++
}
fast++
}
return slow
}
方法二:对撞指针
进阶:如果要删除的元素特别少,且是前面的
需要对整个数组的元素重新赋值,很费时
如果left对应的值是需要删除的,就把right的值赋给left,right–
如果不需要删除,left++
直到left>right
func removeElement(nums []int, val int) int {
if len(nums)==0{
return 0
}
left,right:=0,len(nums)-1
for right>=left{
if nums[left]==val{
nums[left]=nums[right]
right--
}else{
left++
}
}
return right+1
}