二分查找
704.二分查找
题目描述: 704.二分查找.
解法
左闭右闭的二分查找
class Solution(object):
def search(self, nums, target):
l, r = 0, len(nums)-1
while l <= r: #注意
m = l + (r-l) // 2
if nums[m] == target:
return m
if nums[m] < target:
l = m + 1
else:
r = m - 1 #注意
return -1
左闭右开的二分查找
class Solution(object):
def search(self, nums, target):
l, r = 0, len(nums)
while l < r: #注意
m = l + (r-l)//2
if nums[m] == target:
return m
elif nums[m] < target:
l = m + 1
else:
r = m #注意
return -1
思路
二分查找的方法相对来说比较简单,但是要注意的是查找的区间是左闭右闭还是左闭右开
如果是左闭右闭的区间,那么这个区间的左右两个端点都可以取到,因此left一定小于等于right,在更新端点值时,由于可以取到右端点,所以right应当等于mid-1(因为mid是已经被排除掉的点,所以right不应再取这个点)。
如果是左闭右开的区间,那么就是右端点无法被取到,因此left一定小于right,在更新端点值时,因为mid是被排除掉的值,那么 right应当等于mid(同理mid是被排除掉的点,所以right可以直接等于mid)。
二分查找的相关题目
35.搜索插入位置
题目描述: 35.搜索插入位置.
解法
左闭右闭的二分查找
class Solution(object):
def searchInsert(self, nums, target):
l, r = 0, len(nums)-1
while l <= r:
m = l + (r-l)//2
if nums[m] == target:
return m
elif nums[m] < target:
l = m + 1
else:
r = m - 1
return l # r + 1
如果没有找到这个元素的位置,需要将其插入到列表,最后一次循环就一定是left等于right,如果此时mid小于target,target插入的位置就是mid+1的位置,left此后要**+1而right不变,如果此时mid对应的值大于target**,target插入的位置就应该是当前mid位置,right此后要**-1**,所以最终的输出应当是left或者是right+1
左闭右开的二分查找
class Solution(object):
def searchInsert(self, nums, target):
l, r = 0, len(nums)
while l < r:
m = l + (r-l)//2
if nums[m] == target:
return m
elif nums[m] < target:
l = m + 1
else:
r = m
return l # r
没有找到这个元素的最后一次循环时,right一定比left大1,因此此时的mid指向的是left。如果mid小于target,target插入的位置就是mid+1,接下来left+1而right不变。如果mid大于target,target插入的位置就是mid所在的位置,此后left不变,right-1。最终的输出就应该是left或者是right
34.在排序数组中查找元素的第一个和最后一个位置
题目描述: 34.在排序数组中查找元素的第一个和最后一个位置.
解法
class Solution(object):
def searchRange(self, nums, target):
def findRightBoard(nums,target): # 寻找右边界
rightboard = -2
left, right = 0, len(nums)-1
while left <= right:
mid = left + (right-left) // 2
if nums[mid] <= target:
left = mid + 1
rightboard = left
else:
right = mid - 1
return rightboard
def findLeftBoard(nums, target): # 寻找左边界
leftboard = -2
left,right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) //2
if nums[mid] >= target:
right = mid - 1
leftboard = right
else:
left = mid + 1
return leftboard
leftboard = findLeftBoard(nums,target)
rightboard = findRightBoard(nums,target)
if leftboard == -2 or rightboard == -2: # 如果没有左右边界,意味着在数字在数组的左或者右侧
return [-1,-1]
if rightboard - leftboard > 1: # 找到边界且中间有内容
return [leftboard + 1, rightboard - 1]
return [-1,-1] # 有边界但是中间没内容
这道题稍微有点难度,还没有考虑只用一个二分法的方法。首先一定要找到边界,左边界是最后一个小于target的数字位置,右边界是第一个大于target的数字位置,如果没有左边界就意味着所有数字都大于target(此时有右边界),如果没有右边界则意味着所有数字都小于target(此时有左边界)。
因此可以用两个二分查找分别查找左右边界,如果既有左边界又有右边界,那么就要看这两个边界之间是否有内容了,有内容则意味着target在其中,没有内容的话就要输出[-1,-1]。
69.x的平方根
题目描述: 69.x的平方根.
解法
class Solution(object):
def mySqrt(self, x):
if x == 1:
return 1
left, right = 0,x//2
res = 0
while left <= right:
mid = left + (right - left)//2
if mid ** 2 <= x:
res = mid
left = mid + 1
else:
right = mid - 1
return res
比较简单,没啥可说的……
367.有效的完全平方数
题目描述: 367.有效的完全平方数.
解法
class Solution(object):
def isPerfectSquare(self, num):
if num == 1:
return True
left,right = 0, num//2
while left <= right:
mid = left + (right - left) // 2
if mid ** 2 == num:
return True
elif mid ** 2 <num:
left = mid + 1
else:
right = mid - 1
return False
时间上有点慢,不晓得为什么
双指针
27.移除元素
题目描述: 27.移除元素.
解法
在这里插入代码片
class Solution(object):
def removeElement(self, nums, val):
slow=0
for fast in range(len(nums)):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
return slow
思路
简单的快慢指针,快的指针便利整个数组,通过条件筛选处数值赋给慢指针,慢指针如果内容了就需要向下走一个,那么慢指针的index就是更新后的数组长度。
双指针的相关题目
26.删除有序数组中的重复项
题目描述: 26.删除有序数组中的重复项.
解法
class Solution(object):
def removeDuplicates(self, nums):
left = 0
for right in range(len(nums)):
if nums[left] != nums[right]:
left += 1
nums[left] = nums[right]
return left + 1
没什么难度,但是要看左指针如何移动,什么时候移动
283.移动零
题目描述: 283.移动零.
解法
class Solution(object):
def moveZeroes(self, nums):
left = 0
for right in range(len(nums)):
if nums[right] != 0:
tmp = nums[right]
nums[right] = nums[left]
nums[left] = tmp
left += 1
注意一下要交换位置就好。