5.16力扣 并查集 DFS滑动窗口

1202 交换字符串中给定元素
在这里插入图片描述
初始化:一开始根节点就是本身索引
依据pair将2个编号连接,只需把这2个编号的联通分量设置成一样就行了。find方法用于找到某个编号的联通分量,union方法将两个编号的联通分量修改为一样的
把联通分量作为字典key给字符串的索引分组,因为同一组内的索引都是可以互相交换的的
在每一组内,按照索引还原成字符在组内排序,排序完成后根据原所在数组位置放回去

每一个连通分量中的各个索引位都可以互相交换 所以只要对各个连通分量中的字符进行排序再写回就可以了

class Solution:
    def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str:
        n=len(s)
        #根据索引初始化根节点
        p=[i for i in range(n)]
        #查找根节点
        def f(x):
            if x!=p[x]:
                p[x]=f(p[x])
            return p[x]
        def union(i,j):
            p[f(j)]=f(i)
        #合并 并查集
        for i,j in pairs:
            union(i,j)
        # 合并可交换位置
        dic=collections.defaultdict(list)
        #将属于同一组的索引放到一块
        for i in range(len(p)):            
            dic[f(i)].append(i)
        res=list(s)
        for lst in dic.values():
            #各个组内的字符排序
            t=sorted(res[i] for i in lst)
            #对应索引重新插回字符串
            for i, c in zip(lst, t):
                res[i] = c
        return ''.join(res)

面试题 17.07 婴儿名字
在这里插入图片描述

from collections import defaultdict
import re
class Solution:
    def trulyMostPopular(self, names, synonyms) :
        info,name={},[]
        for n in names:
            i,j=re.findall(r'\w+',n)
            #每个名字的频率
            info[i]=int(j)
            #名字列表
            name.append(i)
        p={i:i for i in name}
        def find(x):
            while x != p[x]:
                x=find(p[x])
            return x
        def union(i,j):
            if i not in p or j not in p:
                return
             #按照字典序结合,将字典序小的作为头头
            if find(i)<find(j):
                p[find(j)]=find(i)
            else:
                p[find(i)]=find(j)
            # str.strip(char) 移除字符串头尾指定的字符序列。
         #组合名字
        syns=[s.strip('()').split(',') for s in synonyms]
        for i,j in syns:
            union(i,j)
        res=defaultdict(int)
        #将同一组的概率相加
        for n in name:
            res[find(n)]+=info[n]
        ans=[]
        for key,values in res.items():
            ans.append(key+'('+str(values)+')')
        return ans

947. 移除最多的同行或同列石头
在这里插入图片描述
我们如何建立我们的并查集,我们合并的是点吗?这样的时间复杂度太高了。事实上,我们只需要合并stones中出现的行与列即可。
对于一个坐标为 (i, j) 的石子来说,需要把行 i 和列 j 合并,因为并查集是一维的,用 j+10000 来代替 j。在将所有石子的行和列都合并好之后,只需数一下并查集中有几个集合就可以得到答案了。

//并查集数组,因为题中给出0 <= stones[i][j] < 10000,[0,10000)表示x轴,[10000,20000)表示y轴

分别把x,y+10000来作为两个点处理,先合并在一起. 然后新增加的点通过相同的合并操作,把之前添加的点再合并在一起,

最后看存在几个集合,每个集合都需要剩一个石头,那么可移动的石头数就是石头总数-集合数

class Solution:
    def removeStones(self, stones: List[List[int]]) -> int:
        def find(x):
            while x!=p[x]:
                x=find(p[x])
            return x
        def union(x,y):
            p[find(x)]=find(y)
        
        p=[i for i in range(20000)]
        for x,y in stones:
            union(x,y+10000)
        res=[]
        for x,y in stones:
            if find(x) not in res:
                res.append(find(x))
        return len(stones)-len(res)

1020. 飞地的数量
在这里插入图片描述
在这里插入图片描述

class Solution:
    def numEnclaves(self, A: List[List[int]]) -> int:
        def dfs(i,j):
            A[i][j]=0
            for dx,dy in [(1,0),(-1,0),(0,-1),(0,1)]:
                x,y=dx+i,dy+j
                if 0<=x<n and 0<=y<m and A[x][y]==1:
                    dfs(x,y)
        if len(A)==0:
            return 0
        n=len(A)
        m=len(A[0])
        #从边界的岛屿往里面搜索,将能到达的岛屿变成海洋0,剩下不能到达的即为所需的
        for i in range(n):
            if A[i][0]==1:
                dfs(i,0)
            if A[i][m-1]==1:
                dfs(i,m-1)
        for j in range(m):
            if A[0][j]==1:
                dfs(0,j)
            if A[n-1][j]==1:
                dfs(n-1,j)
        sum=0
        for i in range(n):
            for j in range(m):
                sum+=A[i][j]
        return sum

959. 由斜杠划分区域
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如例5:
grid[0][0]=’/’,则 0 1结合,2 3结合,然后因为r+1<n,c+1<n,进行2 5结合,3 8结合
此时组合为【0,1】,【2,3,5,8】
grid[0][1]=’/’,则4 5结合,6 7 结合,然后因为r+1<n,进行7 12 结合
此时组合为【4,5】,【6,7,12】
grid[1][0]=’/’,则 8 9结合,10,11结合,然后因为c+1<n进行10 13结合
此时组合为【8,9】,【10,11,13】
grid[1][1]=‘ ’,则12 13 14 15 结合,此时组合为【12,13,14,15】
则可以得到组合【0,1】【2,3,4,5,8,9】,【6,7,10,11,12,13,14,15】,组合数为三

题解来源:
https://leetcode-cn.com/problems/regions-cut-by-slashes/solution/959-you-xie-gang-hua-fen-qu-yu-guan-fang-de-ti-jie/
在这里插入图片描述

class Solution:
    def regionsBySlashes(self, grid: List[str]) -> int:
        n=len(grid)
        #例如n=2,则划分成4个小方格,每个小方格在根据斜线分成四个块 0123 4567 8891011 12131415
        p=[i for i in range(4*n*n)]
        def find(x):
            while x!=p[x]:
                x=find(p[x])
            return x
            # if x!=p[x]:
            #     p[x]=find(p[x])
            # return p[x]
        def union(x,y):
            if find(x)!=find(y):
                p[find(x)]=find(y)
        for r,row in enumerate(grid):
            for c,val in enumerate(row):
                #r=0,c=0表示第1行第1个方块 数字号为0 1 2 3
                #r=0,c=1表示第1行第2个方块,数字号为4 5 6 7
                top=4*(r*n+c)
                #当为空格时,0 1 2 3都联合
                #当为/时,0 1结合,2 3结合
                if val in ['/',' ']:
                    union(top+0,top+1)
                    union(top+2,top+3)
                 #当为\时,0 2结合 1 3结合
                if val in ['\\',' ' ]:
                    union(top+0,top+2)
                    union(top+1,top+3)
                #如果右面还有方块或者下面还有方块
                # 该小方块需要和邻近方块结合,无论这个方块如何划分 例如 2 5结合,3 8结合,7,12结合
                if r<n-1:
                    union(top+3,top+4*n+0)
                    # union(top+3,4*(r*n+n+c)+0)
                if c<n-1:
                    union(top+2,top+4+1)
                    # union(top+2,4*(r*n+c+1)+1)
        count=0
        #判断有多少集合
        for i in range(4*n*n):
            if i==p[i]:
                count+=1
        return count
        # return sum(p[x]==x for x in range(4*n*n))

540 有序数组中的单一元素
在这里插入图片描述
O(logn)的时间复杂度及O(1)的空间复杂度,用二分搜索
有序数组,且每个元素出现两次,只有一个元素出现一次,则数组长度为奇数

class Solution:
    def singleNonDuplicate(self, nums: List[int]) -> int:
        n=len(nums)
        l,r=0,n-1
        #主要根据元素个数进行左右边界移动
        while l<r:
            mid=l+(r-l)//2
            # 4 3 3
            if nums[mid]==nums[mid+1]:
                #此时从mid分割,右边数组偶数个,则去除与mid相等的mid+1,则右边是奇数个数,说明单一元素出自右边
                #例如 1 1 3 3 7 7 10 12 12
                if (r-mid)%2==0:
                    l=mid+2
                else:
                    r=mid-1
            elif nums[mid]==nums[mid-1]:
                if (r-mid)%2==0:
                    r=mid-2
                else:
                    l=mid+1
            else:
                return nums[mid]
        #直到最后l==r输出
        return nums[l]

485. 最大连续1的个数
在这里插入图片描述

class Solution:
    def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
        dp=[0 for i in range(len(nums))]
        for i in range(len(nums)):
            if nums[i]==1:
                dp[i]=dp[i-1]+1
            else:
                dp[i]=0
        return max(dp)

在这里插入图片描述

class Solution:
    def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
        maxcount=0
        count=0
        for i in range(len(nums)):
            if nums[i]==1:
                count+=1
            else:
                count=0
            maxcount=max(maxcount,count)
        return maxcount

1004. 最大连续1的个数 III(滑动窗口)
在这里插入图片描述
l为窗口左边界,r为窗口右边界,在窗口中一直保证一直最多k个0存在,然后r-l就是包括这k个零的最大长度,

class Solution:
    def longestOnes(self, A: List[int], K: int) -> int:
        l=0
        res=0
        for r in range(len(A)):
            #如果右侧为0,如果此时K>0,则K-=1,如果K=0,则需要收缩左边边界,当左侧为0时,K才加1,否则继续收缩到K>0时,继续右移
            if A[r]==0:
                while K==0:
                    if A[l]==0:
                        K+=1
                    l+=1
                K-=1  
            res=max(res,r-l+1)
        return res

424. 替换后的最长重复字符(滑动窗口)
在这里插入图片描述
在这里插入图片描述
维护一个数组int[26]来存储当前窗口中各个字母的出现次数,left表示窗口的左边界,right表示窗口右边界
字符表来表示窗口,窗口的大小与新字符的最大计数之间的差值表示可以替换的字符个数,,当可以替换的字符个数大于k时,我们需要缩小窗口,也就是left右移,直到可以替换的字符个数等于k时,我们可以得到结果。
如果当前字符串中的出现次数最多的字母个数+K大于串长度,那么这个串就是满足条件的

maxl保存滑动窗口内相同字母出现次数的历史最大值,通过判断窗口宽度(right - left + 1)是否大于maxl + K来决定窗口是否做滑动,否则窗口就缩小
窗口从左至右不断扩张/滑动,当窗口触达字符串末尾字符时,运算结束,窗口的宽度为最终结果

class Solution:
    def characterReplacement(self, s: str, k: int) -> int:
        hash=[0]*26
        l=0
        maxl=0
        res=0
        for r in range(len(s)):
            idx=ord(s[r])-ord('A')
            hash[idx]+=1
            #当前窗口内相同字符出现的最大次数
            maxl=max(maxl,max(hash))
            #当前窗口内需要替换的字符数大于k时,窗口需要缩小,l右移
            while r-l+1-max(hash)>k:
                hash[ord(s[l])-ord('A')]-=1
                l+=1
            # 当窗口内可替换的字符数小于等于k时,我们需要根据该窗口长度来确定是否更新result
            res=max(res,r-l+1)
        return res

38 外观数列
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
countAndSay(n)处理的是countAndSay(n-1)的结果
如果当前位与前一位一样,那么count_num加一;
如果当前位于前一位不一样,那么需要把前面的字符输出,即追加 count_num+num的字符串;

class Solution:
    def countAndSay(self, n: int) -> str:
        if n==1:
            return '1'
        #递归下一层,用‘*’表示结尾,为了保证正常字符串的最后一位可以添加进来,因为最后一位和 *一定不一样
        num=self.countAndSay(n-1)+'*'
        count=1
        nextstr=''
        for i in range(len(num)-1):
            #当和后面的字符相等时,则继续计数
            if num[i]==num[i+1]:
                count+=1
            # 当字符出现不一致时,拼接起来作为下一层字符串
            elif num[i]!=num[i+1]:
                nextstr+=str(count)+num[i]
                count=1
        return nextstr

69. x 的平方根
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
二分查找:

class Solution:
    def mySqrt(self, x: int) -> int:
        l,r=0,x
        while l<=r:
            mid=l+(r-l)//2
            if mid**2>x:
                r=mid-1
            else:
                ans=mid
                l=mid+1
        return ans

牛顿迭代:
https://leetcode-cn.com/problems/sqrtx/solution/x-de-ping-fang-gen-by-leetcode-solution/
在这里插入图片描述

在这里插入图片描述
x**2=c
在这里插入图片描述

class Solution:
    def mySqrt(self, x: int) -> int:
        if x==0:
            return 0
        x0,c=float(x),float(x)
        while True:
            x1=0.5*(x0+c/x0)
            if abs(x1-x0)<1e-7:
                break
            x0=x1
        return int(x1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值