【MIX】算法题解(20200705)

LC 75. 颜色分类

题目链接
使用三路快排的思想求解
定义三根指针i指向表头,k指向表尾,j为游走
j<=k时循环:

  1. 如果nums[j]==0那么将ij所指向的元素交换,同时将两根指针后移
  2. 如果nums[j]==1,后移j指针
  3. 如果nums[j]==2,交换jk指针所指向的元素,同时将k指针后移

算法时间复杂度 O ( n ) \mathcal{O}(n) O(n),只需要扫描一遍数组即可

class Solution:
    def sortColors(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        # 三路切分
        n=len(nums)
        if not n: return
        i, j, k=0, 0, n-1
        while j<=k:
            if nums[j]==0:
                nums[i], nums[j]=nums[j], nums[i]
                i+=1
                j+=1
            elif nums[j]==1: j+=1
            elif nums[j]==2:
                nums[j], nums[k]=nums[k], nums[j]
                k-=1

LC 76. 最小覆盖字串

题目链接
本题使用双指针算法,建立两个hash表统计指针窗口串和目标串t的分布,对于超过t中的频数的字符,移动j指针,对于不足t中频数的字符,计入已匹配的字符长度cnt,当cnt==t.size()时,更新最小覆盖字串ans.
时间复杂度 O ( n ) \mathcal{O}(n) O(n)(哈希表添加/删除元素时间开销为 O ( 1 ) \mathcal{O}(1) O(1))

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        dt, ds=defaultdict(int), defaultdict(int)
        for c in t: dt[c]+=1

        ans, cnt="", 0
        i, j, n=0, 0, len(s)
        while i<n:
            ds[s[i]]+=1
            if ds[s[i]]<=dt[s[i]]: cnt+=1
            while j<n and ds[s[j]]>dt[s[j]]:
                ds[s[j]]-=1
                j+=1
            if cnt==len(t): # update
                if not len(ans) or len(ans)>i-j+1:
                    ans=s[j:i+1]
            i+=1

        return ans

LC 77. 组合

题目链接
经典递归回溯问题
依次从1,2,…,n开始考虑组合,如果当前集合有k个元素则加入结果集,可以优化循环次数为n+1-len(vec)次.

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        ans=[]
        def dfs(index, vec):
            if len(vec)==k:
                ans.append([*vec])
                return

            for i in range(index, n+2-k+len(vec)):
                vec.append(i)
                dfs(i+1, vec)
                vec.pop(-1)

        dfs(1, [])
        return ans

LC 78. 子集

题目链接
本题和第77题类似,可以使用递归回溯/迭代算法求解.
算法时间复杂度为 O ( 2 n ) \mathcal{O}(2^n) O(2n)
方法1: 递归回溯
依次从index开始的元素组合子集,所有组合均加入结果集

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        ans, n=[], len(nums)
        def dfs(index, vec):
            ans.append([*vec])
        
            for i in range(index, n):
                vec.append(nums[i])
                dfs(i+1, vec)
                vec.pop(-1)

        dfs(0, [])
        return ans

方法2:迭代&二进制
由于子集的数量是固定的,对于一个长度为 n n n的集合,子集的个数为 2 n 2^n 2n, 可以映射到区间 [ 0 , 2 n − 1 ] [0, 2^{n-1}] [0,2n1],考虑使用二进制方式计数.
j=1<<n每一个bit表示对应位置的元素选(1)或不选(0)

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        n, ans =len(nums), []
        for j in range(1<<n):
            vec=[]
            for i in range(n):
                if j>>i&0x01: vec.append(nums[i])
            ans.append(vec)

        return ans

LC 79. 单词搜索

题目链接
本题使用递归回溯的方法求解
算法时间复杂度为 O ( n 2 3 k ) \mathcal{O}(n^23^k) O(n23k)
对于每一个匹配到单词首位的字符进行dfs搜索,将已经匹配到的字符mask,在回溯时执行umask操作,探测可能的3个方向,如果能完全匹配单词,则返回True.

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        r, c, n=len(board), len(board[0]), len(word)
        dirs=[[-1, 0], [0, 1], [1, 0], [0, -1]]

        def dfs(u, x, y):
            if u==n: return True
            t=board[x][y]
            board[x][y]='#'
            for d in dirs:
                nx, ny=x+d[0], y+d[1]
                if nx>=0 and nx<r and ny>=0 and ny<c and board[nx][ny]==word[u]:
                    if dfs(u+1, nx, ny): return True
            board[x][y]=t
            return False

        for i in range(r):
            for j in range(c):
                if word[0]==board[i][j]:
                    if dfs(1, i, j): return True

        return False

LC 80. 删除排序数组中的重复项 II

题目链接
本题使用双指针算法,时间复杂度为 O ( n ) \mathcal{O}(n) O(n)
定义指针j表示有效集的末尾指针(类似end()),i表示当前正在遍历数组的指针,由于数组有序,所以如果满足nums[i]!=nums[k-1]||nums[i]!=nums[k-2]||k<2可以向有效集中添加元素.

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        j=0
        for i in range(len(nums)):
            if j<2 or nums[i]!=nums[j-1] or nums[i]!=nums[j-2]:
                nums[j]=nums[i]
                j+=1
        return j
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Quant0xff

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值