leetcode最热100道

4. 寻找两个正序数组的中位数

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
思路: 二分查找两个数组中排序为k的值或k,k+1的均值,每次两个数组各取第前k/2个比较,小的那个以及后面都淘汰,该数组指数右移到后一位,循环操作,直到k==1或有一个数组淘汰完。

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        def getkth(k):
            offset1 = offset2 = 0
            while True:
            	# 终止条件,有一个数组到底,返回另一数组第k个值,k=1返回当前位置min
                if len(nums1) == offset1:
                    return nums2[offset2 + k - 1]
                if len(nums2) == offset2:
                    return nums1[offset1 + k - 1]
                if k == 1:
                    return min(nums1[offset1], nums2[offset2])
                # 计算新节点位置,注意控制边界
                noffset1 = min(offset1 + k//2 - 1, len(nums1) - 1)
                noffset2 = min(offset2 + k//2 - 1, len(nums2) - 1)
				# 比较两个值,k减去淘汰数,offset右移后一位
                if nums1[noffset1] < nums2[noffset2]:
                    k -= noffset1 - offset1 + 1
                    offset1 = noffset1 + 1
                else:
                    k -= noffset2 - offset2 + 1
                    offset2 = noffset2 + 1
        s = len(nums1) + len(nums2)
        if s % 2 == 0:
            return (getkth(s//2) + getkth(s//2 + 1))/ 2
        return getkth(s // 2 + 1)

5. 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。
思路: 动态规划二维数组i到j为回文则为True,

class Solution:
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        max_len, begin = 1, 0
        dp = [[False]*n for i in range(n)]

        for L in range(n):
            for left in range(n):
                if left + L >= n:
                    break
                right = left + L 
                if s[left] == s[right]:
                    if L <= 2:
                        dp[left][right] = True
                    else:
                        dp[left][right] = dp[left+1][right-1]
                    if dp[left][right] and right - left + 1 > max_len:
                        max_len = right - left + 1
                        begin = left
        return s[begin: begin + max_len]

11.盛最多水的容器

给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器。

class Solution:
    def maxArea(self, height: List[int]) -> int:
        j, i, res = len(height) - 1, 0, 0
        while i < j:
            if height[i] < height[j]:
                res = max(res, (j-i)*height[i])
                i += 1
            else:
                res = max(res, (j-i)*height[j])
                j -= 1
        return res

15.三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。
思路: 双指针收缩寻找和为0,找到和为0避免重复,比较后一数字是否相同,相同跳过。

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        if len(nums) < 3:
            return []
        nums.sort()
        ret = []
        for i in range(len(nums) - 2):
            # 提前结束
            if nums[i] > 0:
                break
            # 避免重复
            if i > 0 and nums[i] == nums[i-1]:
                continue
            L, R = i + 1, len(nums) - 1
            while L < R:
                s = nums[i] + nums[L] + nums[R]
                if s == 0:
                    ret.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
                    # 找到后收缩
                    L += 1
                    R -= 1
                elif s > 0:
                    R -= 1
                else:
                    L += 1
        return ret

17.电话号码的数字组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
在这里插入图片描述

class Solution:
    def letterCombinations(self, digits: str) -> List[str]:
        if not digits: return []
        phoneMap = {
            "2": "abc",
            "3": "def",
            "4": "ghi",
            "5": "jkl",
            "6": "mno",
            "7": "pqrs",
            "8": "tuv",
            "9": "wxyz",
        }
        ret, cached = [], []
        def dfs(k):
            if k == len(digits):
                ret.append("".join(cached))
                return
            for c in phoneMap[digits[k]]:
                cached.append(c)
                dfs(k + 1)
                cached.pop()
        dfs(0)
        return ret            

22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
思路: dfs,这里对左右括号数同时搜索,左括号比右括号少直接返回,小于n时放入括号,最后返回结果

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        if n <= 0:
            return []
        ret = []
        cached = []
        def dfs(l, r):
            if l == n and r == n:
                ret.append(''.join(cached))
                return 
            if l < r:
                return
            if l < n:
                cached.append('(')
                dfs(l + 1, r)
                cached.pop()
            if r < n:
                cached.append(')')
                dfs(l, r + 1)
                cached.pop()
        dfs(0, 0)
        return ret

23. 合并K个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。
思路: heapq模拟优先队列,现将每个链表头入堆,链表指针指向下一个,然后出队列,每次出将非空的当前链表下一个入队列,最后返回

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        prior = []
        root = head = ListNode(-1)
        for i in range(len(lists)):
            if lists[i]:
                heapq.heappush(prior, (lists[i].val, i))
                lists[i] = lists[i].next
        while prior:
            v, i = heapq.heappop(prior)
            root.next = ListNode(v)
            root = root.next
            if lists[i]:
                heapq.heappush(prior, (lists[i].val, i))
                lists[i] = lists[i].next
        return head.next

分治:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        n = len(lists)
        def merge(l1, l2):
            if not l1: return l2
            if not l2: return l1
            if l1.val < l2.val:
                l1.next = merge(l1.next, l2)
                return l1
            else:
                l2.next = merge(l1, l2.next)
                return l2
        
        def mergeList(left, right):
            if left == right:
                return lists[left]
            mid = left + (right - left) // 2
            l = mergeList(left, mid)
            r = mergeList(mid + 1, right)
            return merge(l, r)
        if not lists: return None
        return mergeList(0, len(lists) - 1)

25. K 个一组翻转链表

给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。

k 是一个正整数,它的值小于或等于链表的长度。

如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

进阶:

你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
在这里插入图片描述

思路: 记录pre和end(开始时相同),找到要交换的start(pre.next)和end(走k格),存储end.next,然后置为None(方便翻转),rev进行交换返回翻转后的头结点,pre接上这个节点,start接上保存的next,然后进入下个循环。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseKGroup(self, head: ListNode, k: int) -> ListNode:
        def rev(st):
            pre = None
            cur = st
            while cur != None:
                nxt = cur.next
                cur.next = pre
                pre = cur
                cur = nxt
            return pre
        L = ListNode(-1)
        L.next = head
        pre, end = L, L
        while end.next != None:
            for i in range(k):
                end = end.next
                if not end: break
            if not end: break
            nxt = end.next
            end.next = None
            sta = pre.next
            pre.next = rev(sta)
            sta.next = nxt
            pre = sta
            end = pre
        return L.next

31.下一个排列

实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。

如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。

必须 原地 修改,只允许使用额外常数空间。
在这里插入图片描述
从后面找到第一个升序位置,从后面找到第一个大与该位置值,进行交换,然后把后面的升序部分reverse

class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        for i in range(len(nums)-1,-1,-1):
            if i >= 1 and nums[i-1] < nums[i]:
                for j in range(len(nums)-1,i-1,-1):
                    if nums[j] > nums[i-1]:
                        nums[i-1], nums[j] = nums[j], nums[i-1]
                        break
                break
        l, r = i, len(nums) - 1
        while l < r:
            nums[l], nums[r] = nums[r], nums[l]
            l += 1
            r -= 1
        return

32.最长有效括号

给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度
**思路:**两次遍历,从左到右,计数左右括号,然后左右相等时取max,右大于左归0,但这样会漏掉一种情况,就是遍历的时候左括号的数量始终大于右括号的数量,即 ((), 所以要在进行一次,从右往左反之。

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        l = r = 0
        max_len = 0
        for i in range(len(s)):
            if s[i] == '(': l += 1
            else: r += 1
            if l == r: max_len = max(max_len, l + r)
            elif l < r: l = r = 0
        l = r = 0
        for i in range(len(s) - 1, -1, -1):
            if s[i] == '(': l += 1
            else: r += 1
            if l == r: max_len = max(max_len, l + r)
            elif l > r: l = r = 0
        
        return max_len

辅助栈,栈顶保存匹配前一个index

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        stack = [-1]
        max_len = 0
        for i,c in enumerate(s):
            if c == '(':
                stack.append(i)
            else:
                stack.pop()
                if stack:
                    max_len = max(i - stack[-1], max_len)
                else: stack.append(i)
        return max_len

动态规划:
遍历s,如果当前位置为’)’,前一位置为’(’,则记为前两个位置加2,若当前位置为’)’,前一位置为’)’,则进入未匹配位置(s[i - dp[i - 1] - 1])判断,若这个位置为’(’,匹配成功,当前位置 加上 dp[i - 1] 和 dp[i - dp[i - 1] - 2] 以及 2。

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        max_len = 0
        n = len(s)
        dp = [0]*n
        for i in range(1, n):
            if s[i] == ')':
                if s[i-1] == '(':
                    dp[i] = dp[i - 2] + 2 if i >= 2 else 2
                elif i > dp[i - 1] and s[i - dp[i - 1] - 1] == '(':
                    dp[i] = dp[i-1] + (dp[i - dp[i - 1] - 2] + 2 if i >= dp[i - 1] + 2 else 2)
                max_len = max(max_len, dp[i])
        return max_len

33.搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

二分:

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        l, r = 0, len(nums) - 1
        # 终止条件带等号
        while l <= r:
            mid = l + (r - l)//2
            if target == nums[mid]:
                return mid
            # 划分mid属于哪个区间 》= 0,都是前面
            if nums[mid] >= nums[0]:
                if nums[0] <= target < nums[mid]:
                    r = mid - 1
                else: l = mid + 1
            else:
                if nums[mid] < target <= nums[-1]:
                    l = mid + 1
                else: r = mid - 1
        return -1

72.编辑距离

给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

插入一个字符
删除一个字符
替换一个字符
动态规划:
d p [ i − 1 ] [ j ] 插 入 一 个 字 符 d p [ i ] [ j − 1 ] 删 除 一 个 字 符 d p [ i − 1 ] [ j − 1 ] 替 换 一 个 字 符 dp[i-1][j] 插入一个字符 \\ dp[i][j-1] 删除一个字符 \\ dp[i-1][j-1]替换一个字符 dp[i1][j]dp[i][j1]dp[i1][j1]

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m, n = len(word1), len(word2)
        if m * n == 0: return m + n
        dp = [[0]*(n + 1) for _ in range(m + 1)]
        for i in range(1, m + 1):
            dp[i][0] = dp[i-1][0] + 1
        for j in range(1, n + 1):
            dp[0][j] = dp[0][j-1] + 1
        for i in range(1, m + 1):
            for j in range(1, n + 1):
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1]
                else:
                    dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i][j-1])) + 1
        return dp[-1][-1]

76. 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
思路: 滑动窗口,前后两个指针构成滑动窗口,每次右移右指针,如果当前字符计数大于0,减去need计数,每次遍历都在cnt哈希表中减一,当need计数为0时,说明当前窗口已经包含了,这时要找最小窗口,开始循环左移左指针,直到左指针位置哈希表计数为0,说明当前位置不可删除,当前最小窗口找到了,跟之前比较去最小值,然后左移一位左指针,遍历下一个窗口。

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        cnt = collections.defaultdict(int)
        for c in t:
            cnt[c] += 1
        need = len(t)
        i = 0
        res = (0, len(s) + 1)
        for j in range(len(s)):
            if cnt[s[j]] > 0:
                need -= 1
            cnt[s[j]] -= 1
            if need == 0:
                while True:
                    if cnt[s[i]] == 0:
                        break
                    cnt[s[i]] += 1
                    i += 1
                if j - i < res[1] - res[0]:
                    res = (i, j)
                cnt[s[i]] += 1
                i += 1
                need += 1
        return  s[res[0]:res[1] + 1] if res[1] != len(s) + 1 else ''

148. 排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

进阶:

你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?

思路:
归并排序

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        slow = head
        fast = head.next
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
        mid, slow.next = slow.next, None
        l = self.sortList(head)
        r = self.sortList(mid)
        root = res = ListNode()
        while l and r:
            if l.val < r.val:
                root.next = l
                l = l.next
            else:
                root.next = r
                r = r.next
            root = root.next
        root.next = l if l else r
        return res.next

207.课程表

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。

思路: 判断图里有没有环,有环说明是不行的

dfs

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        dic = collections.defaultdict(set)
        for i, j in prerequisites:
            dic[j].add(i)
        visited = [0] * numCourses
        self.hasring = False
        def dfs(k):
            visited[k] = 1
            for node in dic[k]:
                if visited[node] == 1:
                    self.hasring=True
                    return
                elif visited[node] == 0:
                    dfs(node)
                if self.hasring: return
            visited[k] = 2
        for i in range(numCourses):
            if visited[i] == 0:
                dfs(i)
                if self.hasring: return False
        return True

bfs
记录每个节点的入度,入度为0可以访问进队列,记录访问数做判断

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        dic = collections.defaultdict(set)
        indegree = [0] * numCourses
        for i, j in prerequisites:
            dic[j].add(i)
            indegree[i] += 1
        visited = 0
        q = []
        for i in range(numCourses):
            if indegree[i] == 0:
                q.append(i)
        while q:
            k = q.pop(0)
            visited += 1
            for node in dic[k]:
                indegree[node] -= 1
                if indegree[node] == 0:
                    q.append(node)
            
        return visited == numCourses

215.数组中的第k个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

1.维护k个元素小顶堆,把大于堆顶元素入堆

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        h = []
        for i in range(k):
            heapq.heappush(h,nums[i])
        for i in range(k,len(nums)):
            if nums[i] > h[0]:
                heapq.heappushpop(h, nums[i])
        return h[0]

2.快排分治

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        def quick_sort(begin, end):
            if begin >= end: return
            i, j = begin, end
            tar = nums[begin]
            while i < j:
                while i < j and nums[j] <= tar:
                    j -= 1
                while i < j and nums[i] >= tar:
                    i += 1
                nums[i], nums[j] = nums[j], nums[i]
            nums[i], nums[begin] = nums[begin], nums[i]

            if i < k - 1:
                quick_sort(i+1, end)
            elif i > k - 1:
                quick_sort(begin, i - 1)
            return
        n=len(nums)
        quick_sort(0, n-1)
        return nums[k-1]

234.回文链表

请判断一个链表是否为回文链表。
反转其半部分再判定

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        slow = fast = head
        pre=prepre=None
        while fast and fast.next:
            fast = fast.next.next
            pre = slow
            slow = slow.next
            pre.next = prepre
            prepre = pre
        if fast: slow = slow.next
        while slow and pre:
            if pre.val != slow.val:
                return False
            pre = pre.next
            slow = slow.next
        return True

300.最长上升子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

动态规划,每个位置去找之前位置比自己小的数的最长长度,在此基础上加1

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        dp = [1] * len(nums)
        max_len = 1
        for i in range(1, len(nums)):
            for j in range(i):
                if nums[j] < nums[i]:
                    dp[i] = max(dp[i], dp[j] + 1)
            max_len = max(max_len, dp[i])
        return max_len

二分
维护tail数组,tail数组保存以数组下标结尾的上升序列的最后一个数,每次遍历二分查找当前数n在tail中位置,插入到比n大一些的位置,如果插入位置是当前右指针,则右移右指针,以为二分j=m,遇到大于当前tail中所有数字才会达到右指针位置。res 则为结果

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        tails, res = [0] * len(nums), 0
        for n in nums:
            i, j = 0, res
            while i < j:
                m = (i+j) // 2
                if tails[m] < n: i = m + 1
                else: j = m
            tails[i] = n 
            if i == res: res += 1
        return res

301.删除无效的括号

给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。

返回所有可能的结果。答案可以按 任意顺序 返回。
递归回溯,先遍历找出多余的括号数
dfs,根据当前char判断选项,若为括号可以选择删或者不删,记录现有的lc,rc用于剪枝

class Solution:
    def removeInvalidParentheses(self, s: str) -> List[str]:
        lremove = rremove = 0
        for c in s:
            if c == '(': lremove += 1
            elif c == ')':
                if lremove > 0: lremove -= 1
                else: rremove += 1
        res = set()
        def dfs(idx, l, r, ll, rr, st):
            if idx == len(s):
                if not ll and not rr:
                    res.add(st)
                return
            if s[idx] == '(' and ll:
                dfs(idx+1, l, r, ll-1, rr, st)
            elif s[idx] == ')' and rr:
                dfs(idx+1, l, r, ll, rr-1, st)
            if s[idx] not in '()':
                dfs(idx+1, l, r, ll, rr, st+s[idx])
            elif s[idx] == '(':
                dfs(idx+1, l+1, r, ll, rr, st+s[idx])
            elif l > r:
                dfs(idx+1, l, r+1, ll, rr, st+s[idx])
            return
        dfs(0,0,0,lremove,rremove,'')
        return list(res)

312.戳气球

有 n 个气球,编号为0 到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。

现在要求你戳破所有的气球。戳破第 i 个气球,你可以获得 nums[i - 1] * nums[i] * nums[i + 1] 枚硬币。 这里的 i - 1 和 i + 1 代表和 i 相邻的两个气球的序号。如果 i - 1或 i + 1 超出了数组的边界,那么就当它是一个数字为 1 的气球。

求所能获得硬币的最大数量。

思路: 动态规划,现在数组两边做两个哨兵,dp[i][j]表示i,j开区间的戳气球最大分数,对区间长度进行遍历,从小到大,每个区间计算分数

class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        nums = [1] + nums + [1]
        n = len(nums)
        dp = [[0]*n for _ in range(n)]
        ret = 0
        def chuo(i, j):
            res = 0
            for k in range(i+1, j):
                res = max(res, dp[i][k] + nums[i]*nums[k]*nums[j] +dp[k][j])
            return res
        for L in range(2, n):
            for i in range(0, n - L):
                j = i + L
                dp[i][j] = chuo(i, j)
        return dp[0][-1]

322.零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

思路: dp数组保存每个amount对应的最少数量,每次从减掉coin从头找

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [-1] * (amount + 1)
        coins.sort()
        dp[0] = 0
        for i in range(1, amount + 1):
            m = float('inf')
            for coin in coins:
                if coin > i: break
                if dp[i-coin] != -1:
                    m = min(m, dp[i-coin]+1)
            if m != float('inf'):
                dp[i] = m 
        return dp[-1]

338.比特位计数

给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。

分奇偶,奇数为前一个偶数加一,偶数为除2对应值。

class Solution:
    def countBits(self, n: int) -> List[int]:
        dp = [0]*(n+1)
        for i in range(1, n+1):
            if i & 1:
                dp[i] = dp[i-1] + 1
            else:
                dp[i] = dp[i//2]
        return dp

394.字符串编码

给定一个经过编码的字符串,返回它解码后的字符串。

编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

两种方法区别:栈方法先处理前面的结果然后入栈,入栈时处理下个括号的,所以初始化,出栈的时候已经处理完了括号的,出栈的是括号之前的;dfs方法先处理括号内的返回括号内的然后跟当前拼接,所以返回的是后面括号位置的值,再由当前multi进行乘,遇到有括号返回当前res,每次进入函数时初始化。

思路: 借助栈,栈里保存之前的字符串,和括号前的数字(表示括号里重复几次),每次遇到左括号,说明要先处理括号内的,所以进栈,遇到有括号,说明当前位置处理完毕,出栈得到之前res和当前乘数,其他正常处理

class Solution:
    def decodeString(self, s: str) -> str:
        stack, res, n = [], '', 0
        for i in range(len(s)):
            if s[i] == '[':
                stack.append((res, n))
                res, n = '', 0
            elif s[i] == ']':
                last_res, cur_cnt = stack.pop()
                res = last_res + cur_cnt * res
            elif '0' <= s[i] <= '9':
                n = n * 10 + int(s[i])
            else:
                res += s[i]
        return res

递归
遇到左括号进入递归,返回括号的res和最后idx,然后当前res加上计数乘以递归得到的res,这个multi已经用了再置0,遇到有括号返回。

class Solution:
    def decodeString(self, s: str) -> str:
        def dfs(i):
            res, multi = "", 0
            while i < len(s):
                if s[i] == '[':
                    next_res, i = dfs(i+1)
                    res +=  multi * next_res
                    multi = 0
                elif s[i] == ']':
                    return res, i
                elif '0' <= s[i] <= '9':
                    multi = multi * 10 + int(s[i])
                else:
                    res += s[i]
                i += 1
            return res
        return dfs(0)            

399.除法求值

给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] = [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi = values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。

另有一些以数组 queries 表示的问题,其中 queries[j] = [Cj, Dj] 表示第 j 个问题,请你根据已知条件找出 Cj / Dj = ? 的结果作为答案。

返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -1.0 替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用 -1.0 替代这个答案。

注意:输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。

class Solution:
    def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]:
        # 构建图,保存每个节点对应终点和权值
        g = collections.defaultdict(dict)
        for (i, j), v in zip(equations, values):
            g[i][j] = v
            g[j][i] = 1/v
		# 深度搜索,找从i到j的路径,遍历i的终点,深度优先搜索
        def dfs(i, j):
            if i not in g: return -1
            if i == j: return 1
            visited.add(i)
            for node in g[i]:
                if node in visited: continue
                if node == j: return g[i][node]
                res = dfs(node, j)
                if res != -1: return g[i][node]*res
            return -1
        res = [] # visited保存遍历节点,防止重复无穷递归
        for i,j in queries:
            visited = set()
            res.append(dfs(i, j))
        return res

406. 根据身高重建队列

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

思路: 排序后插入,身高降序,人数升序,插入时,在ret里都是大于等于当前值的,且人数升序,不会影响已经插入的

class Solution:
    def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
        people.sort(key=lambda x: (-x[0], x[1]))
        ret=[]
        for p in people:
            if len(ret) <= p[1]:
                ret.append(p)
            else:
                ret.insert(p[1], p)
        return ret

416. 分割等和子集

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

思路: 动态规划,以sum一半为target,dp[i][j]表示前i个数能不能取出和为j的数。

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        s = sum(nums)
        if s & 1: return False
        target = s // 2 + 1
        dp = [[False] * target for _ in range(len(nums))]
        for i in range(len(nums)): dp[i][0] = True
        for i in range(len(nums)):
            for j in range(1, target):
                dp[i][j] = dp[i-1][j] 
                if j >= nums[i]: dp[i][j]|= dp[i-1][j-nums[i]]
        return dp[-1][-1]

空间优化,将数组变成一维,因为每次遍历向前取值,所以倒序遍历,提前剪枝

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        s = sum(nums)
        if s & 1: return False
        target = s // 2 + 1
        dp = [False] * target 
        dp[0] = True
        for i in range(len(nums)):
            for j in range(target-1, 0, -1):
                if j >= nums[i]:
                    dp[j] |= dp[j-nums[i]]
            if dp[-1] == True: return True
        return dp[-1]

437. 路径总和 III

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

深度搜索,dfs寻找当前root为起点的为target的所有数量,外层递归遍历所有节点,寻找这些节点开始的数量。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def pathSum(self, root: TreeNode, targetSum: int) -> int:
        if not root: return 0
        def dfs(root, tar):
            if not root: return 0
            tar -= root.val
            res = 0
            if tar == 0: res = 1
            return res + dfs(root.left, tar) + dfs(root.right, tar)
        return dfs(root, targetSum) + self.pathSum(root.left, targetSum) + self.pathSum(root.right, targetSum)

前缀和,新建一个字典保存对应前缀和出现的次数,一次dfs,每次传入之前的前缀和,当前前缀和为之前的加上当前节点值,从字典中找以本层为终点的和数组,再把本层前缀和放入字典,加上左右子树的res,最后回退时要在字典中减去当前节点的前缀和

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def pathSum(self, root: TreeNode, targetSum: int) -> int:
        if not root: return 0
        d = collections.defaultdict(int)
        d[0] = 1
        def dfs(root, cursum) -> int:
            if not root: return 0
            cursum += root.val
            res = d[cursum - targetSum]
            d[cursum] += 1
            res += dfs(root.left, cursum)
            res += dfs(root.right, cursum)
            d[cursum] -= 1
            return res
        return dfs(root, 0)

438. 找到字符串中所有字母异位词

给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。

异位词 指字母相同,但排列不同的字符串。

思路: dic保存计数,正数为缺的,负数为多的,diff保存窗口和正确的差距,滑动窗口,每次踢掉的字母计数小于0,则diff减少(多了),反之,diff增加(踢了就缺了),维护字典,每次加入的字母计数小于0,则diff增加(多了不需要了还来),反之,diff减少(缺你你快来),每次滑动进行判定diff,diff==0加入结果list。

class Solution:
    def findAnagrams(self, s: str, p: str) -> List[int]:
        if len(p) > len(s): return []
        d, ret, diff, k = {}, [], len(p), len(p)
        for c in p:
            if c not in d: d[c] = 1
            else: d[c] += 1
        for i in range(k):
            if s[i] in d:
                d[s[i]] -= 1
                if d[s[i]] >= 0: diff -= 1
                else: diff += 1
        if diff == 0: ret.append(0)
        for i in range(k, len(s)):
            if s[i-k] == s[i]:
                if diff == 0: ret.append(i - k + 1)
                continue
            if s[i-k] in d:
                if d[s[i-k]] < 0: diff -= 1
                else: diff += 1
                d[s[i-k]] += 1
            if s[i] in d:
                if d[s[i]] > 0: diff -= 1
                else: diff += 1
                d[s[i]] -= 1
            if diff == 0: ret.append(i - k + 1)
        return ret

494.目标和

给你一个整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

思路:
转化成01背包问题,将数组分成两部分 x + y = sum(nums), x - y = target,所以就是找 x = (sum(nums) + target) // 2 的子数组,类似461分割等和子集相同处理,注意不同的是取和,所以必须遍历改变到dp[0]位置

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        sm = sum(nums) + target
        if sm & 1: return 0
        sm //= 2
        dp = [1] + [0] * sm 
        for i in range(len(nums)):
            for j in range(sm, -1, -1):
                if j > nums[i] - 1:
                    dp[j] += dp[j - nums[i]]
        return dp[-1] 

581.最短无序连续子数组

给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。

请你找出符合题意的 最短 子数组,并输出它的长度。
思路: 数组分成abc三部分,a为升序,c为升序,b为中间乱序,若b中有小于a的数,a的长度应该缩到那个数前,同理c
所以单次遍历,从左到右找大数,先找右边c的边界,若遍历位置更大,满足升序更新max不记录,若当前位置比之前最大值小,说明至少是这个位置边界记录一下,同理,反向遍历找小数,最后指标停在闭区间边界,计算的到长度

class Solution:
    def findUnsortedSubarray(self, nums: List[int]) -> int:
        n = len(nums)
        maxn, minn = float('-inf'), float('inf')
        left = right = -1
        for i in range(n):
            if nums[i] >= maxn:
                maxn = nums[i]
            else:
                right = i
            if nums[n - i - 1] <= minn:
                minn = nums[n - i - 1]
            else:
                left = n - i - 1
        return 0 if left == -1 else right - left + 1

621. 任务调度器

给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。

然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。

你需要计算完成所有任务所需要的 最短时间 。
在这里插入图片描述
在这里插入图片描述
桶子法,计算最多的数量,桶子深度为n+1,若填不满 则 res=(M-1)*(n+1) + x (x为并列最多的个数)如图一
若上面已经全部填满此时size > res,直接返回res就行

class Solution:
    def leastInterval(self, tasks: List[str], n: int) -> int:
        l = len(tasks)
        wordlist = [[i,0] for i in range(26)]
        for c in tasks:
            wordlist[ord(c) - ord("A")][1] += 1
        wordlist.sort(key=lambda x: -x[1])
        m = wordlist[0][1]
        cnt = 0
        for w in wordlist:
            if w[1] != m:
                break
            cnt += 1
        res = (m-1)*(n+1)+cnt
        return max(res, l) 

647. 回文子串

给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

动态规划:
类似 4

class Solution:
    def countSubstrings(self, s: str) -> int:
        n = len(s)
        dp = [[0]*n for _ in range(n)]
        sm = n
        for i in range(n):
            dp[i][i] = 1
        for L in range(2, n + 1):
            for i in range(n- L + 1):
                j = i + L - 1
                if s[i] == s[j]:
                    if L > 2:
                        dp[i][j] = dp[i+1][j-1]
                    else:
                        dp[i][j] = 1
                    if dp[i][j]:
                        sm += 1         
        return sm

中心扩展法

class Solution:
    def countSubstrings(self, s: str) -> int:
        n = len(s)
        res = 0
        for i in range(2*n - 1):
            left = i // 2
            right = left + i % 2
            while 0 <= left and right < n and s[left] == s[right]:
                res += 1
                left -= 1
                right += 1
        return res

739.每日温度

请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。

单调栈

递减栈,若当前值大于栈顶值,说明找到比之前大的温度了,依次出栈记录差值。

class Solution:
    def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
        stack = []
        res = [0] * len(temperatures)
        for i, tp in enumerate(temperatures):
            while stack and temperatures[stack[-1]] < tp:
                idx = stack.pop()
                res[idx] = i - idx
            stack.append(i)
        return res
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值