704 二分查找
题目链接:
https://leetcode.cn/problems/binary-search/
文章讲解:
https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html
视频讲解:
https://www.bilibili.com/video/BV1fA4y1o715
-
注意事项
1,使用二分查找,数组必须是有序的。此题已经排序,否则需要自己进行排序。
2,题目已经假设,数组中的数都是不重复的,因为如果重复,结果就不唯一了。
3,对区间的定义不同,有两种写法,一种是左闭右闭,一种是左闭右开(其他两种不太常用,硬写也行,没有必要而已)。个人比较习惯左闭右闭的写法。
4,区间的定义可以认为是一种模式,一旦选择了某种模式,后面循环条件、变量初始化、变量更新等,就不要违反它。 -
第一种写法,左闭右闭:
class Solution(object):
def search(self, nums, target):
n = len(nums)
if n == 0:
return -1
# 从数组的两端向中间查找
left = 0
right = n-1 # 因为右闭,所以right=n-1,如果right=n,就会发生越界的错误
while(left <= right):
# 用<=而不是<的原因是,由于左闭右闭,当left=right的时候,区间仍是有效的
# 如果使用<号,就会跳过中间那一个数,导致结果不对
mid = (left + right) // 2
if (nums[mid] > target):
# mid已经不是要找的数,所以right要更新为mid的前一个数,由于右闭,取mid-1
right = mid - 1
elif (nums[mid] < target):
# 同理,mid已经不是要找的数,所以left要更新为mid的下一个数,由于左闭,取mid+1
left = mid + 1
else:
return mid
return -1
- 第二种写法,左闭右开:
class Solution(object):
def search(self, nums, target):
n = len(nums)
if n == 0:
return -1
# 从数组的两端向中间查找
left = 0
right = n # 因为右开,所以right=n,实际上right取不到n,所以不会有越界错误
while(left < right):
# 用<的原因是,由于左闭右开,当left=right的时候,区间已经无效了,说明此时循环应该结束
mid = (left + right) // 2
if (nums[mid] > target):
# mid已经不是要找的数,所以right要更新为mid的前一个数,由于右开,取right=mid
right = mid
elif (nums[mid] < target):
# 同理,mid已经不是要找的数,所以left要更新为mid的下一个数,由于左闭,取mid+1
left = mid + 1
else:
return mid
return -1
- 总结:
对区间的定义不同,影响的有三个地方:(1)指针的初始化;(2)循环结束的条件;(3)指针的更新方式。
27 移除元素
题目链接:
https://leetcode.cn/problems/remove-element/
文章讲解:
https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html
视频讲解:
https://www.bilibili.com/video/BV12A4y1Z7LP
- 注意事项
1,数组在内存空间的地址是连续的,删除其实等于覆盖。
2,如果每次找到一个元素,都把后面所有元素整体移动一轮,那么时间复杂度是O(n*n)
3,如果使用快慢指针,只需要遍历一次,就可以把所需要的数字都写到对应的位置,最终返回新数组的长度即可。
class Solution(object):
def removeElement(self, nums, val):
n = len(nums)
slow = 0 # 快慢指针都指向第一个元素,这样就不用额外处理第一个元素是否为val的情况
fast = 0
while(fast < n):# 当快指针遍历到数组的末尾,循环结束
if nums[fast] != val: # 注意这里是移除元素,所以不等于val的才是我们要找的值
nums[slow] = nums[fast] # 找到有效的数字,就进行赋值,然后快慢指针都移动
slow += 1
fast += 1
else:
fast += 1 # 否则快指针继续向后寻找
return slow # 最后一次赋值后,slow指针有+1,因此此时slow的数值就是数组的长度
- 总结:
最开始想到的方法是使用left和right指针,初始化在数组的两头,同时向中间收缩,right遇到有效数字,就赋值给left,但是写了一下发现,要处理的边界问题会很多。同向遍历,是最简单和清晰的写法,我劝大家不要为难自己…