二分算法思想就是设左右指针,慢慢向中间逼近,最后返回中间符合题意的部分。使用二分查找时要格外注意细节,比如while循环中判断语句,mid是否需要加减一。
注意要防止left+right相加过大导致(left+right)/2 溢出,一般可以使用left+(right-left)/2.
搜索一个元素情况---leetcode第704题 704. 二分查找 。
题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
首先这个题区间为[left,right],也就是两端闭。也就是代码停止条件为left==right+1时。代入具体数字[3,2]可以看出,没有数大于3并且小于2,因此是合理的。如果判断条件为,代入具体数字[2,2]可以看出,如果在这种情况下停止代码就会漏掉2。本题中mid加减一主要是因为区间是闭合的,我们已经知道此时mid不是题中想要的解,因此接下来就不需要再进行判断。
class Solution:
def search(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums)-1
mid = left+(right-left)//2
while(left<=right):
if nums[mid]==target:
return mid
elif nums[mid]>target:
right = mid-1
mid = left+(right-left)//2
else:
left = mid+1
mid = left+(right-left)//2
return -1
搜索边界时,口号中如果比较复杂难记,可以改成和第一题中相同模型,记得额外考虑边界。
搜索左边界--第一个错误的版本(278) 搜索插入位置(35结合)
class Solution:
def search(self, nums: List[int], target: int) -> int:
if len(nums)==0:
return -1
left = 0
right = len(nums)-1
mid = left+(right-left)//2
while(left<=right):
if nums[mid]==target:
right = mid-1 # 向左收缩
elif nums[mid]>target:
right = mid-1
else:
left = mid+1
mid = left+(right-left)//2
if left>=len(nums) or nums[left]!=target:
return -1
return left
增加判断条件是因为防止如下图所示超出边界(为啥第一个不用??)
搜索右边界
class Solution:
def search(self, nums: List[int], target: int) -> int:
if len(nums)==0:
return -1
left = 0
right = len(nums)-1
mid = left+(right-left)//2
while(left<=right):
if nums[mid]==target:
left = mid+1 # 向右收缩
elif nums[mid]>target:
right = mid-1
else:
left = mid+1
mid = left+(right-left)//2
if right<0 or nums[right]!=target:
return -1
return right
设置左右指针
leetcode977--有序数组的平方
题目:给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
n = len(nums)
result = [-1]*n
i,j,pos = 0,n-1,n-1
while(i<=j):
if nums[i]*nums[i]>nums[j]*nums[j]:
result[pos] = nums[i]*nums[i]
i += 1
else:
result[pos] = nums[j]*nums[j]
j -= 1
pos -= 1
return result
这个题的关键在于无论是正数还是负数,最大值的平方一定出现在左右两端,比如:
[1,2,3,4,5]----最大值平方25(右端点)
[-5,-4,-3,-2,-1]----最大值平方25(左端点)
[-5,-4,0,3,2]---最大值平方25(左端点)
因此只需要比较每次左右边界平方大小,将大的放到数组最尾端即可。
leetcode977---移动零
题目:给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
left = right =0
while(right
left指向的是已经处理好的序列的最右端,right指向的是未处理序列的最左端。右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换【也可以直接覆盖,最后用for循环对left及以后的赋值为0】,同时左指针右移。
leetcode167---两数之和 II - 输入有序数组
题目:给你一个下标从 1 开始的整数数组 numbers
,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target
的两个数
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
i = 0
j = len(numbers)-1
while (i
初始时两个指针分别指向第一个元素位置和最后一个元素的位置。每次计算两个指针指向的两个元素之和,并和目标值比较。如果两个元素之和等于目标值,则发现了唯一解。如果两个元素之和小于目标值,则将左侧指针右移一位。如果两个元素之和大于目标值,则将右侧指针左移一位。移动指针之后,重复上述操作,直到找到答案。 这个题也可以用二分法【固定一个值,在剩余区间里找target-固定的值】。
leetcode344--反转字符串
题目:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s
的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
left = 0
right = len(s)-1
while(left
leetcode876--链表的中间结点
题目:给定一个头结点为 head
的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
class Solution:
def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
slow = head
fast = head
while(fast and fast.next):
slow = slow.next
fast = fast.next.next
return slow
经典链表快慢指针,慢指针走一步,快指针走两步。快指针走到最后时,慢指针走到一半。
leetcode19--
题目:给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0, head)
slow = dummy
fast = head
for i in range(n):
fast = fast.next
while(fast):
slow = slow.next
fast = fast.next
slow.next = slow.next.next
return dummy.next
整体思路是让前面的指针先移动n
步,之后前后指针共同移动直到前面的指针到尾部为止。这里注意链表常用技巧,设置哑节点dummy【不用特意考虑头节点】
189. 轮转数组
题目:给你一个数组,将数组中的元素向右轮转 k
个位置,其中 k
是非负数。
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
for i in range(k):
nums.insert(0, nums.pop())
return nums
先把最后面数字弹出来再插入到数组第一个位置
557. 反转字符串中的单词 III
题目:给定一个字符串 s
,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。
class Solution:
def reverseWords(self, s: str) -> str:
words = s.split(" ")
result = ""
for i in words:
result += i[::-1] # 从前到后,步长为-1----从后到前
result += " "
result = result[:-1]
return result
使用split函数先将每个单词分开然后对每个单独单词进行操作。最后一步result = result[:-1]是因为前面循环中多加了一个空格