1、数组理论基础(了解一下数组基础,以及数组的内存空间地址)
文章链接:代码随想录
* (关注)二维数组在内存的空间地址是连续的么?
2、704.二分查找
思路:这是给出一个数组(无重复),然后根据输入的数值找到对应的下标。
首先:简单的暴力遍历:(执行用时: 44 ms)
class Solution:
def search(self, nums: List[int], target: int) -> int:
a = 0
# print(nums)
for i in nums:
if int(target) == int(i):
print(a)
return a
else:
a = a + 1
if a >= len(nums):
# print("-1")
return -1
或者更直接的:(执行用时: 56 ms)
class Solution:
def search(self, nums: List[int], target: int) -> int:
if target in nums :
return nums.index(target)
else:
return -1
今日主角:二分法
二分法第一种写法:左闭右闭即[left, right] (执行用时: 44 ms)
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right: # 当left==right,区间[left, right]依然有效,所以用 <=
middle = left + (right-left) // 2
# 不直接用(Left+right)//2 是因为当left和right都很大的时候,(left + right)可能会导致整数溢出。而使用left + (right - left) // 2的方式可以避免这个问题,因为right - left保证了相减操作不会超出整数的表示范围。
if nums[middle] > target:
right = middle-1 ## target在左区间,所以[left, middle - 1]
elif nums[middle] < target:
left = middle+1 # target在右区间,所以[middle + 1, right]
else:
return middle # 数组中找到目标值,直接返回下标
return -1 # 未找到目标值
二分法第二种写法:左闭右开即[left, right) (执行用时: 40 ms)
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) # 定义target在左闭右开的区间里,即:[left, right)
while left < right:# 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
middle = left + (right-left) // 2
# 不直接用(Left+right)//2 是因为当left和right都很大的时候,(left + right)可能会导致整数溢出。而使用left + (right - left) // 2的方式可以避免这个问题,因为right - left保证了相减操作不会超出整数的表示范围。
if nums[middle] > target:
right = middle # target 在左区间,在[left, middle)中
elif nums[middle] < target:
left = middle+1 # target 在右区间,在[middle + 1, right)中
else:
return middle # 数组中找到目标值,直接返回下标
return -1 # 未找到目标值
3、34.二分查找拓展
首先,简单的暴力遍历:(执行用时: 64 ms)
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
all = []
for i in range(0,len(nums)):
if nums[i] == target:
all.append(i)
if len(all)==0:
return[-1,-1]
else:
return [all[0],all[-1]]
接着,二分法:(用左闭右闭即[left, right] )
# 1、首先,在 nums 数组中二分查找 target;
# 2、如果二分查找失败,则 binarySearch 返回 -1,表明 nums 中没有 target。此时,searchRange 直接返回 {-1, -1};
# 3、如果二分查找成功,则 binarySearch 返回 nums 中值为 target 的一个下标。然后,通过左右滑动指针,来找到符合题意的区间
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def binarySearch(nums:List[int], target:int) -> int:
left, right = 0, len(nums)-1
while left<=right: # 不变量:左闭右闭区间
middle = left + (right-left) // 2
if nums[middle] > target:
right = middle - 1
elif nums[middle] < target:
left = middle + 1
else:
return middle
return -1
index = binarySearch(nums, target)
if index == -1:return [-1, -1] # nums 中不存在 target,直接返回 {-1, -1}
# nums 中存在 targe,则左右滑动指针,来找到符合题意的区间
left, right = index, index
# 向左滑动,找左边界
while left -1 >=0 and nums[left - 1] == target: left -=1
# 向右滑动,找右边界
while right+1 < len(nums) and nums[right + 1] == target: right +=1
return [left, right]
# 1、首先,在 nums 数组中二分查找得到第一个大于等于 target的下标(左边界)与第一个大于target的下标(右边界);
# 2、如果左边界<= 右边界,则返回 [左边界, 右边界]。否则返回[-1, -1]
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def binarySearch(nums:List[int], target:int, lower:bool) -> int:
left, right = 0, len(nums)-1
ans = len(nums)
while left<=right: # 不变量:左闭右闭区间
middle = left + (right-left) //2
# lower为True,执行前半部分,找到第一个大于等于 target的下标 ,否则找到第一个大于target的下标
if nums[middle] > target or (lower and nums[middle] >= target):
right = middle - 1
ans = middle
else:
left = middle + 1
return ans
leftBorder = binarySearch(nums, target, True) # 搜索左边界
rightBorder = binarySearch(nums, target, False) -1 # 搜索右边界
if leftBorder<= rightBorder and rightBorder< len(nums) and nums[leftBorder] == target and nums[rightBorder] == target:
return [leftBorder, rightBorder]
return [-1, -1]
# 1、首先,在 nums 数组中二分查找得到第一个大于等于 target的下标leftBorder;
# 2、在 nums 数组中二分查找得到第一个大于等于 target+1的下标, 减1则得到rightBorder;
# 3、如果开始位置在数组的右边或者不存在target,则返回[-1, -1] 。否则返回[leftBorder, rightBorder]
class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:
def binarySearch(nums:List[int], target:int) -> int:
left, right = 0, len(nums)-1
while left<=right: # 不变量:左闭右闭区间
middle = left + (right-left) //2
if nums[middle] >= target:
right = middle - 1
else:
left = middle + 1
return left # 若存在target,则返回第一个等于target的值
leftBorder = binarySearch(nums, target) # 搜索左边界
rightBorder = binarySearch(nums, target+1) -1 # 搜索右边界
if leftBorder == len(nums) or nums[leftBorder]!= target: # 情况一和情况二
return [-1, -1]
return [leftBorder, rightBorder]
4、35.二分查找拓展(搜索插入位置)
首先,简单的暴力遍历:(执行用时: 52 ms)
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
for i in range(0,len(nums)):
if nums[i] >= target:
return i #一旦发现大于或者等于target的num[i],那么i就是我们要的结果
#目标值在数组所有元素之后的情况
return len(nums) #// 如果target是最大的,或者 nums为空,则返回nums的长度
二分法:(执行用时: 60 ms)
思路:是先判断目标值是否大于列表中所有的值,若大于,则返回列表长度;若不大于,则找出第一个大于等于目标值的下标,若目标值在列表中,则找到了该下表标,若目标值不在列表中,则加入该目标值,返回其下标。
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
# for i in range(0,len(nums)):
# if nums[i] == target:
# return i
def erfenfa(nums: List[int], target: int):
left = 0
right = len(nums)-1
while left <= right:
middle = left + (right-left)//2
if nums[middle] >= target:
right = middle -1
else:
left = middle +1
return left
if target > nums[-1]:
return len(nums)
else:
left1 = erfenfa(nums,target)
if nums[left1] == target:
return left1
else:
# print(left1)
return left1
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
# // 分别处理如下四种情况
# // 目标值在数组所有元素之前 [0,0)
# // 目标值等于数组中某一个元素 return middle
# // 目标值插入数组中的位置 [left, right) ,return right 即可
# // 目标值在数组所有元素之后的情况 [left, right),因为是右开区间,所以 return right
return right + 1
5、27.移除元素
首先:简单的暴力遍历:
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
i, l = 0, len(nums)
while i < l:
if nums[i] == val: # 找到等于目标值的节点
for j in range(i+1, l): # 移除该元素,并将后面元素向前平移
nums[j - 1] = nums[j]
l -= 1
i -= 1
i += 1
return l
双指针:
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 快慢指针
fast = 0 # 快指针
slow = 0 # 慢指针
size = len(nums)
while fast < size: # 不加等于是因为,a = size 时,nums[a] 会越界
# slow 用来收集不等于 val 的值,如果 fast 对应值不等于 val,则把它与 slow 替换
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
# 快慢指针
fast = 0 # 快指针
slow = 0 # 慢指针
size = len(nums)
for fast in range(0,size): # 不加等于是因为,a = size 时,nums[a] 会越界
# slow 用来收集不等于 val 的值,如果 fast 对应值不等于 val,则把它与 slow 替换
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
# fast += 1
return slow
6、26.删除有序数组的重复项
题目链接:力扣
首先:简单的暴力遍历:
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
l = len(nums)
i = 0
while i < l - 1:
if nums[i] == nums[i+1]:
for j in range(i+1, l-1):
nums[j] = nums[j+1]
l -= 1
else:
i += 1
return l
双指针:
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
fast = 1
slow = 0
for fast in range(1,len(nums)):
if nums[slow]!=nums[fast]:
nums[slow+1] = nums[fast]
slow = slow+1
else:
slow=slow
return slow +1