二分查找
leetcode 704.二分查找
前提条件: 数组有序,数组中无重复元素
二分区间定义: 左闭右闭 [left, right] or 左闭右开: [left, right)
左闭右闭:while (left <= right), if (nums[middle] > target) right 要赋值为 middle - 1。
如图 target : 11
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
middle = (left + right) // 2
if nums[middle] < target:
left = middle + 1
elif nums[middle] > target:
right = middle - 1
else:
return middle
return -1
左闭右开:while (left < right), if (nums[middle] > target) right 更新为 middle。
如图 target : 11
class Solution:
def search(self, nums: List[int], target: int) -> int:
left,right =0, len(nums)
while left < right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid+1
elif nums[mid] > target:
right = mid
else:
return mid
return -1
总之right 应该为mid 能到达的值,第一个right也一样。
类似题目
35. 搜索插入位置
假设没有重复
循环之后所有right, mid 在比未找到点小的点上, left在要插入的点上。如下(女友画的 (^ ▽ ^)):
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
middle = (left + right) // 2
if nums[middle] < target:
left = middle + 1
elif nums[middle] > target:
right = middle - 1
else:
return middle
return right + 1
- 情况一: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}
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def getRightBorder(nums:List[int], target:int) -> int:
left, right = 0, len(nums)-1
rightBoder = -2 # 记录一下rightBorder没有被赋值的情况
while left <= right:
middle = left + (right-left) // 2
if nums[middle] > target:
right = middle - 1
else: # 找到最右边的为target的坐标,但是会向右超出一个
left = middle + 1
rightBoder = left
return rightBoder # 多加了 1
def getLeftBorder(nums:List[int], target:int) -> int:
left, right = 0, len(nums)-1
leftBoder = -2 # 记录一下leftBorder没有被赋值的情况
while left <= right:
middle = left + (right-left) // 2
if nums[middle] >= target: # 找到最左边的target,但是会多往左边一个
right = middle - 1;
leftBoder = right;
else:
left = middle + 1
return leftBoder # 多减了 1
leftBoder = getLeftBorder(nums, target)
rightBoder = getRightBorder(nums, target)
# 情况一
if leftBoder == -2 or rightBoder == -2: return [-1, -1]
# 情况三
if rightBoder -leftBoder >1: return [leftBoder + 1, rightBoder - 1]
# 情况二
return [-1, -1]
class Solution:
def mySqrt(self, x: int) -> int:
low, high, ans = 0, x, -1
while low <= high:
mid = (low + high) // 2
if mid * mid <= x:
ans = mid
low = mid + 1
else:
high = mid - 1
return ans
class Solution:
def isPerfectSquare(self, num: int) -> bool:
left, right = 0, num
while left <= right:
mid = (left + right) // 2
if mid * mid < num:
left = mid + 1
elif mid * mid > num:
right = mid - 1
else:
return True
return False
移除元素
用双指针法:让a表示一个index, 让b表示第一个index,让a一直往后移动,遍历整个数组,当且仅当我们发现 nums[a] != val 的时候,我们把这个数拷贝到 b 指向的位置,默认 b 是从 0 开始的,然后 b += 1 指向下一个位置。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
a, b = 0, 0
while a <= len(nums) - 1:
if nums[a] != val:
nums[b] = nums[a]
b += 1
a += 1
return b
26. 删除有序数组中的重复项
这里要注意 a + 1 的顺序,不能放到最后
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
a, b = 0, 0
nums[b] = nums[a]
while a < len(nums) - 1:
a += 1
if nums[b] != nums[a]:
b = b + 1
nums[b ] = nums[a]
return b + 1
283. 移动零
b最后在第一个0上,a溢出了一位,所以最后a和b的距离刚好就是0的个数。
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
a, b = 0, 0
while a <= len(nums) - 1:
if nums[a] != 0:
nums[b] = nums[a]
b += 1
a += 1
nums[b:] = [0 for _ in range(a - b)]
844. 比较退格的字符串
这里存在多 ‘ # ’ 的情况,如果没有字符可以退了,就让b留在-1的点上。
def backspaceCompare(self, s, t):
def printStr(strs):
a, b = 0, 0
str_li = list(strs)
while a < len(str_li):
str_li[b] = str_li[a]
if str_li[a] == '#':
b -= 2
if b <= -1:
b = -1
a += 1
b += 1
return str_li[:b]
str_1 = printStr(s)
str_2 = printStr(t)
if str_1 == str_2:
return True
else:
return False
有序数组的平方
class Solution:
def sortedSquares(self, nums):
i, j, k = 0, len(nums) - 1, len(nums) - 1
res = [0 for i in range(len(nums))]
while i <= j:
if nums[i] ** 2 > nums[j] ** 2:
res[k] = nums[i] ** 2
i += 1
k -= 1
else:
res[k] = nums[j] ** 2
j -= 1
k -= 1
return res
长度最小的子数组
使用滑动窗口:窗口即满足其和长度 >= s 的长度的连续子数组。
这里是 num_sum >= target 而不是 num_sum > target 不加=会错过最小窗口
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
length = float('inf')
num_sum = 0
left = 0
for right in range(len(nums)):
num_sum += nums[right]
while num_sum >= target:
length = min(length, right - left + 1)
num_sum -= nums[left]
left += 1
return 0 if length == float('inf') else length
904.水果成篮
这里的counter是一个字典
class Solution:
def totalFruit(self, fruits: List[int]) -> int:
left, length = 0, 0
counter = collections.Counter()
for j, typ in enumerate(fruits):
counter[typ] += 1
while len(counter) > 2:
counter[fruits[left]] -= 1
if counter[fruits[left]] == 0:
del counter[fruits[left]]
left += 1
length = max(length, j - left + 1)
return length
76. 最小覆盖子串
核心思想:滑动窗口
- 首先创建一个初始数量都为0的空字典,然后将t中每种元素需要的数量写入,并用count记录 t 的总元素个数
- 遍历s,每过一个元素,字典中的元素减1,一旦遇见t中的元素,字典中该元素减1,count减一
- 当count等于0,left找到s中第一个存在于t的元素,计算left和right的距离
- left离开当前元素(该元素存在于t中),count加一,字典中该元素加一,right向右寻找该元素(其中当窗口中含有某种元素多于t所需时,该元素在字典中会变为负数,left向左寻找下一个t中元素时可以跳过该元素,但是当窗口划出该元素需要将该元素在字典中重新加上)
class Solution:
def minWindow_1(self, s, t):
left = 0
length = len(s) + 1
dict_t = collections.defaultdict(int)
for val in t:
dict_t[val] += 1
count = len(t)
for right, val in enumerate(s):
if dict_t[val] > 0:
count -= 1
dict_t[val] -= 1 # 如果有多余的t需要的元素,该元素会变成负的,在寻找下一个left的时候可以直接跳过
if count == 0:
while True:
if dict_t[s[left]] == 0:
break
dict_t[s[left]] += 1 # left 在左移的过程中需要将移出去的t需要的元素重新加上
left += 1
if length > right - left + 1:
length = right - left + 1
min_l = left
min_r = right + 1
dict_t[s[left]] += 1
count += 1
left += 1
return '' if length == len(s) + 1 else s[min_l: min_r]
螺旋矩阵
坚持每条边左闭右开
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
matrix = [[0] * n for _ in range(n)]
left, right, up, down = 0, n - 1, 0, n - 1
number = 1 # 要填充的数字
while left < right and up < down:
# 从左至右填充
for i in range(left, right):
matrix[up][i] = number
number += 1
# 从上至下填充
for j in range(up, down):
matrix[j][right] = number
number += 1
# 从右至左填充
for i in range(right, left, -1):
matrix[down][i] = number
number += 1
# 从下至上填充
for j in range(down, up, -1):
matrix[j][left] = number
number += 1
left += 1
right -= 1
up += 1
down -= 1
# 如果为基数,填充中间,序号从零开始
if n % 2:
matrix[n // 2][n // 2] = number
return matrix
感谢阅读