未完待续…
一、哈希
1、两数之和
# 暴力解,时间复杂度:o(n^2)
class Solution(object):def twoSum(self, nums, target):""":type nums: List[int]:type target: int:rtype: List[int]"""for i in range(len(nums)):for j in range(i + 1, len(nums)):if nums[j] == target - nums[i]:print([i, j])return [i, j]# print(i, j)# 哈希优化解,时间复杂度o(n)
class Solution2(object):def twoSum(self, nums, target):dic = {}for i in range(len(nums)):tar_num = target - nums[i]if tar_num in dic:print([dic[tar_num], i])return [dic[tar_num], i]dic[nums[i]] = ireturn []if __name__ == '__main__':# s = Solution()# # s.twoSum([2, 7, 11, 15], 9)# # s.twoSum([3, 2, 4], 6)# s.twoSum([3, 3], 6)s = Solution2()# s.twoSum([2, 7, 11, 15], 9)s.twoSum([3, 2, 4], 6)# s.twoSum([3, 3], 6)
2.字母异位词分组
# 自己写的
class Solution(object):def groupAnagrams(self, strs):""":type strs: List[str]:rtype: List[List[str]]"""dic = {}final_list = []for word in strs:sort_tup_word = tuple(list(sorted(word)))if sort_tup_word in dic:dic[sort_tup_word].append(word)else:dic[sort_tup_word] = [word] # value为列表,才可以实现appendprint(dic)for k in dic:final_list.append(dic.get(k))print(final_list)return final_list# 看了大佬之后的优化方法,思路倒是一样的,只是对代码没那么熟悉
class Solution2(object):def groupAnagrams(self, strs):""":type strs: List[str]:rtype: List[List[str]]"""dic = {}final_list = []for word in strs:sort_word = "".join(sorted(word))if sort_word in dic:dic[sort_word].append(word)else:dic[sort_word] = [word]print(list(dic.values()))return list(dic.values())if __name__ == '__main__':# strs = ["eat", "tea", "tan", "ate", "nat", "bat"]# # strs = [""]# # strs = ["a"]# # for i in strs:# # print(list(sorted(i)))# s = Solution()# s.groupAnagrams(strs)strs = ["eat", "tea", "tan", "ate", "nat", "bat"]# strs = [""]# strs = ["a"]# for i in strs:# print(list(sorted(i)))s = Solution2()s.groupAnagrams(strs)
3.最长连续序列
class Solution(object):def longestConsecutive(self, nums):""":type nums: List[int]:rtype: int"""sort_num = sorted(set(nums)) # 题目没有明说,当出现两个数字相同时,要去重!cnt = 0 if len(nums) == 0 else 1 # 避免输入一个空列表的情况cnt_list = []for i in range(len(sort_num)):if i+1 < len(sort_num):if sort_num[i] + 1 == sort_num[i+1]:cnt += 1else:cnt_list.append(cnt)cnt = 1cnt_list.append(cnt)print(max(cnt_list))return max(cnt_list)if __name__ == '__main__':# nums = [100, 4, 200, 1, 3, 2]# nums = [0, 3, 7, 2, 5, 8, 4, 6, 0, 1]# nums = [9, 1, 4, 7, 3, -1, 0, 5, 8, -1, 6]nums = [1, 2, 0, 1]s = Solution()s.longestConsecutive(nums)
二:双指针
4、移动零
# 实际开发写法,类似快排的实现
class Solution1(object):def moveZeroes(self, nums):""":type nums: List[int]:rtype: None Do not return anything, modify nums in-place instead."""zero_index = 0for i in range(len(nums)):if nums[i] != 0:tmp = nums[zero_index]nums[zero_index] = nums[i]nums[i] = tmpzero_index += 1 # 一定要index所在值非0了,再往后移动,否则会导致数据0排在非0前面return nums# 缩写写法
class Solution2(object):def moveZeroes(self, nums):""":type nums: List[int]:rtype: None Do not return anything, modify nums in-place instead."""zero_index = 0for i in range(len(nums)):if nums[i] != 0:nums[zero_index], nums[i] = nums[i], nums[zero_index]zero_index += 1return numsif __name__ == '__main__':nums = [0, 1, 0, 3, 12]# nums = [0]# nums = [0, 0, 1]s = Solution2()print(s.moveZeroes(nums))
5、盛水最多的容器
class Solution(object):def maxArea(self, height): # 这个会超出时间限制""":type height: List[int]:rtype: int"""max_area = 0for i in range(len(height) - 1):for j in range(i+1, len(height)):length = j - iwidth = min(height[i], height[j])if max_area < length*width:max_area = length*widthprint(max_area)return max_area# 双指针,从两头开始内卷,先卷了挫的那头def maxArea_2(self, height):""":type height: List[int]:rtype: int"""l = 0r = len(height) - 1max_area = 0while l < r:tmp = min(height[l], height[r]) * (r-l)max_area = max(max_area, tmp)if height[l] < height[r]:l += 1else:r -= 1print(max_area)return max_areaif __name__ == '__main__':# test_list = [1, 8, 6, 2, 5, 4, 8, 3, 7]test_list = [1, 1]s = Solution()# s.maxArea(test_list)s.maxArea_2(test_list)
6、三数之和
本题的暴力题解可以仿照二数之和,直接三层遍历,取和为 0 的三元组,并记录下来,最后再去重。但是作为一个有智慧的人,我们不能这么去做。
因为我们的目标是找数,当然使用指针的方式最简单。假若我们的数组为:
[-1, 0, 1, 2, -1, -4]
求解过程如下:首先我们先把数组排个序(原因一会儿说),排完序长这样:
因为我们要同时找三个数,所以采取固定一个数,同时用双指针来查找另外两个数的方式。所以初始化时,我们选择固定第一个元素(当然,这一轮走完了,这个蓝框框我们就要也往前移动),同时将下一个元素和末尾元素分别设上 left 和 right 指针。画出图来就是下面这个样子:
现在已经找到了三个数,当然是计算其三值是否满足三元组。但是这里因为我们已经排好了序,如果**固定下来的数(上面蓝色框框)本身就大于 0,那三数之和必然无法等于 0 **。比如下面这种:
然后自然用脚指头也能想到,我们需要移动指针。现在我们的排序就发挥出用处了,如果和大于 0,那就说明 right 的值太大,需要左移。如果和小于 0,那就说明 left 的值太小,需要右移。(上面这个思考过程是本题的核心) 整个过程如下图所示:
其中:在第 6 行时,因为三数之和大于 0,所以 right 进行了左移。最后一行,跳过了重复的 -1。
然后啰嗦一句,因为我们需要处理重复值的情况。除了固定下来的i值(蓝框框),left 和 right 当然也是需要处理重复的情况,所以对于 left 和 left+1,以及 right 和 right-1,我们都单独做一下重复值的处理。(其实没啥处理,就是简单的跳过)
class Solution(object):def threeSum(self, nums):""":type nums: List[int]:rtype: List[List[int]]"""n = len(nums)res = []if not nums or n < 3:return []nums.sort() # 排序是为了使用双指针,从最小的数字开始遍历res = []for i in range(n):if nums[i] > 0: # 如果排序之后数据i是大于0的值,那么就没必要进行循环了return resif i > 0 and nums[i] == nums[i - 1]: # 跳过重复的数据continueL = i + 1 # 初始化R = n - 1 # 初始化while L < R:if nums[i] + nums[L] + nums[R] == 0:res.append([nums[i], nums[L], nums[R]])while L < R and nums[L] == nums[L + 1]: # 跳过重复的数字L = L + 1while L < R and nums[R] == nums[R - 1]:R = R - 1L = L + 1 # 盲猜为了得到L>R,结束循环R = R - 1 # 盲盲猜为了得到L>R,结束循环elif nums[i] + nums[L] + nums[R] > 0:R = R - 1else:L = L + 1return resif __name__ == '__main__':nums = [-1, 0, 1, 2, -1, -4]s = Solution()print(s.threeSum(nums))
7、接雨水
暴力解:很明显每个柱子顶部可以储水的高度为,该柱子的左右两侧最大高度的较小者减去此柱子的高度。因此我们只需要遍历每个柱子,累加每个柱子可以储水的高度即可。
双指针:
class Solution(object): # 暴力解,时间会超时 o(n^2)def trap(self, height):# 很明显每个柱子顶部可以储水的高度为:该柱子的左右两侧最大高度的较小者减去此柱子的高度。# 因此我们只需要遍历每个柱子,累加每个柱子可以储水的高度即可。""":type height: List[int]:rtype: int"""nums = 0for i in range(1, len(height) - 1):max_l = 0max_r = 0for j in range(0, i):max_l = max(height[j], max_l)for j in range(i + 1, len(height)):max_r = max(height[j], max_r)tmp = min(max_r, max_l) - height[i]if tmp < 0:tmp = 0nums += tmpreturn numsdef trap_2(self, height): # 双指针解,有点无法解释...# 1、初始左右指针在数组的第一个位置和最后一个位置# 2、如果左指针所指柱子的高度小于右指针所指柱子的高度,那么左右指针之间柱子的接雨水量取决于左指针所指柱子的高度,从左向右移动左指针,# 并记录柱子高度的最大值,如果当前柱子高度小于最大值,则当前柱子可以接雨水为柱子高度的最大值减去当前柱子高度# 3、否则,如果右指针所指柱子的高度小于左指针所指柱子的高度,那么左右指针之间柱子的接雨水量取决于右指针所指柱子的高度# 从右向左移动右指针,并记录柱子高度的最大值# 如果当前柱子高度小于最大值,则当前柱子可以接雨水为柱子高度的最大值减去当前柱子高度# 4.重复步骤2, 3""":type height: List[int]:rtype: int"""ans = 0left, right = 0, len(height) - 1leftMax = rightMax = 0while left < right:leftMax = max(leftMax, height[left])rightMax = max(rightMax, height[right])if height[left] < height[right]:ans += leftMax - height[left]left += 1else:ans += rightMax - height[right]right -= 1return ansif __name__ == '__main__':height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]s = Solution()# print(s.trap(height))print(s.trap_2(height))
三、滑动窗口
8、无重复字符的最长子串
class Solution(object):def lengthOfLongestSubstring(self, s):""":type s: str:rtype: int"""if len(s) == 0:return 0if len(s) == 1:return 1data_list = list(s)result = []for i in range(len(data_list) - 1):tmp = data_list[i]for dst in range(i, len(data_list) - 1):if data_list[dst + 1] in tmp:result.append(set(tmp))breakelse:tmp += data_list[dst+1]if dst + 1 == len(data_list) - 1:result.append(set(tmp))return len(max(result, key=len))if __name__ == '__main__':data = "abcabcbb"# data = "bbbbb"# data = "pwwkew"# data = " "# data = "au"# data = "cdd"# data = "abcabcbb"s = Solution()print(s.lengthOfLongestSubstring(data))
9、找出字符串中所有字母异位词
逻辑:遍历s字符串中,当窗口大小与P_len一样时,比较两个窗口的字母出现的次数,相同即保留相应的下标;
假设示例:
循环遍历s字符串,i经过两轮循环,窗口往右走,窗口扩大一次,相应schar的字母次数值就要+1,当窗口长度扩大至2(i=1),此时scharpchar,保留i - p_len + 1 (+1是因为i是从0开始) = 0的下标;
继续遍历,i=2,此时i>=p_len,超出p的长度了,要减去第一个进入窗口的字母次数值,schar={‘a’:1,‘b’:1},此时scharpchar,保留i - p_len + 1 (+1是因为i是从0开始) = 1的下标;
继续遍历,i=3,重复第三张图操作,b值减1,b变为0,要从计数中删除;
此时schar==pchar,保留i - p_len + 1 (+1是因为i是从0开始) = 2的下标;
继续遍历,i=4,重复第三张图操作,a值减1,a变为0,要从计数中删除;
此时schar != pchar,不保留下标,遍历结束;
最终结果:[0, 1, 2]
class Solution(object): --排序暴力检索,会超时def findAnagrams(self, s, p):""":type s: str:type p: str:rtype: List[int]"""len_tar = len(p)target = sorted(p)result = []for i in range(len(s) - len_tar + 1):if target == sorted(s[i:i + len_tar]):result.append(i)return resultclass Solution:def findAnagrams(self, s, p): -- 统计窗口出现次数实现方案,可实现""":type s: str:type p: str:rtype: List[int]"""from collections import Counters_len, p_len = len(s), len(p)pChar, sChar = Counter(p), Counter()result = []for i in range(s_len):sChar[s[i]] += 1if i >= p_len:sChar[s[i - p_len]] -= 1if sChar[s[i - p_len]] == 0:del sChar[s[i - p_len]]if pChar == sChar:result.append(i - p_len + 1)return resultif __name__ == '__main__':# s = "cbaebabacd"# p = "abc"s = "abab"p = "ab"# s = "abacbabc"# p = "abc"so = Solution()print(so.findAnagrams(s, p))
四、子串
太难了,不会做
五、普通数组
1、最大子数组和(中等,人生哲理题)
# 该题双循环不用看了,必超时
# 核心是从前往后遍历,例如-2和1的sum,与1相比,1比-1大,pre就取1,依次与后一位数字比较
# 人生哲理是这行:pre = max(data, pre + data):后面的人与前一个max的人在一起,如果能变优秀,那么我就和它在一起过余生,否则还不如后面的人自己过日子;
class Solution(object):def maxSubArray(self, nums):""":type nums: List[int]:rtype: int"""pre = 0max_sum = nums[0]for data in nums:pre = max(data, pre + data)max_sum = max(pre, max_sum)return max_sumif __name__ == '__main__':so = Solution()nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4]# nums = [-2, 1]print(so.maxSubArray(nums))
2、合并区间(中等)
class Solution:def merge(self, intervals):if len(intervals) == 0:return []res = []intervals.sort(key=lambda x: x[0]) # 先按区间左边界值由小到大排序for inter in intervals:if len(res) == 0 or res[-1][1] < inter[0]: # 如果结果集最后一个元素的右边界比新加入区间的左边界小,直接加入结果集res.append(inter)else: # 否则,说明新加入的和结果集最后一个区间有重合,更新区间右边界即可res[-1][1] = max(res[-1][1], inter[1])return resif __name__ == '__main__':so = Solution()intervals = [[6, 20], [1, 3], [2, 6], [5, 10], [15, 18]]print(so.merge(intervals))
3、轮转数组
class Solution(object):def rotate(self, nums, k):""":type nums: List[int]:type k: int:rtype: None Do not return anything, modify nums in-place instead."""# 对k求余是因为k可能会大于数组的长度,直接旋转会导致数组元素重叠。通过取模运算可以确保k不会超过数组的长度,从而避免数组元素重叠的问题。k = k % len(nums)# 为什么不从k开始取,因为nums下标是从0开始的,k的值余下标实际是差1的,不利于定位# 而用负下标,-1代表就是最后一个元素,符合k的数值定义# 以后尽量都用负下表定位会简单很多nums[:] = nums[-k:] + nums[:-k]return numsif __name__ == '__main__':nums = [1, 2, 3, 4, 5, 6, 7]k = 6# nums = [-1, -100, 3, 99]# k = 2# nums = [1, 2]# k = 1# k = 2# k = 3so = Solution()print(so.rotate(nums, k))
除自身以外数组的乘积
分别使用pre和last两个列表存储每个位置对应的前缀乘积和后缀乘积;
之后相乘即可,直接两个for循环会超时;
class Solution(object):def productExceptSelf(self, nums):""":type nums: List[int]:rtype: List[int]"""answer = []# 前缀乘积pre = [1]tmp_pre = 1for i in range(len(nums) - 1):tmp_pre = tmp_pre * nums[i]pre.append(tmp_pre)# 后缀乘积last = [1]tmp_last = 1for i in range(len(nums) - 1):tmp_last = tmp_last * nums[len(nums) - i - 1]last.append(tmp_last)# 相乘for i in range(len(nums)):answer.append(pre[i] * last[len(nums) - i - 1])return answerif __name__ == '__main__':so = Solution()# nums = [1, 2, 3, 4]nums = [-1,1,0,-3,3]print(so.productExceptSelf(nums))
六、二叉树
1、中序遍历:
class Solution(object):def inorderTraversal(self, root):""":type root: TreeNode:rtype: List[int]"""res = []def dfs(root):if not root:return# 按照 左-打印-右的方式遍历dfs(root.left)res.append(root.val)dfs(root.right)dfs(root)return res
class Solution(object):def inorderTraversal(self, root):"""莫里斯遍历"""res = []pre = Nonewhile root:# 如果左节点不为空,就将当前节点连带右子树全部挂到# 左节点的最右子树下面if root.left:pre = root.leftwhile pre.right:pre = pre.rightpre.right = root# 将root指向root的lefttmp = rootroot = root.lefttmp.left = None# 左子树为空,则打印这个节点,并向右边遍历else:res.append(root.val)root = root.rightreturn res
2、二叉树最大深度
class Solution:def maxDepth(self, root):if root is None:return 0else:left_height = self.maxDepth(root.left)right_height = self.maxDepth(root.right)return max(left_height, right_height) + 1
3、翻转二叉树
class Solution(object):def invertTree(self, root):# 递归函数的终止条件,节点为空时返回if not root:return None# 将当前节点的左右子树交换root.left, root.right = root.right, root.left# 递归交换当前节点的 左子树和右子树self.invertTree(root.left)self.invertTree(root.right)# 函数返回时就表示当前这个节点,以及它的左右子树# 都已经交换完了return rootclass Solution2(object):def invertTree(self, root):# 迭代方式if not root:return None# 将二叉树中的节点逐层放入队列中,再迭代处理队列中的元素queue = [root]while queue:# 每次都从队列中拿一个节点,并交换这个节点的左右子树tmp = queue.pop(0)tmp.left, tmp.right = tmp.right, tmp.left# 如果当前节点的左子树不为空,则放入队列等待后续处理if tmp.left:queue.append(tmp.left)# 如果当前节点的右子树不为空,则放入队列等待后续处理if tmp.right:queue.append(tmp.right)# 返回处理完的根节点return root
4、对称二叉树
class Solution(object):def isSymmetric(self, root):if not root:return Truedef dfs(left, right):# 递归的终止条件是两个节点都为空# 或者两个节点中有一个为空# 或者两个节点的值不相等if not (left or right):return Trueif not (left and right):return Falseif left.val != right.val:return False# 分别比较左子节点的左子节点与右子节点的右子节点、左子节点的右子节点与右子节点的左子节点,如果两对节点都对称,则返回True,否则返回False。return dfs(left.left, right.right) and dfs(left.right, right.left)# 用递归函数,比较左节点,右节点return dfs(root.left, root.right)
5、二叉树的直径
# 返回二元 tuple (depth, diameter)
# 二叉树的最长路径=max{左子树的最长路径,右子树的最长路径,经过根结点的最长路径}
# 经过根结点的最长路径 = 左子树的深度+右子树的深度
# 所以二叉树的最长路径=max{左子树的最长路径,右子树的最长路径,左子树的深度+右子树的深度}
# depth 表示子树的最大深度,diameter 表示子树的最长路径(直径)
class Solution(object):def diameterOfBinaryTree(self, root):def traverse(root):if root is None:return (0, 0)left_depth, left_diam = traverse(root.left)right_depth, right_diam = traverse(root.right)# 求二叉树深度的常规方法depth = 1 + max(left_depth, right_depth)# 套用上面推导出的最长路径公式diam = max(left_diam, right_diam, left_depth + right_depth)return depth, diamdepth, diam = traverse(root)return diam
6、二叉树层序遍历
class Solution(object):def levelOrder(self, root):if not root:return []res = []queue = [root]while queue:# 获取当前队列的长度,这个长度相当于 当前这一层的节点个数size = len(queue)tmp = []# 将队列中的元素都拿出来(也就是获取这一层的节点),放到临时list中# 如果节点的左/右子树不为空,也放入队列中for _ in range(size):r = queue.pop(0)tmp.append(r.val)if r.left:queue.append(r.left)if r.right:queue.append(r.right)# 将临时list加入最终返回结果中res.append(tmp)return res# 首先,定义一个用于构建二叉树的简单类
class TreeNode:def __init__(self, x):self.val = xself.left = Noneself.right = None# 构建一棵二叉树
def build_binary_tree():root = TreeNode(1)root.left = TreeNode(2)root.right = TreeNode(3)root.left.left = TreeNode(4)root.left.right = TreeNode(5)root.right.left = TreeNode(6)return root# 测试代码
solution = Solution()# 创建测试用的二叉树
test_tree = build_binary_tree()# 调用levelOrder方法并打印结果
result = solution.levelOrder(test_tree)
print("层序遍历的结果:", result)# 预期结果是每一层节点值组成的列表
expected_result = [[1], [2, 3], [4, 5, 6]]
assert result == expected_result, "测试失败,实际结果与预期结果不符"
7、将有序数组转为二叉搜索树
# 二叉搜索树满足两个条件:
# 1)根比左子树大比右子树小;
# 2)高度平衡。因此直接获取有序数组中值作为根就可以,递归生成左子树和右子树即可class Solution(object):def sortedArrayToBST(self, nums):""":type nums: List[int]:rtype: TreeNode"""if len(nums)==0: returnelif len(nums)==1: return TreeNode(nums[0]) # 这个TreeNode会报错,但是不影响在leetcode的判断mid = len(nums)//2node = TreeNode(nums[mid])node.left = self.sortedArrayToBST(nums[:mid])node.right = self.sortedArrayToBST(nums[mid+1:])return node
七、链表
1、合并两个有序链表
class Solution:def mergeTwoLists(self, l1, l2):if l1 is None:return l2elif l2 is None:return l1elif l1.val < l2.val:l1.next = self.mergeTwoLists(l1.next, l2)return l1else:l2.next = self.mergeTwoLists(l1, l2.next)return l2# 执行不了
# if __name__ == '__main__':
# l1 = [1, 2, 4]
# l2 = [1, 3, 4]
# so = Solution
# so.mergeTwoLists(l1, l2)
2、相交链表
# 暴力解:o(n^2)
# class Solution(object):
# def getIntersectionNode(self, headA, headB):
# p = headA
# while p:
# q = headB
# while q:
# if p == q:
# return p
# q = q.next
# p = p.next
# return p# 哈希表:先将其中一个链表存到哈希表中,此时再遍历另外一个链表查找重复结点只需 O(1) 时间
class Solution(object):def getIntersectionNode(self, headA, headB):s = set()p, q = headA, headBwhile p:s.add(p)p = p.nextwhile q:if q in s:return qq = q.nextreturn None
3、反转链表
# 双指针迭代
class Solution(object):def reverseList(self, head):""":type head: ListNode:rtype: ListNode"""# 申请两个节点,pre和 cur,pre指向Nonepre = Nonecur = headwhile cur:# 记录当前节点的下一个节点tmp = cur.next# 然后将当前节点指向precur.next = pre# pre和cur节点都前进一位pre = curcur = tmpreturn pre
4、回文链表
# 先将链表数据存入到数组arr, 双指针i,j分别从第一个和最后一个从前往后和从后往前遍历
# 相等就继续遍历,直至i > j,如果i和j指向的值不相等,就判断不是回文
class Solution(object):def isPalindrome(self, head):""":type head: ListNode:rtype: bool"""if not (head and head.next):return Truearr, i = [], 0# 申请一个数组,然后把元素都放到数组中while head:_, head = arr.append(head.val), head.nextj = len(arr) - 1# 用i和j两个指针,一个往后,一个往前,不断迭代# 如果i的值不等于j说明不是回文,反之是回文while i < j:if arr[i] != arr[j]:return Falsei, j = i + 1, j - 1return True
5、环形链表
# # 哈希:我们可以利用set这个数据结构来解决这道题,首先定义一个Set。
# # 之后遍历链表的节点,每遍历一个节点,就将这个节点的元素放入set中,如果这个链表没有环,那么最终遍历就结束了。
# # 如果链表有环的话,那么肯定有一个元素会被访问两次,当第二次访问这个元素的时候,set中就有记录了,这样就可以判断出链表是否有环了。
# class Solution(object):
# def hasCycle(self, head):
# # 定义一个set,然后不断遍历链表
# s = set()
# while head:
# # 如果某个节点在set中,说明遍历到重复元素了,也就是有环
# if head in s:
# return True
# s.add(head)
# head = head.next
# return False# 快慢指针解法
# 按照这个思路,我们可以假设有a和b两个指针,一个慢一个快,如果链表是有环状的,那么走的快的那个指针迟早会跟慢指针重合的
class Solution(object):def hasCycle(self, head):""":type head: ListNode:rtype: bool"""if not (head and head.next):return False# 定义两个指针i和j,i为慢指针,j为快指针i, j = head, head.nextwhile j and j.next:if i == j:return True# i每次走一步,j每次走两步i, j = i.next, j.next.nextreturn False
6、两数相加
# 我们不断的遍历两个链表,每次遍历都将链表a和链表b的值相加,再赋给链表a。如果有进位我们还需要记录一个进位标志。
# 循环的条件是链表a不为空或者链表b不为空,这样当整个循环结束时,链表就被串起来了。
# 当循环结束时,如果进位标志>0还需要处理下边界条件。
# 我们不用生成一个新的节点,直接将两个节点相加的值赋给节点a就可以了,这样只用改变节点的内容,速度会更快一些。class Solution(object):def addTwoNumbers(self, l1, l2):# 定义一个进位标志a, b, p, carry = l1, l2, None, 0while a or b:# a和b节点的值相加,如果有进位还要加上进位的值val = (a.val if a else 0) + (b.val if b else 0) + carry# 根据val判断是否有进位,不管有没有进位,val都应该小于10carry, val = val / 10 if val >= 10 else 0, val % 10p, p.val = a if a else b, val# a和b指针都前进一位a, b = a.next if a else None, b.next if b else None# 根据a和b是否为空,p指针也前进一位p.next = a if a else b# 不要忘记最后的边界条件,如果循环结束carry>0说明有进位需要处理这个条件if carry:p.next = ListNode(carry)# 每次迭代实际上都是将val赋给a指针的,所以最后返回的是l1return l1
八、二分查找
1、搜索插入位置
class Solution:def searchInsert(self, nums, target):left, right = 0, len(nums) # 采用左闭右开区间[left,right)while left < right: # 右开所以不能有=,区间不存在mid = left + (right - left) // 2 # 防止溢出, //表示整除if nums[mid] < target: # 中点小于目标值,在右侧,可以得到相等位置left = mid + 1 # 左闭,所以要+1else:right = mid # 右开,真正右端点为mid-1return left # 此算法结束时保证left = right,返回谁都一样if __name__ == '__main__':nums = [1, 2, 3, 4, 5, 6, 8]target = 5so= Solution()print(so.searchInsert(nums, target))
2、搜索二维矩阵
# # 暴力解:直接遍历
# class Solution(object):
# def searchMatrix(self, matrix, target):
# """
# :type matrix: List[List[int]]
# :type target: int
# :rtype: bool
# """
# M, N = len(matrix), len(matrix[0])
# for i in range(M):
# for j in range(N):
# if matrix[i][j] == target:
# return True
# return False# 如果 target 大于这一行的末尾元素,那么target 一定不在这一行中,只能出现在矩阵的下面的行中。
# 那么,假如 target < matrix[i][N - 1]时,说明 target 可能在本行中出现,而且由于下面各行的元素都大于 matrix[i][N - 1] ,
# 所以,不可能在下面的行中出现。此时,可以在本行中使用顺序查找,或者二分查找。
class Solution(object):def searchMatrix(self, matrix, target):M, N = len(matrix), len(matrix[0])for i in range(M):if target > matrix[i][N - 1]:continueif target in matrix[i]:return Truereturn Falseif __name__ == '__main__':matrix = [[1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 60]]so = Solution()print(so.searchMatrix(matrix, 3))
3、在排序数组中找到元素的第一个位置和最后一个位置
# 找到相同的元素即为起始索引,向后一直找到终止索引
class Solution:def searchRange(self, nums, target):for i in range(len(nums)):if nums[i] == target:j = i + 1while j < len(nums) and nums[j] == target:j += 1return [i, j-1]return [-1, -1]if __name__ == '__main__':nums = [5, 7, 7, 8, 8, 10]target = 8so = Solution()print(so.searchRange(nums, target))
九、矩阵
1、矩阵置0
# 遍历一次矩阵,记录一下每行、列是否出现了 0,分别记录行值和列值;
# 第二次遍历,如果行值或者列值出现在去重集合中,则将矩阵所在点的值置0
class Solution:def setZeroes(self, matrix):if not matrix or not matrix[0]:returnM, N = len(matrix), len(matrix[0])row, col = set(), set()for i in range(M):for j in range(N):if matrix[i][j] == 0:row.add(i)col.add(j)for i in range(M):for j in range(N):if i in row or j in col:matrix[i][j] = 0if __name__ == '__main__':matrix = [[1, 1, 1], [1, 0, 1], [1, 1, 1]]so =Solution()print(so.setZeroes(matrix))
10、栈
1、有效括号
# 通过栈,使用右括号匹配左括号
class Solution:def isValid(self, s):dic = {')': '(', ']': '[', '}': '{'}stack = []for i in s:if stack and i in dic:if stack[-1] == dic[i]:stack.pop()else:return Falseelse:stack.append(i)# 使用not stack 代替 True可以减少计算S只有一位的值的测试数据return not stackif __name__ == '__main__':so = Solution()# s = "()[]{}"s = "{()[()]}"print(so.isValid(s))
11、贪心算法
1、股票买卖的最佳时期
class Solution:def maxProfit(self, prices: List[int]) -> int:minprice = float('inf')maxprofit = 0for price in prices:minprice = min(minprice, price)maxprofit = max(maxprofit, price - minprice)return maxprofit
12、动态规划
1、杨辉三角
class Solution:def generate(self, numRows: int):triangle = []for i in range(numRows):row = [1] * (i + 1)if i >= 2:for j in range(1, i):row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j]triangle.append(row)return triangleif __name__ == '__main__':numRows = 5so = Solution()print(so.generate(numRows))
13、技巧
1、只出现一次的数字
# 异或解法:异或运算满足交换律,a^b^a=a^a^b=b,因此ans相当于nums[0]^nums[1]^nums[2]^nums[3]^nums[4].....
# 然后再根据交换律把相等的合并到一块儿进行异或(结果为0),然后再与只出现过一次的元素进行异或,这样最后的结果就是,只出现过一次的元素(0^任意值=任意值)
class Solution(object):def singleNumber(self, nums):""":type nums: List[int]:rtype: int"""ans = nums[0]if len(nums) > 1:for i in range(1, len(nums)):ans = ans ^ nums[i]return ansif __name__ == '__main__':# nums = [2, 2, 1]# nums = [2, 2, 1, 1, 5]nums = [2, 3, 4, 3, 4]so = Solution()print(so.singleNumber(nums))
2、多数元素
# 时间复杂度O(nlogn)
# class Solution(object):
# def majorityElement(self, nums):
# """
# :type nums: List[int]
# :rtype: int
# """
# nums.sort()
# return nums[len(nums)//2]# 时间复杂度:O(n),空间复杂度:O(M)
class Solution(object):def majorityElement(self, nums):dict = {}for i in nums:if i in dict:dict[i] = dict[i] + 1else:dict[i] = 1maxval = max(dict.values())# print(maxval)for key, val in dict.items():if dict[key] == maxval:return keyif __name__ == '__main__':# nums = [2, 2, 1, 1, 1, 2, 2]nums = [3, 3, 4]so = Solution()print(so.majorityElement(nums))