from typing importList nums=input().split() nums=list(map(int,nums)) classSolution: deflongestConsecutive(self,nums:List[int])->int: nums=set(nums) #将数组放入哈希表 ans=0 for x in nums: if x-1in nums: continue y=x+1 while y in nums: y+=1 ans=max(ans,y-x) return ans a=Solution() print(a.longestConsecutive(nums))
from typing importList nums=input().split() nums=list(map(int,nums)) classSolution: defthreeSum(self,nums:List[int])->list[list[int]]: nums.sort() ans=[] n=len(nums) if n<3: return ans if nums[0]>0: return ans for i inrange(n): if i>0and nums[i]==nums[i-1]: #固定第一个元素,跳过重复元素 continue L=i+1 R=n-1 while L<R: if nums[i]+nums[L]+nums[R]==0: ans.append([nums[i],nums[L],nums[R]]) while L<R and nums[L]==nums[L+1]: L+=1 while L<R and nums[R]==nums[R-1]: R-=1 R-=1 L+=1 elif nums[i]+nums[L]+nums[R]>0: R-=1 else: L+=1 return ans a=Solution() print(a.threeSum(nums)) ``` ## 4.接雨水 题目:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。 思路:以最高柱子为中心,向左右扩展,计算能接的雨水量,左测跟左边最高柱子,右侧跟右边最高柱子 ```python from typing importList height=input().split() height=list(map(int,height)) classSolution: deftrap(self,height:List[int])-> int: n=len(height) if n<3: return0 l,r=0,n-1 ans=0 max_left=height[0] max_right=height[-1] while l<r: if height[l]<height[r]: ans+=max_left-height[l] l+=1 max_left=max(max_left,height[l]) else: ans+=max_right-height[r] r-=1 max_right=max(max_right,height[r]) return ans a=Solution() print(a.trap(height))
classSolution: defgetIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode: a,b=headA,headB while a!=b: a=a.nextif a else headB b=b.nextif b else headA return a
2.链表反转
题目:给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 思路:双指针,改变next
1 2 3 4 5 6 7 8 9
classsolution: defreverseList(self,head:ListNode)->ListNode: a,b=head,None while a: t=a.next a.next=b b=a a=t return b
3.回文链表
题目:给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。 思路:堆栈,将链表压入堆栈,然后依次弹出,判断是否相等
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution: defisPalindrome(self,head:ListNode)->bool: stack=[] a=head while a: stack.append(a) a=a.next b=head while stack: c=stack.pop() if c.val!=b.val: returnFalse b=b.next returnTrue
4.环形链表
题目:给你一个链表的头节点 head ,判断链表中是否有环。 思路:哈希表存储,判断有无重复结点
1 2 3 4 5 6 7 8 9
classSolution: defhasCycle(self,head:ListNode)->bool a=set() #集合 set 本质就是去掉 value 的哈希表 while head: if head in a: returnTrue a.add(head) head=head.next returnFalse
classSolution: defaddTwoNumbers(self,l1:Optional[ListNode],l2:Optional[ListNode],carry=0)->Optional[ListNode]: if l1 isNoneand l2 isNoneand carry==0: returnNone s+=carry #上一位的进位值 if l1: s+=l1.val l1=l1.next if l2: s+=l2.val l2=l2.next return ListNode(s%10,self.addTwoNumbers(l1,l2,s//10)) #当前位的值,下一位的进位值
8.删除链表倒数第n个结点
题目:给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点 思路:双指针,快指针走n步,慢指针走,当快指针走到头时,慢指针就走到倒数第n个结点,删除慢指针的下一个结点
1 2 3 4 5 6 7 8 9 10
classSolution: defremoveNthFromEnd(self, head: ListNode, n: int) -> ListNode: left = right = dummy = ListNode(next=head) for _ inrange(n): right = right.next# 右指针先向右走 n 步 while right.next: left = left.next right = right.next# 左右指针一起走 left.next = left.next.next# 左指针的下一个节点就是倒数第 n 个节点 return dummy.next
题目:给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。 思路:在每个节点后面插入一个新节点,新节点的值与原节点相同,新节点的 next 指针指向原节点的 next 指针,新节点的 random 指针指向原节点的 random 指针的next
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution: defcopyRandomList(self,head: 'Optional[ListNode]') -> 'Optional[ListNode]': cur=head while cur: cur.next=ListNode(cur.val,cur.next) #在每个节点后面插入一个新节点 cur.next.random=cur.random cur=cur.next.next cur=head while cur: if cur.random: #新节点的random指针指向原节点的random指针的next cur.next.random=cur.random.next cur=cur.next.next cur=temp=Node(0,head) while cur.next: #将新节点从原链表中分离出来,遍历老节点 cur.next=cur.next.next# cur=cur.next return temp.next
classSolution: defmerge(self,first:Optional[ListNode],second:Optional[ListNode]): temp=ListNode(0) tail=temp while first and second: if first.val<second.val: #小的先放 tail.next=first first=first.next else: tail.next=second second=second.next tail=tail.next#tail指向新链表的最后一个节点 if first: tail.next=first #将链表剩余部分直接连接到新链表的末尾 else: tail.next=second return temp.next defsortList(self,head: Optional[ListNode])->Optional[ListNode]: if head isNoneor head.nextisNone: #链表为空或只有一个节点,直接返回 return head slow,fast=head,head.next#顺序不能反 while fast and fast.next:# 快指针走两步,慢指针走一步,当快指针走到头时,慢指针就走到中间 1 2 3 4 slow=slow.next fast=fast.next.next second=slow.next#第二个链表的头节点 slow.next=None#断开链表,将链表分为两个部分 first=head first=self.sortList(first) second=self.sortList(second) returnself.merge(first,second)
classSolution: definorderTraversal(self, root: TreeNode) -> List[int]: if root isNone: return [] stack=[] res=[] a=root while a or stack: while a: stack.append(a) a=a.left a=stack.pop() res.append(a.val) a=a.right return res
classSolution: defisSymmetric(self,root: Optional[TreeNode])->bool: ifnot root: returnTrue defifmirror(left,right): ifnot left andnot right: returnTrue ifnot left ornot right or left.val!=right.val: returnFalse return ifmirror(left.left,right.right) and ifmirror(left.right,right.left) return ifmirror(root.left,root.right)
classSolution: defrightSideView(self,root:Optional[TreeNode])->List[int]: res=[] ifnot root: return [] cur=[root] while cur: res.append(cur[-1].val) next1=[] for node in cur: if node.left: next1.append(node.left) if node.right: next1.append(node.right) cur=next1 return res
#二分查找 classSolution: defsearchInsert(self, nums: List[int], target: int) -> int: l, r = 0, len(nums) while l < r: mid = (l + r) // 2 if nums[mid] < target: l = mid + 1 else: r = mid return l
6. 滑动窗口
1. 最长无重复子串
给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。 思路:使用滑动窗口,窗口内无重复字符则更新最大长度,有重复字符则移动窗口的左边界,直到无重复字符。 白话:我们维护一个窗口 [left, right],满足硬性规则:✅ 窗口内所有字符,不存在重复,right 一直往右走(正常遍历字符串);一旦发现当前字符char已经存在窗口里面:就要把窗口左边界left挪到【上一次这个字符位置的下一位】,把旧的重复字符踢出窗口。 abca
1 2 3 4 5 6 7 8 9 10 11 12
classSolution: deflengthOfLongestSubstring(self, s: str) -> int: dic,res,i={},0,-1 for j inrange(len(s)): #取长度记得用len if s[j] indir: i=max(i,dic[s[j]]) dic[s[j]]=j res=max(res,j-i) return res sol = Solution() s = input("请输入字符串:") print(sol.lengthOfLongestSubstring(s))
2.找到字符串中所有字母异位词
题目:给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。 思路:滑动窗口,用字典统计字符个数是否相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from collections import Counter #统计字符个数 from typing importList s=input() p=input() classSolution: deffindAnagrams(self,s:str,p:str)->List[int]: dic_p=Counter(p) #Counter 是 dict 的子类,本质就是一个普通 Python 字典 {字符:出现次数},为空也不报错 dic_s=Counter() ans=[] for right, c inenumerate(s): #right为索引,c为字符 dic_s[c]+=1 left =right-len(p)+1#right-left+1=窗口大小 if left <0: continue if dic_s==dic_p: ans.append(left) dic_s[s[left]]-=1#字典里是放key,不是value return ans
7. 栈
1. 有效的括号
题目:给定一个只包括 ‘(‘,’)’,’{‘,’}’,’[‘,’]’ 的字符串 s ,判断字符串是否有效。 有效:左与右相同闭合,且必须以正确顺序闭合。 思路:用栈存储,是左括号就入栈,不是括号就弹出,最后判断栈是否为空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
s=input("请输入字符串:") classSolution: defisValid(self, s:str)->bool: dic={'(':')','[':']','{':'}'} stack=[] for c in s: if c in dic: stack.append(c) elifnot stack : returnFalse elif dic[stack.pop()]!=c: #栈空的时候绝对不能 pop returnFalse returnnot stack a=Solution() print(a.isValid(s))
8. 贪心算法
1.买卖股票的最佳时机
题目:给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。 思路:记录最低价格,每次更新最低价格,计算利润,更新最大利润
1 2 3 4 5 6 7 8 9 10 11 12 13 14
prices=input("请输入股票价格:") prices=list(map(int,prices.split())) #map:将字符串转换为整数=for s in str_list: prices.append(int(s)) #一个个转,map可以批量 classSolution: defmaxProfit(self, prices: List[int]) -> int: min_price=prices[0] res=0 for price in prices: min_price=min(min_price,price) profit=price-min_price res=max(profit,res) return res a=Solution() print(a.maxProfit(prices))
9. 动态规划
1.爬楼梯
题目:假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 思路:动态规划,dp[i]表示第i阶的方法数,dp[i]=dp[i-1]+dp[i-2],初始条件dp[0]=1,dp[1]=1,dp[2]=2
numRows=int(input()) classSolution: defgenerate(self,numRows: int)-> List[List[int]]: c= [[1]*(i+1)for i inrange(numRows)] #初始化杨辉三角全为1 for i inrange(2,numRows): for j inrange(1,i): c[i][j]=c[i-1][j-1]+c[i-1][j] return c a=Solution() print(a.generate(numRows))
10. 技巧算法
11.子串
1.和为k的子数组
题目:给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。子数组是数组中元素的连续非空序列。 思想:前缀和(s[0]=0,就不用对第一天特殊处理),记录每个元素的前缀和,用前缀和的差来判断是否有和为k的子数组,写成 s[j]+(−s[i])=k 就能看得更明白。优化:两数之和:哈希
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
nums=input().split() nums=list(map(int,nums)) k=int(input()) classSolution: defsubarraySum(self,nums:List[int])->int: cnt=defaultdict(int) cnt[0]=1#前缀和为0初始为1次,因为s[0]=0 ans=s=0 for num in nums: s+=num #当前位置前缀和 ans+=cnt[s-k] #查询有没有前缀和为s-k(离k还有多少)的子数组,有则加1,没有则加0 cnt[s]+=1#当前前缀和为s的次数加1 return ans a=Solution() print(a.subarraySum(nums))
nums=input() nums=list(map(int,nums.split())) classSolution: defsingleNumber(self,nums:List[int])->int: res=0 for num in nums: res^=num return res a=Solution() print(a.singleNumber(nums))
nums=input() nums=list(map(int,nums.split())) classSolution: defmajorityElement(self,nums:List[int])->int: vote =0 for num in nums: if vote==0: res=num if res==num: vote+=1 else: vote-=1 return res a=Solution() print(a.majorityElement(nums))
nums=input().split() nums=list(map(int,nums)) classSolution: defmaxSubArray(self,nums:List[int])->int: ans=nums[0] s=0 min_s=0#初始最小前缀和为0,因为s[0]=0,所以s[0]-min_s=s[0],就是s[0]的和 for num in nums: s+=num ans=max(ans,s-min_s) #当前前缀和与最小前缀和的差,就是当前子数组的和 min_s=min(min_s,s) return ans
from typing importList intervals=[] for i inrange(int(input())): a,b=map(int,input().split()) intervals.append([a,b]) classSolution: defmerge(self,intervals:List[List[int]])->List[List[int]]: intervals.sort(key=lambda x:x[0]) #按区间起点排序 res=[] for p in intervals: if res and res[-1][1]>=p[0]: #当前区间左端点小于等于上一个区间的右端点,说明有重叠 res[-1][1]=max(res[-1][1],p[1]) #合并区间,右端点取较大值 else: res.append(p) return res a=Solution() print(a.merge(intervals))
3.轮转数组
题目:给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。 思路:先将数组反转,再将前k个元素反转,最后将后n-k个元素反转。(交换子数组)
nums=input().split() nums=list(map(int,nums)) classSolution: defproductExceptSelf(self,nums:List[int])->List[int]: ans,tep=[1]*len(nums),1 for i inrange(1,len(nums)): #从第二个元素开始,前缀积为1 ans[i]=ans[i-1]*nums[i-1] for i inrange(len(nums)-2,-1,-1): #从倒数第二个元素开始,后缀积为1,步长为-1,(逆序遍历) tep*=nums[i+1] ans[i]*=tep return ans
13.矩阵
1.矩阵置零
题目:给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
classSolution: defsetZeroes(self,matrix:List[List[int]])->None: m,n=len(matrix),len(matrix[0]) first_row= 0in matrix[0] #第一行是否有0 first_col= any(row[0]==0for row in matrix) #第一列是否有0 for i inrange(1,m): for j inrange(1,n): if matrix[i][j]==0: matrix[i][0]=0#将当前元素所在行的第一个元素设为0 matrix[0][j]=0#将当前元素所在列的第一个元素设为0 for i inrange(1,m): for j inrange(1,n): if matrix[i][0]==0or matrix[0][j]==0: matrix[i][j]=0 if first_row: #单独处理第一行 for j inrange(n): matrix[0][j]=0 if first_col: #单独处理第一列 for row in matrix: row[0]=0
2.螺旋矩阵
题目:给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。 思路:边界控制,每次循环控制一个方向,直到边界重合。
classSolution: defspiralOrder(self,matrix:List[List[int]])->List[int]: ifnot matrix: return [] m,n=len(matrix),len(matrix[0]) res=[] top,bottom,left,right=0,m-1,0,n-1#上、下、左、右边界 whileTrue: for i inrange(left,right+1): res.append(matrix[top][i]) top+=1#上边界收缩 if top>bottom: break for i inrange(top,bottom+1): res.append(matrix[i][right]) right-=1#右边界收缩 if left>right: break for i inrange(right,left-1,-1): res.append(matrix[bottom][i]) bottom-=1#下边界收缩 if top>bottom: break for i inrange(bottom,top-1,-1): res.append(matrix[i][left]) left+=1#左边界收缩 if left>right: break return res
3.螺旋图像
题目:给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 思路:4个数字进行交换,四个角的数字交换,利用临时变量存储。
1 2 3 4 5 6 7 8 9 10
classSolution: defrotate(self,matrix:List[List[int]])->None: n=len(matrix) for i inrange(n//2): for j inrange((n+1)//2): temp=matrix[i][j] matrix[i][j]=matrix[n-j-1][i] #A=D matrix[n-1-j][i]=matrix[n-1-i][n-1-j] #D=C matrix[n-1-i][n-1-j]=matrix[j][n-1-i] #C=B matrix[j][n-1-i]=temp #B=A
4.搜索二维矩阵Ⅱ
题目:编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
classSolution: defremove(self, s: str) -> str: stack=[] for c in s: stack.append(c) iflen(stack)>=3: if stack[1]==stack[3] and stack[1]!=stack[2]: stack.pop() stack.pop() stack.pop() return"".join(stack)