本文所有题目和部分题解均源于LeetCode
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problemset/all/
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
说来也是惭愧,在大学里就注册了LeetCode账号,而到现在又重新注册了一个账号开始刷题…
若不是最近在深入学习人工智能,我可能不会碰它吧…哈哈
LeetCode刷题之路
1. 两数之和
-
题目
给定一个整数数组
nums
和一个目标值target
,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
-
解题思路
我们把原先的数组转为字典,通过字典查询加快速度。
- 判断
target
-nums[i]
是否在字典中,不在就添加 - 通过查询到的字典的值反向获取下标索引
- 实现代码
def twoSum(nums, target): dic = {} n = len(nums) for i in range(n): if target - nums[i] in dic: return dic[target - nums[i]], i else: dic[nums[i]] = i
2. 两数相加
-
题目
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807
-
解题思路
通过对每一个位置进行除法运算和取余来确定本位置的值,以及该位置是否需要进一。 -
实现代码
-
在Pycharm上实现代码
class ListNode: def __init__(self, val): if isinstance(val, int): self.val = val self.next = None elif isinstance(val, list): self.val = val[0] self.next = None cur = self for i in val[1:]: cur.next = ListNode(i) cur = cur.next def gatherAttrs(self): return ", ".join("{}: {}".format(k, getattr(self, k)) for k in self.__dict__.keys()) def __str__(self): return self.__class__.__name__ + " {" + "{}".format(self.gatherAttrs()) + "}" class Solution: def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: if isinstance(l1, list): l1 = ListNode(l1) l2 = ListNode(l2) head = ListNode(0) # 头结点,无存储,指向链表第一个结点 node = head # 初始化链表结点 s = 0 # 初始化 进一 的数 while l1 or l2: x = l1.val if l1 else 0 y = l2.val if l2 else 0 sum = x + y + s # 对每一位求和 s = sum // 10 # 求进一(其为0或1) node.next = ListNode(sum % 10) # 取余数,求本位结点 if l1: # 求空否,防止出现无后继结点 l1 = l1.next if l2: # 同上 l2 = l2.next node = node.next # 更新指针 if s != 0: # 验证最后一位相加是否需 进一 node.next = ListNode(1) return head.next # 返回头结点的下一个结点,即链表的第一个结点 if __name__ == '__main__': test = Solution() print(test.addTwoNumbers([1, 3], [2, 1, 3]))
-
在LeetCode上实现代码
# Definition for singly-linked list. # class ListNode: # def __init__(self, x): # self.val = x # self.next = None class Solution: def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: head = ListNode(0) #头结点,无存储,指向链表第一个结点 node = head #初始化链表结点 s = 0 #初始化 进一 的数 while l1 or l2: x = l1.val if l1 else 0 y = l2.val if l2 else 0 sum = x + y + s # 对每一位求和 s = sum // 10 # 求进一(其为0或1) node.next = ListNode(sum % 10) # 取余数,求本位结点 if l1: # 求空否,防止出现无后继结点 l1 = l1.next if l2: # 同上 l2 = l2.next node = node.next # 更新指针 if s != 0: # 验证最后一位相加是否需 进一 node.next = ListNode(1) return head.next # 返回头结点的下一个结点,即链表的第一个结点
3. 无重复字符的最长子串
-
题目
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。示例 1:
输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc", 所以其长度为3。
示例 2:
输入: "bbbbb" 输出: 1 解释: 因为无重复字符的最长子串是 "b", 所以其长度为 1。
示例 3:
输入: "pwwkew" 输出: 3 解释: 因为无重复字符的最长子串是 "wke", 所以其长度为 3。请注意,你的答案必 须是子串的长度,"pwke" 是一 个子序列,不是子串。
-
解题思路
本题主要采取遍历的方式来记录最长的字符串。
- 对字符串进行遍历,若不重复,存入暂存的字符串变量;
- 遇到重复,则找到重复字符出现的第一个位置,保存其后面的字符串;
- 比较当前字符串和记录的最长字符串,如果当前更长,则进行替换。
- 实现代码
def lengthOfLongestSubstring(str): max_str = '' # 保存最大字符串长度 ele_str = '' # 暂存字符串 for element in str: # 对字符串进行遍历,如果不重复则存入暂存的字符串变量 if element not in ele_str: ele_str += element # 遇到重复,则找到重复字符出现的第一个位置,保存其后面的字符串 else: ele_str = ele_str[ele_str.index(element) + 1:] ele_str += element # 如果当前字符串更长,则进行替换 if len(ele_str) > len(max_str): max_str = ele_str return len(max_str)
4. 寻找两个正序数组的中位数
-
题目
给定两个大小为m
和n
的正序(从小到大)数组nums1
和nums2
。请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为
O(log(m + n))
。你可以假设
nums1
和nums2
不会同时为空。 -
解题思路
我们可以将两个数组合并成一个数组,排序后找到中位数(1) 用Python数组的
extend()
函数,将第二个数组的元素添加到第一个数组中nums1.extend(nums2)
(2) 对当前数组进行排序nums1.sort()
(3) 判断中位数下标,我们设数组长度为l
,l = len(nums1)
,中位数设为res
- 若为数组长度为奇数,则中位数为
nums1[l//2]
- 若为数组长度为偶数,则中位数为
(nums1[l // 2 - 1] + nums1[l // 2]) / 2
- 若为数组长度为奇数,则中位数为
-
实现代码
def findMedianSortedArrays(num1, num2): num1.extend(num2) num1.sort() l = len(num1) if l % 2 == 1: res = num1[l // 2] else: res = (num1[l // 2 - 1] + num1[l // 2]) / 2 return res
5. 最长回文子串
-
题目
给定一个字符串s
,找到s
中最长的回文子串。你可以假设s
的最大长度为 1000。示例 1:
输入: "babad" 输出: "bab" 注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
-
解题思路
从最长字符串开始比较,比如说字符串长是5,那就先把原串取反进行比较,如果不相等,则缩短长度到4,分别比较,以此类推。 -
实现代码
def longestPalindrome(s): for l in range(len(s), -1, -1): for i in range(0, len(s) - l + 1): res_str = s[i:l + i] if res_str == res_str[::-1]: return res_str
6. Z字形变换
-
题目
将一个给定字符串根据给定的行数,以从上往下、从左到右进行Z
字形排列。比如输入字符串为
"LEETCODEISHIRING"
行数为 3 时,排列如下:L C I R E T O E S I I G E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:
"LCIRETOESIIGEDHN"
。请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入: s = "LEETCODEISHIRING", numRows = 3 输出: "LCIRETOESIIGEDHN"
示例 2:
输入: s = "LEETCODEISHIRING", numRows = 4 输出: "LDREOEIIECIHNTSG" 解释: L D R E O E I I E C I H N T S G
-
解题思路
我们先把L C I R E T O E S I I G E D H N
写成下面这个样子,也就是
V
型的,比题目说的N
型好理解
可以想象用手捏着最后一个字符,往右拉了一下:L C I R E T O E S I I G E D H N
实际上就是,
对列来说,一直往右走,
对行来说,碰到边界就改变方向 -
实现代码
def convert(s: str, numRows: int) -> str: if numRows <= 1: return s array = [s for a in range(numRows)] # ['LEETCODEISHIRING', 'LEETCODEISHIRING', 'LEETCODEISHIRING'] ans = [list() for b in range(numRows)] # [[], [], []] i, direction = 0, 1 res = '' for j in range(0, len(s)): ans[i].append(array[i][j]) i += direction if i == 0 or i == numRows - 1: direction *= -1 res = list() for element in ans: for k in element: res.append(k)
7. 整数反转
-
题目
给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。示例 1:
输入: 123 输出: 321
示例 2:
输入: -123 输出: -321
示例 3:
输入: 120 输出: 21
注意:
假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。
-
解题思路
- 先将整数转换为字符串;
- 判断字符串的第一个元素是否为
‘-’
,如果是,加‘-’
并反转字符串,如果不是则直接反转字符串; - 判断反转后的字符串是否在 -231~231-1 范围内,若不在返回0。
-
实现代码
def reverse_(x): s = str(x) if s[0] == '-': x = int('-' + s[1:][::-1]) else: x = int(s[::-1]) if (-2 ** 31) < x < (2 ** 31 - 1): return x else: return 0
8. 字符串转换整数(atoi)
-
题目
请你来实现一个 atoi 函数,使其能将字符串转换成整数。首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。接下来的转化规则如下:
● 如果第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字字符组合起来,形成一个有符号整数。
● 假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成一个整数。
● 该字符串在有效的整数部分之后也可能会存在多余的字符,那么这些字符可以被忽略,它们对函数不应该造成影响。注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换,即无法进行有效转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0 。
提示:
本题中的空白字符只包括空格字符 ’ ’ 。
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。示例 1:
输入: "42" 输出: 42
示例 2:
输入: " -42" 输出: -42 解释: 第一个非空白字符为 '-', 它是一个负号。 我们尽可能将负号与后面所有连续出现的数字 组合起来,最后得到 -42 。
示例 3:
输入: "4193 with words" 输出: 4193 解释: 转换截止于数字 '3' , 因为它的下一个字符不为数字。
示例 4:
输入: "words and 987" 输出: 0 解释: 第一个非空字符是 'w', 但它不是 数字或正、负号。因此无法执行有效的转换。
示例 5:
输入: "-91283472332" 输出: -2147483648 解释: 数字 "-91283472332" 超过 32 位有符号 整数范围。 因此返回 INT_MIN。
-
解题思路
本题采用正则表达式来匹配关键字,从而得到自己想要的结果。 -
实现代码
import re def myAtoi(str): INT_MIN = -2 ** 31 INT_NAX = 2 ** 31 - 1 str = str.lstrip() num = 0 str_re = re.compile('^[\+\-]?\d+') num_str = str_re.findall(str) for i in num_str: num = int(''.join(i)) if num > INT_NAX: num = INT_NAX elif num < INT_MIN: num = INT_MIN return num
9. 回文数
-
题目
判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。示例 1:
输入: 121 输出: true
示例 2:
输入: -121 输出: false 解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例 3:
输入: 10 输出: false 解释: 从右向左读, 为 01 。因此它不是一个回文数。
-
解题思路
转为字符串,再进行判断 -
实现代码
def isPalindrome(x: int): s = str(x) # if s == s[::-1]: # return True # else: # return False return s[::-1] == s
10. 正则表达式匹配
-
题目
给你一个字符串s
和一个字符规律p
,请你来实现一个支持'.'
和'*'
的正则表达式匹配。'.' 匹配任意单个字符 '*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串
s
的,而不是部分字符串。说明:
●
s
可能为空,且只包含从a-z
的小写字母。
●p
可能为空,且只包含从a-z
的小写字母,以及字符.
和*
。示例 1:
输入: s = "aa" p = "a" 输出: false 解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:
输入: s = "aa" p = "a*" 输出: true 解释: 因为 '*' 代表可以匹配零个或多个前面的 那一个元素, 在这里前面的元素就是 'a'。 因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
输入: s = "ab" p = ".*" 输出: true 解释: ".*" 表示可匹配零个或多个('*') 任意字符('.')。
示例 4:
输入: s = "aab" p = "c*a*b" 输出: true 解释: 因为 '*' 表示零个或多个,这里 'c' 为 0 个, 'a' 被重复一次。因此可以匹配字符串 "aab"。
示例 5:
输入: s = "mississippi" p = "mis*is*p*." 输出: false
-
解题思路
这里参考的他人的解题思路,不得不说,我怕是长了个假脑子,
@lru_cache(None)装饰符,实现记忆化搜索,等价于Top-Down动态规划,不懂其用法的可自行百度… -
实现代码
class Solution: @lru_cache(None) def isMatch(self, s: str, p: str) -> bool: if not p: return not s # 结束条件 first_match = (len(s) > 0) and p[0] in {s[0], '.'} # 先处理 `*` if len(p) >=2 and p[1] == '*': # 匹配0个 | 多个 return self.isMatch(s, p[2:]) or (first_match and self.isMatch(s[1:], p)) # 处理 `.` ,匹配一个 return first_match and self.isMatch(s[1:], p[1:])