https://leetcode.cn/problems/search-in-rotated-sorted-array/description/
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
# 1.首先,找出数组的中间元素。
# 2.然后,判断数组的哪一半是有序的(左半部分或右半部分)。
# 3.检查目标值是否在有序的部分内。
# 如果是,就在这一半里继续搜索。
# 如果不是,就在另一半里搜索。
# 4.重复以上步骤,直到找到目标值或者搜索范围为空。
class Solution(object):
def search(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
# 初始化左右指针
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2 # 计算中间指针的位置
# 如果中间的元素就是目标值,直接返回下标
if nums[mid] == target:
return mid
# 判断左半部分是否有序
if nums[left] <= nums[mid]:
# 如果目标值在左半部分,调整右指针
if nums[left] <= target < nums[mid]:
right = mid - 1
else: # 否则调整左指针
left = mid + 1
else: # 右半部分有序
# 如果目标值在右半部分,调整左指针
if nums[mid] < target <= nums[right]:
left = mid + 1
else: # 否则调整右指针
right = mid - 1
# 如果找不到目标值,返回 -1
return -1
测试代码
# 输入:nums = [4,5,6,7,0,1,2], target = 0
# 输出:4
# 输入:nums = [4,5,6,7,0,1,2], target = 3
# 输出:-1
# 输入:nums = [1], target = 0
# 输出:-1
# 定义示例数组和目标值
nums = [4,5,6,7,0,1,2]
target = 0
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.search(nums, target)
result
https://leetcode.cn/problems/find-peak-element/description/
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞ 。
你必须实现时间复杂度为 O(log n) 的算法来解决此问题。
# 1.初始化左右指针分别为数组的开始和结束位置。
# 2.找到中间位置的元素。
# 4.检查中间元素是否大于其两侧的元素。如果是,那么中间元素就是一个峰值,返回其索引。
# 5.如果中间元素不是峰值,根据与相邻元素的比较,决定是向左还是向右二分查找。
# 6.重复步骤 2-4,直到找到一个峰值。
class Solution(object):
def findPeakElement(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
left, right = 0, len(nums) - 1
while left < right:
mid = (left + right) // 2
# 比较中间元素和其右侧元素
if nums[mid] < nums[mid + 1]:
# 如果中间元素小于右侧元素,则峰值在右侧
left = mid + 1
else:
# 否则,峰值在左侧
right = mid
# 最终 left 和 right 会相遇在峰值位置
return left
测试代码
# 输入:nums = [1,2,3,1]
# 输出:2
# 解释:3 是峰值元素,你的函数应该返回其索引 2。
# 输入:nums = [1,2,1,3,5,6,4]
# 输出:1 或 5
# 解释:你的函数可以返回索引 1,其峰值元素为 2;
# 或者返回索引 5, 其峰值元素为 6。
# 定义示例数组和目标值
nums = [1,2,1,3,5,6,4]
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.findPeakElement(nums)
result
https://leetcode.cn/problems/median-of-two-sorted-arrays/description/
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
class Solution(object):
def findMedianSortedArrays(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: float
"""
# 确保 nums1 是两个数组中较短的一个,m 和 n 分别是 nums1 和 nums2 的长度
m, n = len(nums1), len(nums2)
if m > n:
nums1, nums2, m, n = nums2, nums1, n, m
low, high = 0, m
while low <= high:
# partitionM 是在 nums1 中的切分位置,partitionN 是在 nums2 中的切分位置
partitionM = (low + high) // 2
partitionN = (m + n + 1) // 2 - partitionM
# 如果 partitionM 是 0,表示 nums1 的左侧没有元素,所以左侧最大值设为负无穷
# 如果 partitionM 是 m 的长度,表示 nums1 的右侧没有元素,所以右侧最小值设为正无穷
maxLeftM = float('-inf') if partitionM == 0 else nums1[partitionM - 1]
minRightM = float('inf') if partitionM == m else nums1[partitionM]
# 对 nums2 执行类似操作
maxLeftN = float('-inf') if partitionN == 0 else nums2[partitionN - 1]
minRightN = float('inf') if partitionN == n else nums2[partitionN]
# 检查是否找到正确的切分
if maxLeftM <= minRightN and maxLeftN <= minRightM:
# 若数组总长度为偶数,中位数为左侧最大值和右侧最小值的平均值
# 若数组总长度为奇数,中位数为左侧的最大值
if (m + n) % 2 == 0:
return (max(maxLeftM, maxLeftN) + min(minRightM, minRightN)) / 2.0
else:
return max(maxLeftM, maxLeftN)
elif maxLeftM > minRightN: # 如果 maxLeftM 大于 minRightN,则向左移动切分
high = partitionM - 1
else: # 否则向右移动切分
low = partitionM + 1
# 如果达到这里,意味着输入数组不是有序的
raise ValueError("输入的数组不是有序的。")
测试代码
# 输入:nums1 = [1,3], nums2 = [2]
# 输出:2.00000
# 解释:合并数组 = [1,2,3] ,中位数 2
# 输入:nums1 = [1,2], nums2 = [3,4]
# 输出:2.50000
# 解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
# 定义示例数组和目标值
nums1 = [1,3]
nums2 = [2]
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.findMedianSortedArrays(nums1,nums2)
result
https://leetcode.cn/problems/search-a-2d-matrix-ii/description/
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
# 1.从右上角开始,即 matrix[0][n-1](如果 matrix 是非空的)。
# 2.如果当前元素等于 target,返回 True。
# 3.如果当前元素大于 target,移动到左边的元素。
# 4.如果当前元素小于 target,移动到下面的元素。
# 5.重复步骤 2-4 直到找到 target 或者搜索范围不再有效为止。
class Solution(object):
def searchMatrix(self, matrix, target):
"""
:type matrix: List[List[int]]
:type target: int
:rtype: bool
"""
if not matrix or not matrix[0]:
return False
m, n = len(matrix), len(matrix[0])
# 从右上角开始
row, col = 0, n - 1
while row < m and col >= 0:
if matrix[row][col] == target:
return True
elif matrix[row][col] > target:
col -= 1 # 向左移动
else:
row += 1 # 向下移动
return False
测试代码
# 输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
# 输出:true
# 定义示例数组和目标值
matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]]
target = 5
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.searchMatrix(matrix,target)
result
https://leetcode.cn/problems/sqrtx/description/
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。
class Solution(object):
def mySqrt(self, x):
"""
:type x: int
:rtype: int
"""
# 如果x小于2,它的平方根就是它本身(因为0的平方根是0,1的平方根是1)
if x < 2:
return x
# 初始化左右指针
left, right = 0, x
# 使用二分查找法
while left <= right:
# 计算中点
mid = (left + right) // 2
# 检查中点的平方是否等于x
if mid * mid == x:
return mid
# 如果中点的平方小于x,意味着平方根在右半部分
elif mid * mid < x:
left = mid + 1
# 如果中点的平方大于x,意味着平方根在左半部分
else:
right = mid - 1
# 当循环结束时,right指针会指向小于x的最大平方根
return right
测试代码
# 输入:x = 8
# 输出:2
# 解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
# 定义示例数组和目标值
x =8
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.mySqrt(x)
result
https://leetcode.cn/problems/move-zeroes/description/
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
# 1.初始化 lastNonZeroFoundAt 为 0。
# 2.遍历数组:
# 如果当前元素不是 0,将其移动到 lastNonZeroFoundAt 的位置,并将 lastNonZeroFoundAt 加 1。
# 3.所有非零元素都已经按原有顺序移动到数组的前面后,从 lastNonZeroFoundAt 开始直到数组末尾的所有位置,都填充 0。
class Solution(object):
def moveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
# 初始化最后一个非零元素的下一个位置
lastNonZeroFoundAt = 0
# 遍历数组,将非零元素移动到前面
for i in range(len(nums)):
if nums[i] != 0:
nums[lastNonZeroFoundAt], nums[i] = nums[i], nums[lastNonZeroFoundAt]
lastNonZeroFoundAt += 1
# 数组剩余部分填充0
# 这一步其实在上一步的交换中已经完成,可以省略
测试代码
# 输入: nums = [0,1,0,3,12]
# 输出: [1,3,12,0,0]
# 定义示例数组和目标值
nums = [0,1,0,3,12]
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.moveZeroes(nums)
nums
https://leetcode.cn/problems/add-strings/description/
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
class Solution(object):
def addStrings(self, num1, num2):
"""
:type num1: str
:type num2: str
:rtype: str
"""
# 初始化结果字符串为空串,进位值为0
result, carry = "", 0
# 初始化两个指针i和j,分别指向num1和num2的末尾
i, j = len(num1) - 1, len(num2) - 1
# 当任一字符串还有字符未处理,或者还有进位未加时,继续循环
while i >= 0 or j >= 0 or carry:
# 从num1和num2中取出一个字符转换为数字,如果已经超出字符串长度,则用0代替
n1 = int(num1[i]) if i >= 0 else 0
n2 = int(num2[j]) if j >= 0 else 0
# 计算当前位的总和(包括进位)
total = n1 + n2 + carry
# 计算新的进位(如果两数之和大于等于10,则进位为1;否则为0)
carry = total // 10
# 将当前位的值(总和的个位数)添加到结果字符串的开头
result = str(total % 10) + result
# 向左移动指针,继续下一轮计算
i, j = i - 1, j - 1
# 返回最终的结果字符串
return result
测试代码
# 输入:num1 = "11", num2 = "123"
# 输出:"134"
# 定义示例数组和目标值
num1 = "11"
num2 = "123"
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.addStrings(num1,num2)
result
https://leetcode.cn/problems/sliding-window-maximum/description/
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
from collections import deque
class Solution(object):
def maxSlidingWindow(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: List[int]
"""
# 特殊情况处理:如果数组为空或窗口大小为0,返回空列表
if not nums or k == 0:
return []
# 如果窗口大小为1,每个元素都是最大值,直接返回原数组
if k == 1:
return nums
# 初始化双端队列和结果列表
deq = deque()
max_values = []
# 遍历数组元素
for i in range(len(nums)):
# 如果队列不为空,且当前元素大于队尾元素,从队尾移除元素
# 这确保队列从大到小排列
while deq and nums[i] > nums[deq[-1]]:
deq.pop()
# 将当前元素索引加入队尾
deq.append(i)
# 如果队首元素的索引已经不在窗口内(即索引小于当前索引减窗口大小)
# 从队首移除该元素
if deq[0] == i - k:
deq.popleft()
# 从达到窗口大小后开始,将当前窗口的最大值(队首元素)加入结果列表
if i >= k - 1:
max_values.append(nums[deq[0]])
# 返回结果列表
return max_values
测试代码
# 输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
# 输出:[3,3,5,5,6,7]
# 解释:
# 滑动窗口的位置 最大值
# --------------- -----
# [1 3 -1] -3 5 3 6 7 3
# 1 [3 -1 -3] 5 3 6 7 3
# 1 3 [-1 -3 5] 3 6 7 5
# 1 3 -1 [-3 5 3] 6 7 5
# 1 3 -1 -3 [5 3 6] 7 6
# 1 3 -1 -3 5 [3 6 7] 7
# 定义示例数组和目标值
nums = [1,3,-1,-3,5,3,6,7]
k = 3
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.maxSlidingWindow(nums, k)
result
https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
class Solution(object):
def lengthOfLongestSubstring(self, s):
"""
:type s: str
:rtype: int
"""
# 使用集合来存储当前窗口中的字符
charSet = set()
# 初始化左指针和结果
left = 0
result = 0
# 遍历字符串,用右指针表示当前字符的位置
for right in range(len(s)):
# 当前字符
current_char = s[right]
# 如果当前字符已经在集合(窗口)中,不断移动左指针
# 并从集合中移除对应的字符,直到移除了重复的字符
while current_char in charSet:
charSet.remove(s[left])
left += 1
# 将当前字符添加到集合中
charSet.add(current_char)
# 更新结果,即最长子串的长度
result = max(result, right - left + 1)
return result
测试代码
# 输入: s = "pwwkew"
# 输出: 3
# 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
# 请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
# 定义示例数组和目标值
s = "pwwkew"
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.lengthOfLongestSubstring(s)
result
https://leetcode.cn/problems/minimum-window-substring/description/
给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
class Solution(object):
def minWindow(self, s, t):
"""
:type s: str
:type t: str
:rtype: str
"""
# 初始化需要的字符的哈希表
need = {}
for char in t:
need[char] = need.get(char, 0) + 1
window = {}
left, right = 0, 0
valid = 0
start, length = 0, float('inf')
while right < len(s):
# c 是将移入窗口的字符
c = s[right]
# 右移窗口
right += 1
# 进行窗口内数据的一系列更新
if c in need:
window[c] = window.get(c, 0) + 1
if window[c] == need[c]:
valid += 1
# 判断左侧窗口是否要收缩
while valid == len(need):
# 更新最小覆盖子串
if right - left < length:
start = left
length = right - left
# d 是将移出窗口的字符
d = s[left]
# 左移窗口
left += 1
# 进行窗口内数据的一系列更新
if d in need:
if window[d] == need[d]:
valid -= 1
window[d] -= 1
# 返回最小覆盖子串
return "" if length == float('inf') else s[start:start + length]
测试代码
# 输入:s = "ADOBECODEBANC", t = "ABC"
# 输出:"BANC"
# 解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
# 定义示例数组和目标值
s = "ADOBECODEBANC"
t = "ABC"
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.minWindow(s,t)
result
https://leetcode.cn/problems/maximum-length-of-repeated-subarray/description/
给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。
class Solution(object):
def findLength(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: int
"""
# 获取两个数组的长度
m, n = len(nums1), len(nums2)
# 初始化动态规划数组,dp[i][j] 表示以 nums1[i-1] 和 nums2[j-1] 结尾的最长公共子数组长度
dp = [[0] * (n + 1) for _ in range(m + 1)]
# 用于记录最长公共子数组的长度
max_length = 0
# 遍历两个数组
for i in range(1, m + 1):
for j in range(1, n + 1):
# 当发现 nums1[i-1] 和 nums2[j-1] 相等时
if nums1[i - 1] == nums2[j - 1]:
# 更新 dp[i][j],当前长度是之前的长度加1
dp[i][j] = dp[i - 1][j - 1] + 1
# 更新最长公共子数组的长度
max_length = max(max_length, dp[i][j])
# 返回最长公共子数组的长度
return max_length
测试代码
# 输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
# 输出:3
# 解释:长度最长的公共子数组是 [3,2,1] 。
# 定义示例数组和目标值
nums1 = [1,2,3,2,1]
nums2 = [3,2,1,4,7]
# 创建 Solution 的实例
sol = Solution()
# 对每个目标值进行测试
result = sol.findLength(nums1,nums2)
result
https://leetcode.cn/problems/remove-duplicates-from-sorted-list/description/
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 初始化当前节点为头节点
current = head
# 遍历链表直到当前节点或其下一个节点为空
while current and current.next:
# 如果当前节点和下一个节点的值相同
if current.val == current.next.val:
# 删除下一个节点,即将当前节点的 next 指向下下个节点
current.next = current.next.next
else:
# 如果不相同,移动到下一个节点
current = current.next
# 返回处理后的链表头节点
return head
def printList(head):
current = head
while current:
print(current.val, end=" ")
current = current.next
print()
测试代码
# 输入:head = [1,1,2]
# 输出:[1,2]
# 创建链表 1 -> 1 -> 2
head = ListNode(1)
head.next = ListNode(1)
head.next.next = ListNode(2)
# 删除重复元素
solution = Solution()
result = solution.deleteDuplicates(head)
# 打印结果链表
printList(result)
https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/description/
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def deleteDuplicates(self, head):
"""
:type head: ListNode
:rtype: ListNode
"""
# 使用哨兵节点简化边界条件处理
sentinel = ListNode(0, head)
# pred 是最近一个没有重复的节点
pred = sentinel
while head:
# 如果当前节点的值与下一个节点的值相同,则跳过所有相同的值
if head.next and head.val == head.next.val:
# 跳过所有相同的值
while head.next and head.val == head.next.val:
head = head.next
# 连接 pred 和 head 的下一个节点(跳过所有重复的节点)
pred.next = head.next
else:
# 如果当前节点的值不重复,则更新 pred
pred = pred.next
# 移动 head 到下一个节点
head = head.next
# 返回哨兵节点的下一个节点,即处理后的链表头
return sentinel.next
def printList(head):
current = head
while current:
print(current.val, end=" ")
current = current.next
print()
测试代码
# 输入:head = [1,2,3,3,4,4,5]
# 输出:[1,2,5]
# 创建链表 1 -> 2 -> 3 -> 3 -> 4 -> 4 -> 5
head = ListNode(1)
head.next = ListNode(2)
head.next.next = ListNode(3)
head.next.next.next = ListNode(3)
head.next.next.next.next = ListNode(4)
head.next.next.next.next.next = ListNode(4)
head.next.next.next.next.next.next = ListNode(5)
# 删除重复元素
solution = Solution()
result = solution.deleteDuplicates(head)
# 打印结果链表
printList(result)