LeetCode 704.二分查找
题目链接:704.二分查找
题目讲解:代码随想录
题目描述:给定一个
n
个元素有序的(升序)整型数组nums
和一个目标值target
,写一个函数搜索nums
中的target
,如果目标值存在返回下标,否则返回-1
。
分析:这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,可以考虑使用二分法。
二分法的第一种写法(左闭右闭)
- while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
//时间复杂度:O(log n)
//空间复杂度:O(1)
func search(nums []int, target int) int {
// 初始化左右边界
left := 0
right := len(nums) - 1
// 循环逐步缩小区间范围
for left <= right{
// 求区间中点
middle := left + (right - left) / 2
// 根据 nums[mid] 和 target 的大小关系
// 调整区间范围
if nums[middle] > target{
right = middle - 1
}else if nums[middle] < target {
left = middle + 1
}else{
return middle
}
}
// 在输入数组内没有找到值等于 target 的元素
return -1
}
二分法的第二种写法(左闭右开)
- while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
- if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
//时间复杂度:O(log n)
//空间复杂度:O(1)
func search(nums []int, target int) int {
left := 0
right := len(nums)
for left < right {
middle := left + (right - left) / 2
if nums[middle] > target{
right = middle
}else if nums[middle] < target{
left = middle + 1
}else{
return middle
}
}
return -1
}
LeetCode 35.搜索插入位置
题目链接:35.搜索插入位置
题目讲解:代码随想录
题目描述:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
使用二分法(左闭右开)
搜索插入位置,通过找规律,发现在left <= right的循环条件下,除了能直接在数组中找到对应的target,其余情况,都是在跳出left <= right的循环条件,即left > right时才满足条件。
func searchInsert(nums []int, target int) int {
left := 0
right := len(nums) - 1
for left <= right{
mid := left + (right - left) / 2
if nums[mid] > target{
right = mid - 1
}else if nums[mid] < target{
left = mid + 1
}else{ // 恰好能找到对应的元素
return mid
}
}
return left
}
LeetCode 34.在排序数组中查找元素的第一个和最后一个位置
题目讲解:代码随想录
题目描述:给你一个按照非递减顺序排列的整数数组
nums
,和一个目标值target
。请你找出给定目标值在数组中的开始位置和结束位置
寻找target在数组里的左右边界,有如下三种情况:
- 情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
- 情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
- 情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}
自己的理解就是:利用二分查找,分别去找target(可能有多个)位置的前一个位置和后一个位置,不包括target元素本身(即找到边界)。然后统一处理以上三种情况。
// 时间复杂度O(log n)
func searchRange(nums []int, target int) []int {
leftBorder := getLeft(nums, target)
rightBorder := getRight(nums, target)
// 情况一,target 在数组范围的右边或者左边
if leftBorder == -2 || rightBorder == -2 {
return []int{-1, -1}
}
// 情况三,target 在数组范围中,且数组中存在target
if rightBorder - leftBorder > 1 {
return []int{leftBorder + 1, rightBorder - 1}
}
// 情况二,target 在数组范围中,且数组中不存在target
return []int{-1, -1}
}
// 获得左边界的位置(不包括target)
func getLeft(nums []int, target int) int {
left, right := 0, len(nums)-1
border := -2 // 记录border没有被赋值的情况
for left <= right { // []闭区间
mid := left + ((right - left) >> 1)
if nums[mid] > target {
right = mid - 1
} else if nums[mid] < target{
left = mid + 1
}else{ // 找到等于target的位置,后续逐步更新,标记第一个target的前一个位置
right = mid - 1
border = right
}
}
return border
}
// 获取右边界的位置(不包括target)
func getRight(nums []int, target int) int {
left, right := 0, len(nums) - 1
border := -2
for left <= right {
mid := left + ((right - left) >> 1)
if nums[mid] > target {
right = mid - 1
} else if nums[mid] < target{
left = mid + 1
}else { // 找到等于target的位置,后续逐步更新,标记最后一个target的后一个位置
left = mid + 1
border = left
}
}
return border
}
LeetCode 27.移除元素
题目链接:27.移除元素
题目讲解:代码随想录
题目描述:给你一个数组
nums
和一个值val
,你需要 原地 移除所有数值等于val
的元素。元素的顺序可能发生改变。然后返回nums
中与val
不同的元素的数量。
使用快慢指针实现
双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
定义快慢指针
- 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组(fast指针对应数组为target值时,跳过该值的相应操作,这样fast指针会比slow指针多走一步)
- 慢指针:指向更新新数组下标的位置
// 时间复杂度 O(n)
// 空间复杂度 O(1)
func removeElement(nums []int, val int) int {
slow := 0
for fast := 0; fast < len(nums); fast++{
if nums[fast] == val{
continue
}
nums[slow] = nums[fast]
slow++
}
return slow
}
LeetCode 977.有序数组的平方
题目链接:997.有序数组的平方
题目讲解:代码随想录
题目描述:给你一个按 非递减顺序 排序的整数数组
nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
数组其实是有序的, 只不过负数平方之后可能成为最大数了。
那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
此时可以考虑双指针法了
- i指向起始位置,j指向终止位置。
- 新定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。
- 比较起始位置和最终位置元素平方的大小,较大的放入新数组中。
//时间复杂度为O(n)
func sortedSquares(nums []int) []int {
n := len(nums)
i, j, k := 0, n-1, n-1
ans := make([]int, n)
for i <= j{
lm, rm := nums[i]*nums[i], nums[j]*nums[j]
if lm > rm{
ans[k] = lm
i++
}else{
ans[k] = rm
j--
}
k--
}
return ans
}