Leetcode刷题记录1-21,python语言
1 两数之和
哈希表:利用python的字典,键为输入对象,值为输入对象的索引index
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
dict0 = {}
for i in range(0,len(nums)):
sub = target - nums[i]
if sub not in dict0:
dict0[nums[i]]=i
else:
return [dict0[sub],i]
2 两数相加
链表ListNode
% 取余数
// 除法,取整数
/ 除法,取浮点数
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
sum1 = l1.val + l2.val
l3 = ListNode(sum1 % 10)
l3.next = ListNode(sum1 // 10)
p1 = l1.next
p2 = l2.next
p3 = l3
while True:
if p1 and p2:
sum2 = p1.val + p2.val +p3.next.val
p3.next = ListNode(sum2 % 10)
p3.next.next = ListNode(sum2 // 10)
p1 = p1.next
p2 = p2.next
p3 = p3.next
elif p1 and not p2:
sum2 = p1.val + p3.next.val
p3.next = ListNode(sum2 % 10)
p3.next.next = ListNode(sum2 // 10)
p1 = p1.next
p3 = p3.next
elif p2 and not p1:
sum2 = p2.val + p3.next.val
p3.next = ListNode(sum2 % 10)
p3.next.next = ListNode(sum2 // 10)
p2 = p2.next
p3 = p3.next
else:
if p3.next.val == 0:
p3.next = None
break
return l3
3 无重复字符的最长子串
哈希表:字典,键为字母,值为索引
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
dict0 = {}
length = len(s)
if length == 1:
return length
head, max_length = 0, 0
for i in range(length):
if s[i] in dict0 and head <= dict0[s[i]]:
head = dict0[s[i]] + 1
else:
max_length = max(max_length, i-head+1)
dict0[s[i]] = i
return max_length
法二, 滑动窗口
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
dic, res, i = {}, 0, -1
for j in range(len(s)):
if s[j] in dic:
i = max(dic[s[j]], i) # 更新左指针 i
dic[s[j]] = j # 哈希表记录
res = max(res, j - i) # 更新结果
return res
4 寻找两个正序数组的中位数(Hard)
这段代码定义了两个函数 findMedianSortedArrays 和 findKth。findMedianSortedArrays 函数接受两个正序数组 nums1 和 nums2 作为输入,并返回这两个数组的中位数。findKth 函数用来找到两个数组的第 k 小的元素。
在 findMedianSortedArrays 函数中,如果两个数组的总长度是偶数,就返回中间两个数的平均值;如果总长度是奇数,则返回中间的那个数。
findKth 函数使用了递归的方法,在每次递归中,根据两个数组的中位数来判断应该舍弃哪一半的数组元素,然后继续在剩余的部分中寻找第 k 小的元素。
class Solution:
def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
total_len = len(nums1) + len(nums2)
def findKth(k, nums1, nums2):
if not nums1:
return nums2[k]
if not nums2:
return nums1[k]
index1, index2 = len(nums1) // 2, len(nums2) // 2
mid1, mid2 = nums1[index1], nums2[index2]
if index1 + index2 < k:
if mid1 > mid2:
return findKth(k - index2 - 1, nums1, nums2[index2 + 1:])
else:
return findKth(k - index1 - 1, nums1[index1 + 1:], nums2)
else:
if mid1 > mid2:
return findKth(k, nums1[:index1], nums2)
else:
return findKth(k, nums1, nums2[:index2])
if total_len % 2 == 0:
return (findKth(total_len // 2, nums1, nums2) + findKth(total_len // 2 - 1, nums1, nums2)) / 2
else:
return findKth(total_len // 2, nums1, nums2)
5 最长回文子串
这段代码定义了一个函数 longestPalindrome,它接受一个字符串 s 作为输入,并返回其中最长的回文子串。
该函数使用了中心扩展法,遍历字符串中的每个字符,以当前字符为中心向两边扩展,同时考虑了奇数长度和偶数长度的回文子串。通过不断更新最长回文子串的起始位置和长度,最终得到整个字符串中的最长回文子串。
class Solution:
def longestPalindrome(self, s: str) -> str:
if not s:
return ""
start = 0
max_length = 1
n = len(s)
def expand_around_center(s: str, left: int, right: int) -> int:
# 从指定的左右边界向两端扩展,直到无法扩展为止
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
# 返回回文子串的长度
return right - left - 1
# 中心扩展法
for i in range(n):
# 以单个字符为中心
length1 = expand_around_center(s, i, i)
# 以两个相邻字符的间隙为中心
length2 = expand_around_center(s, i, i + 1)
# 取两种中心扩展的最大长度
cur_length = max(length1, length2)
# 更新最长回文子串的起始位置和长度
if cur_length > max_length:
start = i - (cur_length - 1) // 2
max_length = cur_length
return s[start: start + max_length]
6 Z字形变换
这段代码定义了一个函数 convert,它接受一个字符串 s 和一个整数 numRows 作为输入,返回按照 Z 字形排列后的字符串。
在函数内部,我们使用一个长度为 min(numRows, len(s)) 的列表 rows 来存储每一行的字符。然后,我们遍历字符串 s 中的每个字符,根据当前行号 cur_row 将字符添加到对应的行中。当 cur_row 到达第一行或者最后一行时,我们改变方向,以便按照 Z 字形排列字符。
最后,我们将每一行的字符连接起来,得到最终的 Z 字形排列结果。
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows == 1:
return s
rows = [''] * min(numRows, len(s)) # 当取4行,就是这种['', '', '', '']
direction = 1
cur_row = 0
for char in s:
rows[cur_row] += char
if cur_row == 0:
direction = 1
elif cur_row == numRows - 1:
direction = -1
cur_row += direction
return ''.join(rows)
7 整数反转
定义一个函数 reverse(x),接受一个整数 x 作为参数。
首先判断输入的整数 x 的正负情况,分别处理。
使用 str(x) 将整数转换为字符串,然后通过切片 [::1] 或者 [1:][::-1] 对字符串进行反转操作。
将反转后的字符串转换为整数,并根据题目要求返回结果。
最后进行边界值判断,如果结果超出了 32 位有符号整数的范围,则返回 0。
执行测试样例,输出结果。
class Solution:
def reverse(self, x: int) -> int:
if x >= 0:
result = int(str(x)[::-1]) # [start : end : step],步长为-1,从右往左取数,索引为-1的开始 倒着取
else:
result = -int(str(x)[1:][::-1])
if result < -2**31 or result > 2**31 - 1:
return 0
else:
return result
8 字符串转换整数(atoi)
定义一个函数 myAtoi(s),接受一个字符串 s 作为参数。
使用 strip() 方法去除字符串两端的空格。
如果字符串为空,则直接返回 0。
设置一个符号位 sign,默认为正数,如果字符串起始位置是符号位,则根据情况更新符号位,并将字符串截取掉符号部分。
遍历剩余的字符串,处理每个字符,如果是数字则进行累加,如果遇到非数字字符则跳出循环。
将累加得到的数乘以符号位,得到最终结果。
最后处理溢出情况,如果结果超出了 32 位有符号整数的范围,则返回相应的边界值。
执行测试样例,输出结果。
class Solution:
def myAtoi(self, s: str) -> int:
s = s.strip( ) # 去除字符串两端的空格 strip() 处理的时候,如果不带参数,默认是清除两边的空白符,例如:/n, /r, /t, ‘ ‘)。
if not s: # 处理空字符串情况
return 0
sign = 1 # 符号位,默认为正数
if s[0] in ['-', '+']: # 处理符号位
if s[0] == '-':
sign = -1
s = s[1:]
num = 0
for char in s:
if char.isdigit(): # 字符串所有字符都是数字返回True
num = num * 10 + int(char) # 每出现下一次,就把上次的乘以10,进位1*10+2=12,12*10+3=123
else:
break # 碰到非数字的,跳出整个for循环
num = sign * num # 加上符号位,正负号
# 处理溢出情况
if num < -2**31:
return -2**31
elif num > 2**31 - 1:
return 2**31 - 1
else:
return num
9 回文数
解法:可以将整数转换为字符串,然后判断字符串是否为回文串。记住用while循环。
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0:
return False
num_str = str(x)
left = 0
right = len(num_str) - 1
while left < right:
if num_str[left] != num_str[right]:
return False
left += 1
right -= 1
return True
11 盛最多水的容器
在代码中,我们使用了两个指针left和right来表示数组的起始位置和结束位置。通过计算min(height[left], height[right]) * (right - left)来得到当前区域的面积,并与已经得到的最大面积max_area进行比较,更新最大面积。然后,根据当前左右指针所指向的高度的大小关系,移动较小的指针,继续寻找更大的面积。最后返回最大面积。
class Solution:
def maxArea(self, height: List[int]) -> int:
left = 0
right = len(height) - 1
max_area = 0
while left < right:
area = min(height[left], height[right]) * (right - left)
max_area = max(max_area, area)
if height[left] < height[right]:
left += 1
else:
right -= 1
return max_area
12 整数转罗马数字
用字典存储每个特殊罗马数字,键为整数,值为罗马数字。在这段代码中,intToRoman 函数接受一个整数作为输入,首先建立了一个字典 roman_dict 用来存储罗马数字与整数的对应关系。然后通过循环遍历字典中的键值对,从大到小逐步减去对应的整数值,并将对应的罗马数字添加到结果字符串 roman_num 中。
class Solution:
def intToRoman(self, num: int) -> str:
roman_dict = {
1000:'M', 900:'CM', 500:'D', 400:'CD', 100:'C', 90:'XC', 50:'L', 40:'XL', 10:'X', 9:'IX', 5:'V', 4:'IV', 1:'I'
}
roman_num = ''
for value, token in roman_dict.items():
while num >= value:
roman_num += token
num -= value
return roman_num
13 罗马数字转整数
在这段代码中,romanToInt 函数接受一个罗马数字字符串作为输入,首先建立了一个字典 roman_dict 用来存储罗马数字与整数的对应关系。然后通过遍历罗马数字字符串,根据规则进行转换并累加得到最终的整数值。在遍历过程中,根据当前字符的值和前一个字符的值来判断是做加法还是减法操作。记得替换值prev_value = cur_value
class Solution:
def romanToInt(self, s: str) -> int:
num_dict = {
'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000
}
prev_value = 0
total = 0
for char in s:
cur_value = num_dict[char]
if cur_value > prev_value:
total += cur_value - 2 * prev_value
else:
total += cur_value
prev_value = cur_value
return total
14 最长公共前缀
首先判断输入的字符串数组是否为空,如果为空则直接返回空字符串。
初始化公共前缀为第一个字符串,然后遍历字符串数组,逐个比较每个字符串与当前公共前缀的最长公共前缀,更新公共前缀。
最终得到的公共前缀即为最长公共前缀。
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
if not strs:
return ""
prefix = strs[0]
for string in strs[1:]: # 循环的是list中的各个元素第一个单词第二个单词第三个单词,而不是单个单个的字母
while string.find(prefix) != 0: # 只要有一个字母不一样,find()都是-1,都在string里面find()就是0
prefix = prefix[:-1]
if not prefix:
return ""
return prefix
15 三数之和
双指针用法,left right
首先将数组排序,方便后续操作。
遍历数组,将当前元素作为目标值,使用双指针来寻找满足条件的两个数。
在当前元素后面的范围内使用双指针,通过调整左右指针的值来逼近目标值。
如果找到了满足条件的三个数,则将其加入结果列表中。
最后返回结果列表。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort() # 先对数组排序
res = []
length = len(nums)
for i in range(length - 2):
# 跳过重复元素
if i > 0 and nums[i] == nums[i - 1]:
continue
left = i + 1
right = length - 1
target = -nums[i]
while left < right:
# 找到满足条件的三个数
if nums[left] + nums[right] == target:
res.append([nums[i], nums[left], nums[right]])
# 跳过重复元素
while left < right and nums[left] == nums[left + 1]:
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
left += 1
right -= 1
# 调整左右指针的值
elif nums[left] + nums[right] < target:
left += 1 # sort()后 让小的变大
else:
right -= 1 # 让大的变小
return res
16 最接近的三数之和
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
nums.sort()
closest_sum = float('inf')
for i in range(len(nums) - 2):
left, right = i + 1, len(nums) - 1 # 左指针从i+1开始,右指针最后一位
while left < right:
current_sum = nums[i] + nums[left] + nums[right]
if abs(current_sum - target) < abs(closest_sum - target):
closest_sum = current_sum
if current_sum < target:
left += 1
elif current_sum > target:
right -= 1
else:
return current_sum
return closest_sum
17 电话号码的字母组合
在这段代码中,我们首先建立了一个数字到字母的映射关系,然后使用迭代的方式来生成所有可能的字母组合。我们初始化一个空列表 combinations,然后遍历输入的每一个数字。对于每一个数字,我们将其对应的字母取出,然后将当前已有的组合与字母进行组合,得到新的组合,并存储在 new_combinations 中。最后更新 combinations 为 new_combinations,继续下一轮迭代,直到处理完所有的数字。最终返回所有可能的字母组合。
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
if not digits:
return []
digit_mapping = {
'2': 'abc',
'3': 'def',
'4': 'ghi',
'5': 'jkl',
'6': 'mno',
'7': 'pqrs',
'8': 'tuv',
'9': 'wxyz'
}
combinations = ['']
for digit in digits:
letters = digit_mapping[digit]
new_combinations = []
for combination in combinations:
for letter in letters:
new_combinations.append(combination + letter)
combinations = new_combinations
return combinations
18 四数之和
首先对输入数组进行排序,然后使用四个指针来遍历数组,找到所有满足条件的四元组。在遍历的过程中,利用双指针的方法来不断调整左右指针,以逼近目标值。
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
nums.sort()
n = len(nums)
result = []
for i in range(n-3): # 最左边的index,第一位
if i > 0 and nums[i] == nums[i-1]: # 最左边为相同值,跳到下一个i循环
continue
for j in range(i+1, n-2): # 第二位,从i+1
if j > i+1 and nums[j] == nums[j-1]: # j的上一次为相同值,下一个j循环
continue
left = j + 1 # 左指针,除去第一位 第二位
right = n - 1 # 右指针
while left < right:
total = nums[i] + nums[j] + nums[left] + nums[right]
if total == target:
result.append([nums[i], nums[j], nums[left], nums[right]])
while left < right and nums[left] == nums[left+1]: # 左指针向左碰到相同值
left += 1
while left < right and nums[right] == nums[right-1]: # 向右碰到相同值
right -= 1
left += 1
right -= 1
elif total < target:
left += 1
else:
right -= 1
return result
19 删除链表的倒数
双指针的方法来解决此问题。我们首先创建一个虚拟头结点 dummy,并将其指向链表的头部。然后,我们初始化两个指针 fast 和 slow 都指向 dummy。接着,我们让 fast 先向前移动 n+1 步,然后同时移动 fast 和 slow 直到 fast 到达链表末尾。此时 slow 指向要删除节点的前一个节点,我们只需将 slow.next 指向 slow.next.next 即可删除倒数第 n 个节点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0) # 虚拟头结点
dummy.next = head
fast = slow = dummy
# 让fast指针先向前移动n+1步。快慢指针相差n+1步是关键,到最后,快指针在尾节点下一个none,慢指针在要删的前一个,就刚好有slow.next = slow.next.next删去操作
for _ in range(n + 1):
fast = fast.next
# 同时移动fast和slow,直到fast到达链表末尾
while fast is not None:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy.next
20 有效的括号
栈的数据结构来解决此问题。我们遍历输入的字符串,对于每个字符,如果是左括号,则将其压入栈中;如果是右括号,则与栈顶元素进行匹配,若匹配则弹出栈顶元素,否则返回False。最后,检查栈是否为空,若为空则表示所有括号都匹配成功,返回True;否则返回False。
这种方法能够有效地检查括号是否匹配,因为栈的特性符合括号匹配的后进先出规则。
class Solution:
def isValid(self, s: str) -> bool:
stack = []
mapping = {')': '(', '}': '{', ']': '['}
for char in s: # 从左往右循环看
if char in mapping: # 针对字典的键,看char是否为键,若为右括号,
top_element = stack.pop() if stack else '#' # pop是默认弹出列表中的最后一个元素 若stack非空list时,看stack上一次加入的是否为对应左括号。否则top_element令为 # 井号,一定不匹配。
if mapping[char] != top_element: # 不匹配,
return False
else:
stack.append(char)
return not stack # 这里stack为空的话那就是空的s字符串也匹配, not stack意思是返回一个bool类型true 。 判断stack是否为空,为空也是匹配成功
21 合并两个有序链表
首先定义了一个链表节点类ListNode,然后定义了函数mergeTwoLists(l1, l2)来合并两个有序链表。在函数中,通过使用一个dummy节点和一个current指针来构建合并后的链表。接着通过循环比较两个链表的节点值,按顺序将较小的节点接入到合并后的链表中。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
dummy = ListNode(0) # 虚拟头结点
current = dummy # 指针,
while list1 and list2: # 将l1和l2视为指针,l1和l2都不为空时
if list1.val < list2.val: #
current.next = list1
list1 = list1.next
else:
current.next = list2
list2 = list2.next
current = current.next
if list1: #l1不为空时
current.next = list1
else: # l1为空时
current.next = list2
return dummy.next