Leetcode 买卖股票的最佳时机 II
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution : def maxProfit (self, prices: List [int ] ) -> int : n = len (prices) @cache def dfs (i, own ): if i>=n: if own: return float ("-inf" ) else : return 0 if own: return max (dfs(i+1 , True ), dfs(i+1 , False )+prices[i]) else : return max (dfs(i+1 , True ) - prices[i], dfs(i+1 , False )) ans = dfs(0 , False ) return ans
有效的括号
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution : def isValid (self, s: str ) -> bool : n = len (s) if n%2 : return False match = {")" : "(" , "]" : "[" , "}" : "{" } st = [] for c in s: if c not in match : st.append(c) elif not st or st.pop()!=match [c]: return False return len (st) == 0
环形链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution : def hasCycle (self, head: Optional [ListNode] ) -> bool : if not head or not head.next : return False slow = fast = head while fast and fast.next : slow = slow.next fast = fast.next .next if slow == fast: return True return False
全排列 II
注意要sort再全排列避免重复,如果相同项没用就先都不用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution : def permuteUnique (self, nums: List [int ] ) -> List [List [int ]]: nums.sort() n = len (nums) ans = [] path = [0 ] * n on_path = [False ] * n def dfs (i ): if i == n: ans.append(path.copy()) return for j, on in enumerate (on_path): if on: continue if j > 0 and nums[j] == nums[j - 1 ] and not on_path[j - 1 ]: continue path[i] = nums[j] on_path[j] = True dfs(i + 1 ) on_path[j] = False dfs(0 ) return ans
接雨水
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution : def trap (self, height: List [int ] ) -> int : left, right = 0 , len (height) - 1 leftmax, rightmax = 0 , 0 ans = 0 while left < right: leftmax = max (leftmax, height[left]) rightmax = max (rightmax, height[right]) if leftmax < rightmax: ans += leftmax - height[left] left+=1 else : ans += rightmax - height[right] right-=1 return ans
相交链表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution : def getIntersectionNode (self, headA: ListNode, headB: ListNode ) -> ListNode: nodeA = headA nodeB = headB while nodeA != nodeB: nodeA = nodeA.next if nodeA else headB nodeB = nodeB.next if nodeB else headA return nodeA
仅执行一次字符串交换能否使两个字符串相等
最多交换 s2 中两个字符一次,能不能变成 s1,那必须刚好有 两个位置不同 ,并且交叉相等
1 2 3 4 5 6 7 8 9 10 11 12 class Solution : def areAlmostEqual (self, s1: str , s2: str ) -> bool : diff = [] for i in range (len (s1)): if s1[i]!=s2[i]: diff.append(i) if len (diff) == 0 : return True if len (diff) !=2 : return False i, j = diff return s1[i] == s2[j] and s1[j] == s2[i]
最长递增子序列
dp数组含义是以nums[i]结尾的最长递增子序列长度
1 2 3 4 5 6 7 8 9 10 11 class Solution : def lengthOfLIS (self, nums: List [int ] ) -> int : n = len (nums) if n == 0 : return 0 dp = [1 ] * n for i in range (n): for j in range (i): if nums[j] < nums[i]: dp[i] = max (dp[i], dp[j]+1 ) return max (dp)
寻找两个正序数组的中位数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution : def findMedianSortedArrays (self, nums1: List [int ], nums2: List [int ] ) -> float : if len (nums1) > len (nums2): nums1, nums2 = nums2, nums1 m, n = len (nums1), len (nums2) total = m + n half = (total + 1 ) // 2 left, right = -1 , m + 1 while left + 1 < right: i = (left + right) // 2 j = half - i nums1_left = nums1[i - 1 ] if i > 0 else float ("-inf" ) nums1_right = nums1[i] if i < m else float ("inf" ) nums2_left = nums2[j - 1 ] if j > 0 else float ("-inf" ) nums2_right = nums2[j] if j < n else float ("inf" ) if nums1_left <= nums2_right and nums2_left <= nums1_right: if total % 2 : return max (nums1_left, nums2_left) else : return ( max (nums1_left, nums2_left) + min (nums1_right, nums2_right) ) / 2 elif nums1_left > nums2_right: right = i else : left = i
数组中的第K个最大元素
小根堆
1 2 3 4 5 6 7 8 class Solution : def findKthLargest (self, nums: List [int ], k: int ) -> int : heap = [] for num in nums: heapq.heappush(heap,num) if len (heap) > k: heapq.heappop(heap) return heap[0 ]
使数组按非递减顺序排列 (重点多理解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution : def totalSteps (self, nums: List [int ] ) -> int : stack = [] ans = 0 for x in nums: step = 0 while stack and stack[-1 ][0 ] <= x: step = max (step, stack[-1 ][1 ]) stack.pop() if stack: step += 1 else : step = 0 ans = max (ans, step) stack.append((x, step)) return ans
快速排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Solution : def quickSort (self, nums: List [int ], l: int , r: int ) -> None : if l >= r: return x = nums[r] i = l for j in range (l, r): if nums[j] <= x: nums[i], nums[j] = nums[j], nums[i] i += 1 nums[i], nums[r] = nums[r], nums[i] self .quickSort(nums, l, i - 1 ) self .quickSort(nums, i + 1 , r)
冒泡排序
1 2 3 4 5 6 7 8 9 10 11 12 13 def bubble_sort (arr ): n = len (arr) for i in range (n): swapped = False for j in range (n-1 -i): if arr[j] > arr[j+1 ]: arr[j], arr[j+1 ] = arr[j+1 ],arr[j] swapped = True if not swapped: break return arr
平衡二叉树
1 2 3 4 5 6 7 8 9 10 11 12 class Solution : def isBalanced (self, root: Optional [TreeNode] ) -> bool : def dfs (node ): if node is None : return 0 left = dfs(node.left) right = dfs(node.right) if left == -1 or right == -1 or abs (left-right)>1 : return -1 return max (left, right) + 1 return dfs(root) != -1
把-1作为标识传递
零钱兑换
完全背包问题
1 2 3 4 5 6 7 8 9 10 class Solution : def coinChange (self, coins: List [int ], amount: int ) -> int : dp = [float ("inf" )] * (amount+1 ) n = len (coins) dp[0 ] = 0 for coin in coins: for x in range (coin, amount + 1 ): dp[x] = min (dp[x], dp[x - coin] + 1 ) return dp[amount] if dp[amount]!=float ("inf" ) else -1
拼写单词
1 2 3 4 5 6 7 8 9 10 class Solution : def countCharacters (self, words: List [str ], chars: str ) -> int : count_c = Counter(chars) ans = 0 for s in words: if Counter(s) <= count_c: ans += len (s) return ans
最长回文子串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution : def longestPalindrome (self, s: str ) -> str : n = len (s) ans_left = 0 ans_right = 0 for i in range (n): left = right = i while left>=0 and right<n and s[left] == s[right]: left-=1 right+=1 if right - left - 1 > ans_right - ans_left: ans_right = right ans_left = left+1 for i in range (n-1 ): left = i right = i+1 while left>=0 and right<n and s[left] == s[right]: left-=1 right+=1 if right - left - 1 > ans_right - ans_left: ans_right = right ans_left = left+1 return s[ans_left:ans_right]
丑数
1 2 3 4 5 6 7 8 9 class Solution : def isUgly (self, n: int ) -> bool : if n <= 0 : return False while n % 3 == 0 : n //= 3 while n % 5 == 0 : n //= 5 return n & (n - 1 ) == 0
知道位运算可以判断2的幂次
腐烂的橘子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution : def orangesRotting (self, grid: List [List [int ]] ) -> int : m, n = len (grid), len (grid[0 ]) fresh = 0 q = deque() for i in range (m): for j in range (n): if grid[i][j] == 1 : fresh +=1 elif grid[i][j] == 2 : q.append((i,j)) if fresh == 0 : return 0 if len (q) == 0 : return -1 directions = [(-1 ,0 ),(1 ,0 ),(0 ,-1 ),(0 ,1 )] ans = 0 while q and fresh > 0 : per = len (q) for _ in range (per): i, j = q.popleft() for di, dj in directions: new_i = i + di new_j = j + dj if 0 <= new_i < m and 0 <= new_j < n and grid[new_i][new_j] == 1 : grid[new_i][new_j] = 2 fresh -= 1 q.append((new_i, new_j)) ans +=1 return ans if fresh == 0 else -1
最大数
1 2 3 4 5 6 7 8 9 class Solution : def largestNumber (self, nums: List [int ] ) -> str : nums = list (map (str , nums)) nums.sort( key=cmp_to_key( lambda a, b: int (b + a) - int (a + b) ) ) return "0" if nums[0 ] == "0" else '' .join(nums)
机器学习 AUC曲线 基于二分类模型的 ROC 曲线点 (FPR, TPR),使用 梯形积分法(Trapezoidal Rule) 计算 AUC(Area Under Curve) $$ \mathrm{TPR}=\mathrm{Recall} = \frac{TP}{TP+FN} \qquad \mathrm{FPR}=\frac{FP}{FP+TN} $$ 衡量的是:模型把正样本排在负样本前面的能力,AUC 越大,说明模型整体排序能力越强
ROC 曲线横轴是FPR,纵轴是TPR
AUC
含义
1.0
完美排序,所有正样本得分都高于负样本
0.5
接近随机猜测
< 0.5
排序方向可能反了,模型倾向于把负样本排在正样本前面
给定一组按 FPR 递增排序 的点: $$ \left{ (\mathrm{FPR}_0, \mathrm{TPR}_0), (\mathrm{FPR}_1, \mathrm{TPR}_1), \ldots, (\mathrm{FPR}_n, \mathrm{TPR}_n) \right} $$ AUC 的梯形积分公式为: $$ \mathrm{AUC} \sum_{i=1}^{n} \frac{ \left(\mathrm{FPR}i-\mathrm{FPR} {i-1}\right) \left(\mathrm{TPR}i+\mathrm{TPR} {i-1}\right) }{2} $$ 在画 ROC 曲线时,不断降低分类阈值 threshold
阈值越低,被判为正类的样本越多,所以 TP 可能增加,FP 也可能增加,因此 TPR 和 FPR 都呈现非递减趋势
1 2 3 4 5 6 7 8 9 10 11 12 13 from typing import List def calculate_auc (self, FPR: List [float ], TPR: List [float ] ) -> float : """计算曲线下面积 (AUC) 的函数""" n = len (FPR) auc = 0.0 for i in range (1 , n): width = FPR[i] - FPR[i - 1 ] height = (TPR[i] + TPR[i - 1 ]) / 2 auc += width * height return auc
ROC 曲线整体向右上方走
因为 threshold 从高到低扫描时,预测为正的样本集合只会越来越大,不会变小
新加入正类集合的样本只有两种情况:
新加入的是正样本:$TP \uparrow \Rightarrow TPR \uparrow$,表现为 ROC 曲线 向上走
新加入的是负样本:$FP \uparrow \Rightarrow FPR \uparrow$,表现为 ROC 曲线 向右走