双指针有两种:1)快慢指针:两个指针向同一个方向前进,一快一慢;2)左右指针:两个指针相向或相背移动
https://leetcode.cn/problems/remove-duplicates-from-sorted-array
给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
思路:使用下标作为指针。
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
slow = fast = 0
while fast < len(nums):
if nums[slow] != nums[fast]:
# +1 是为了让[0,slow]都没有重复,修改的是后一个位置
slow += 1
nums[slow] = nums[fast]
fast += 1
# 要求返回修改后数组的长度
return slow + 1
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
https://leetcode.cn/problems/remove-duplicates-from-sorted-list/
思路:跟数组这个完全一样!!!
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
if head == None or head.next == None:
return head
fast = slow = head
while fast != None:
if fast.val != slow.val:
slow.next = fast
slow = slow.next
fast = fast.next
# 此时已经slow后面是重复元素了,所以给它连到None上去
# 比如1→2→2,slow最后停在第一个2上
slow.next = None
return head
https://leetcode.cn/problems/remove-element
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
思路:双指针。快指针一路向前,如果是val,那么直接跳过去;如果不是val,将快指针值赋值到慢指针位置,然后移动慢指针。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
if len(nums) == 0: return 0
fast = slow = 0
while fast < len(nums):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
https://leetcode.cn/problems/move-zeroes/
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
思路:这题直接复用上面的代码,使val=0即可。然后再将slow后面的元素设为0
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if len(nums) == 1: return
fast = slow = 0
while fast < len(nums):
if nums[fast] != 0:
nums[slow] = nums[fast]
slow += 1
fast += 1
while slow < len(nums):
nums[slow] = 0
slow += 1
或者直接交换也行,这样跟前面的格式更像:
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
fast = slow = 0
while fast < len(nums):
if nums[fast] != 0:
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1
fast += 1
def binarySearch(nums:list[int], target:int)->int:
left = 0
right = len(nums) - 1
# 注意这里有个=
while left <=right:
mid = (right + left) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
其中计算mid可以换成: left+(right-left)//2 这样可以防止数值过大时溢出
https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/
思路:看到非递减、有序之类的字眼立刻想到能不能用二分查找。。
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
left = 0
right = len(numbers) - 1
# 不能重复使用
while left < right:
s = numbers[left] + numbers[right]
if s == target:
return [left + 1, right + 1]
elif s < target:
left += 1
else:
right -= 1
就地反转数组or字符串,判断回文串都是利用相向的双指针。不再赘述。
def binarySearchLeftBound(nums:list[int], target:int)->int:
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] == target:
right = mid - 1 # 不断向左收缩,找左边界
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
if left == len(nums): return -1
if nums[left] == target: return left
else: return -1
def binarySearchLeftBound(nums:list[int], target:int)->int:
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] <= target:
left = mid + 1 # 不断向右
else:
right = mid - 1
if left -1 < 0: return -1
if nums[left-1] == target: return left-1
else: return -1
https://leetcode.cn/problems/search-insert-position
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
nums 为 无重复元素 的 升序 排列数组
思路:插入位置是左边界
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
left = 0
right = len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
https://leetcode.cn/problems/sqrtx
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
示例 1:
输入:x = 4
输出:2
示例 2:
输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842…, 由于返回类型是整数,小数部分将被舍去。
思路:二分查找,找右边界。(找右边界是因为最终跳出的时候有边界比较小!!!这样好记)
class Solution:
def mySqrt(self, x: int) -> int:
left = 1
right = x
while left <= right:
mid = (left + right) // 2
m = mid * mid
if m == x: return mid
elif m < x:
left = mid + 1
else:
right = mid - 1
return right
思路:直接套用,在return mid的时候return True,最后return False完事。
https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
思路:直接调用上面的函数即可。另外加对[]的判断。
class Solution:
def searchLeftBound(self, nums: List[int], target: int) -> int:
if nums == []: return -1
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] >= target:
right = mid -1
else:
left = mid + 1
if left == len(nums) or nums[left] != target: return -1
return left
def searchRightBound(self, nums: List[int], target: int) -> int:
if nums == []: return -1
left = 0
right = len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] <= target:
left = mid + 1
else:
right = mid - 1
if left < 1 or nums[left-1] != target: return -1
return left-1
def searchRange(self, nums: List[int], target: int) -> List[int]:
return [self.searchLeftBound(nums, target), self.searchRightBound(nums, target)]
https://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:
思路:还是直接复用二分查找左右边界的函数。这里直接写主函数了。
def search(self, nums: List[int], target: int) -> int:
left = self.searchLeftBound(nums, target)
# 如果左边界已经找不到,那没必要继续查找右边界了
if left == -1: return 0
right = self.searchRightBound(nums, target)
return right - left + 1
https://leetcode.cn/problems/first-bad-version/
# The isBadVersion API is already defined for you.
# def isBadVersion(version: int) -> bool:
class Solution:
def firstBadVersion(self, n: int) -> int:
left = 1
right = n
while left < right:
mid = left + (right - left) // 2
# 答案在[left, mid]
if isBadVersion(mid):
right = mid
else:
left = mid + 1
return left
https://leetcode.cn/problems/peak-index-in-a-mountain-array
符合下列属性的数组 arr 称为 山脉数组 :
示例 1:
输入:arr = [0,1,0]
输出:1
示例 2:
输入:arr = [0,2,1,0]
输出:1
思路: https://leetcode.cn/problems/find-peak-element 峰值元素是指其值严格大于左右相邻值的元素。 输入:nums = [1,2,1,3,5,6,4] 思路: 跟852的代码可以完全一样:) https://leetcode.cn/problems/find-the-distance-value-between-two-arrays 给你两个整数数组 arr1 , arr2 和一个整数 d ,请你返回两个数组之间的 距离值 。 「距离值」 定义为符合此距离要求的元素数目:对于元素 arr1[i] ,不存在任何元素 arr2[j] 满足 |arr1[i]-arr2[j]| <= d 。 示例 1: 解释: 对于 arr1[1]=5 我们有: 对于 arr1[2]=8 我们有: 故而只有 arr1[0]=4 和 arr1[1]=5 两个符合距离要求,距离值为 2 思路: 这题思路没什么,主要是注意条件,尤其是插入位置在数组范围外时。 https://leetcode.cn/problems/longest-palindromic-substring 思路:这次是从中心向两侧扩展。相背而行的双指针。 https://leetcode.cn/problems/palindromic-substrings/ 思路:照着上题的改,只不过每次得到回文的时候都+1 https://leetcode.cn/problems/valid-palindrome 给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 。 示例 1: 思路: 需要注意的一点是,如果左右指针出界了,说明已经没有可以比较的字母数字字符了,就比如倒数第二个case吧,“,;”,这种,自然要判定为True的。
跟上一题的思路是一样的。注意left<=right的话可能一直循环。因为一定会有结果,所以leftclass Solution:
def peakIndexInMountainArray(self, arr: List[int]) -> int:
left = 0
right = len(arr) - 1
while left < right:
mid = (left + right) // 2
if arr[mid] > arr[mid + 1]:
right = mid
else:
left = mid + 1
return left
162. 寻找峰值
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
输出:1 或 5
解释:你的函数可以返回索引 1,其峰值元素为 2;或者返回索引 5, 其峰值元素为 6。1385. 两个数组间的距离值
输入:arr1 = [4,5,8], arr2 = [10,9,1,8], d = 2
输出:2
对于 arr1[0]=4 我们有:
|4-10|=6 > d=2
|4-9|=5 > d=2
|4-1|=3 > d=2
|4-8|=4 > d=2
所以 arr1[0]=4 符合距离要求
|5-10|=5 > d=2
|5-9|=4 > d=2
|5-1|=4 > d=2
|5-8|=3 > d=2
所以 arr1[1]=5 也符合距离要求
|8-10|=2 <= d=2
|8-9|=1 <= d=2
|8-1|=7 > d=2
|8-8|=0 <= d=2
存在距离小于等于 2 的情况,不符合距离要求class Solution:
def seek(self, arr, t):
left = 0
right = len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == t:
return mid
elif arr[mid] > t:
right = mid - 1
else:
left = mid + 1
return left
def findTheDistanceValue(self, arr1: List[int], arr2: List[int], d: int) -> int:
arr2.sort()
ans = 0
for i in arr1:
a = self.seek(arr2, i)
if a < 0 or len(arr2) < 2:
if abs(arr2[0] - i) > d:
ans += 1
elif a == 0:
if min(abs(arr2[0] - i), abs(arr2[1] - i)) > d:
ans += 1
continue
elif a > len(arr2) - 1 or len(arr2) < 2:
if abs(arr2[-1] - i) > d:
ans += 1
elif a == len(arr2) - 1:
if min(abs(arr2[-1] - i), abs(arr2[-2] - i)) > d:
ans += 1
continue
else:
if min(abs(arr2[a-1]-i), abs(arr2[a]-i), abs(arr2[a+1]-i)) > d:
ans += 1
continue
return ans
回文串
【中等】5. 最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。class Solution:
# 计算以[left,right]为中心的最长回文子串
def findWithBound(self, s:str, left:int, right:int) -> str:
# 下标合法且满足回文要求,则向两侧扩展子串
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
return s[left+1:right]
def longestPalindrome(self, s: str) -> str:
ans = ""
for i in range(len(s)):
# 如果字串长度为奇数,中心就是自己
odds = self.findWithBound(s, i, i)
# 如果字符串长度为偶数,那么输入相邻的两个
evens = self.findWithBound(s, i, i + 1)
# 选择长度最长的那个更新ans值
if len(odds) > len(ans):
ans = odds
if len(evens) > len(ans):
ans = evens
return ans
【中等】647. 回文子串
给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
输入:s = “aaa”
输出:6
解释:6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”class Solution:
def findWithBound(self, s:str, left:int, right:int) -> int:
cnt = 0
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
# 表示此时有一个回文子串
cnt += 1
return cnt
def countSubstrings(self, s: str) -> int:
cnt = 0
for i in range(len(s)):
cnt += self.findWithBound(s,i,i) + self.findWithBound(s,i,i+1)
return cnt
125. 验证回文串
如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。字母和数字都属于字母数字字符。
输入: s = “A man, a plan, a canal: Panama”
输出:true
解释:“amanaplanacanalpanama” 是回文串。
示例 2:
输入:s = “race a car”
输出:false
解释:“raceacar” 不是回文串。class Solution:
def isPalindrome(self, s: str) -> bool:
t = []
s = s.lower()
left = 0
right = len(s) - 1
while left <= right:
while left < len(s) and not s[left].isalnum():
left += 1
while right > -1 and not s[right].isalnum():
right -= 1
if left >= len(s) or right <= 0: return True
if s[left] != s[right]:
return False
else:
left += 1
right -= 1
return True