【日常系列】LeetCode《21·综合应用2》

数据规模->时间复杂度

<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)

内容

在这里插入图片描述

lc 217 :存在重复元素
https://leetcode.cn/problems/contains-duplicate/
提示:
1 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9

在这里插入图片描述

#方案一:哈希
#o(n),o(n)
class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        #写法1
        # visited=set()
        # for i in range(len(nums)):
        #     if nums[i] in visited:return True
        #     visited.add(nums[i])
        # return False
        
        #写法2
        # visited={}
        # for i in range(len(nums)):
        #     if visited.get(nums[i]):return True #if nums[i] in visited
        #     visited[nums[i]]=1
        # return False

        #写法3
        return len(nums)!=len(set(nums))

#方案二:排序
#时:o(nlogn),空:o(logn)/o(n)
class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        #nums.sort()
        nums=sorted(nums)
        for i in range(1,len(nums)):
            if nums[i]==nums[i-1]:return True
        return False

lc 219 :存在重复元素 II
https://leetcode.cn/problems/contains-duplicate-ii/
提示:
1 <= nums.length <= 10^5
-109 <= nums[i] <= 10^9
0 <= k <= 10^5

在这里插入图片描述

#方案一:哈希
#o(n),o(n)
class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
        visited={}
        for i in range(len(nums)):
            if nums[i] in visited and i-visited[nums[i]]<=k:return True #visited.get(nums[i])等价于visited[nums[i]]
            visited[nums[i]]=i
        return False
        
#方案二:滑动窗+哈希
#时:o(n),空:o(min(n,k))
class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
        left=right=0
        window=set()
        while right<len(nums):
            #
            if nums[right] in window:return True
            #
            window.add(nums[right])
            if len(window)>k:
                window.remove(nums[left])
                left+=1
            #更新窗口
            right+=1
        return False

lc 220 【剑指 057】:存在重复元素 III
https://leetcode.cn/problems/contains-duplicate-iii/
提示:
0 <= nums.length <= 2 * 10^4
-2^31 <= nums[i] <= 2^31 - 1
0 <= k <= 10^4
0 <= t <= 2^31 - 1

在这里插入图片描述
注意:相邻桶也有可能存在在这里插入图片描述
注意:负数/0的情况
在这里插入图片描述

#方案一:滑动窗(位置差)+桶(数值差)
#时:o(n),o(min(n,k))
class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], indexDiff: int, valueDiff: int) -> bool:
        left=right=0
        #key1:保证值区间
        bucketsize=valueDiff+1 
        window_bucket={}
        while right<len(nums):
            bucketid=self.getbucketid(nums[right],bucketsize)
            #考虑单桶/相邻桶情况
            if bucketid in window_bucket:return True
            if bucketid+1 in window_bucket and window_bucket[bucketid+1]-nums[right]<=valueDiff:
                return True
            if bucketid-1 in window_bucket and nums[right]-window_bucket[bucketid-1]<=valueDiff:
                return True
            #  
            window_bucket[bucketid]=nums[right]

            #key2:保证窗口个数区间(桶的个数=min(k,n))
            if len(window_bucket)>indexDiff:
                del window_bucket[self.getbucketid(nums[left],bucketsize)]
                left+=1
            #key3:更新窗口
            right+=1
        return False

    def getbucketid(self,x,bucketsize):
        if x>:return x//bucketsize
        return ((x+1)//bucketsize)-1

lc 258 :各位相加
https://leetcode.cn/problems/add-digits/
提示:
0 <= num <= 2^31 - 1

class Solution:
    def addDigits(self, num: int) -> int:
        while num>=10:
            num=self.sumdig(num)
        return num
    
    def sumdig(self,num):
        total=0
        while num!=0:
            total+=num%10
            num//=10 #注意:不是/
        return total

lc 202 :快乐数
https://leetcode.cn/problems/happy-number/
提示:
1 <= n <= 2^31 - 1

在这里插入图片描述在这里插入图片描述

#抽象为“单向链表"是否有环问题
#方案一:哈希
class Solution:
    def isHappy(self, n: int) -> bool:
        visited=set()
        while True:
            if n==1:return True
            if n in visited:return False
            #
            visited.add(n)
            n=self.squaresum(n)

    def squaresum(self,num):
        s=0
        while num!=0:
            s+=(num%10)**2
            num//=10
        return s
        
#方案二:快慢指针
class Solution:
    def isHappy(self, n: int) -> bool:
        if n==1:return True
        #
        slow=fast=n
        while True:
            slow=self.squaresum(slow)
            fast=self.squaresum(self.squaresum(fast))
            if fast==1:return True #slow==1 or
            if slow==fast:return False
            
    def squaresum(self,num):
        s=0
        while num!=0:
            s+=(num%10)**2
            num//=10
        return s

lc 263 :丑数
https://leetcode.cn/problems/ugly-number/
提示:
-2^31 <= n <= 2^31 - 1
在这里插入图片描述在这里插入图片描述

#问题抽象为”树形结构“
#方案一:DFS
class Solution:
    def isUgly(self, n: int) -> bool:
        if n==0: return False #死循环
        return self.dfs(n)
    
    def dfs(self,n):
        if n==1:return True
        factors=[2,3,5]
        for fac in factors:
            if n%fac==0 and self.dfs(n/fac): #key:能判断出-能被整除,则对n/fac一直整除下去
                return True
        return False
#方案二:迭代
class Solution:
    def isUgly(self, n: int) -> bool:
        if n==0:return False
        factors=[2,3,5]
        for fac in factors:
            while n%fac==0:
                n/=fac
        return n==1

字典树 - 前缀树 - Tire
在这里插入图片描述

#Node:表达节点-表达单词结尾
#功能:添加单词-查询指定单词(哈希表)
class Node:
    def __init__(self):
        self.children={} #c->Node(c)
        self.isend=False #标记每个单词的结尾字符
    
class trie:
    def __init__(self):
        self.root=Node()
    
    def add(self,word):
        curr=self.root
        for c in word:
            #key
            if c not in curr.children:
                curr.children[c]=Node()
            curr=curr.children[c]
        #
        curr.isend=True

    def contain(self,word):
        curr=self.root
        for c in word:
            if c not in curr.children:
                return False
            curr=curr.children[c]
        #
        return curr.isend

lc 208 【剑指 062】:实现 Trie (前缀树)【top100】
https://leetcode.cn/problems/implement-trie-prefix-tree/
提示:
1 <= word.length, prefix.length <= 2000
word 和 prefix 仅由小写英文字母组成
insert、search 和 startsWith 调用次数 总计 不超过 3 * 10^4 次

#方案一:map
class Node:
    def __init__(self):
        self.children={}
        self.isend=False

class Trie:

    def __init__(self):
        self.root=Node()

    def insert(self, word: str) -> None:
        curr=self.root
        for c in word:
            if c not in curr.children:
                curr.children[c]=Node()
            curr=curr.children[c]
        curr.isend=True


    def search(self, word: str) -> bool:
        curr=self.root
        for c in word:
            if c not in curr.children:
                return False
            curr=curr.children[c]
        return curr.isend


    def startsWith(self, prefix: str) -> bool:
        curr=self.root
        for c in prefix:
            if c not in curr.children:
                return False
            curr=curr.children[c]
        return True #说明找到最后一个字符时,也未返回False

# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)

#方案二:数组
class Node:
    def __init__(self):
        self.children=[None]*26
        self.isend=False

class Trie:

    def __init__(self):
        self.root=Node()

    def insert(self, word: str) -> None:
        curr=self.root
        for c in word:
            if curr.children[ord(c)-ord('a')]==None:
                curr.children[ord(c)-ord('a')]=Node()
            curr=curr.children[ord(c)-ord('a')]
        curr.isend=True


    def search(self, word: str) -> bool:
        curr=self.root
        for c in word:
            if curr.children[ord(c)-ord('a')]==None:
                return False
            curr=curr.children[ord(c)-ord('a')]
        return curr.isend


    def startsWith(self, prefix: str) -> bool:
        curr=self.root
        for c in prefix:
            if curr.children[ord(c)-ord('a')]==None:
                return False
            curr=curr.children[ord(c)-ord('a')]
        return True #说明找到最后一个字符时,也未返回False


# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)

lc 642 :搜索自动补全系统
https://leetcode.cn/problems/design-search-autocomplete-system/
提示:
n == sentences.length
n == times.length
1 <= n <= 100
1 <= sentences[i].length <= 100
1 <= times[i] <= 50
c 是小写英文字母, ‘#’, 或空格 ’ ’
每个被测试的句子将是一个以字符 ‘#’ 结尾的字符 c 序列。
每个被测试的句子的长度范围为 [1,200]
每个输入句子中的单词用单个空格隔开。
input 最多被调用 5000 次

在这里插入图片描述
在这里插入图片描述

#①
class trieNode: #for build 前缀树
    def __init__(self):
        self.children={}
        self.end_times=0

#②
class sentenceInfo: #for matchsentences.sort
    def __init__(self,content,time):
        self.content=content
        self.time=time
#③key:如果有多条热度相同的句子,请按照 ASCII 码的顺序输出(ASCII 码越小排名越前)
class compareKey(sentenceInfo):
    def __lt__(x,y):
        return x.content<y.content if x.time == y.time else x.time>y.time


###########################
class AutocompleteSystem:
    def __init__(self, sentences: List[str], times: List[int]):
        #
        self.root=trieNode()
        for i in range(len(sentences)):
            self.insert(sentences[i],times[i])#字典树:字符串+热度
        #记录当前输入
        self.currentinput=""


    def input(self, c: str) -> List[str]:
        res=[]
        if c=="#":
            self.insert(self.currentinput, 1)#完整句子情况
            self.currentinput=""
        else:
            self.currentinput+=c #莫忘
            matchsentences=self.searchmatchs(self.currentinput)#找匹配
            matchsentences.sort(key=lambda x:(-x.time,x.content))#排个序 #???-x.time
            for i in range(min(3,len(matchsentences))):#存前三
                res.append(matchsentences[i].content)
        return res


##########################################
    #构建前缀树    
    def insert(self,s,time):
        curr=self.root
        for c in s:
            if c not in curr.children:
                curr.children[c]=trieNode()
            curr=curr.children[c]
        #key:热度可叠加
        curr.end_times+=time

    #搜索以s开头的所有句子
    def searchmatchs(self,s):
        matchs=[] #注意位置
        ##curr->指向前缀末端(比如do的o)
        curr=self.root
        for c in s:
            if c not in curr.children:
                curr.children[c]=trieNode()
            curr=curr.children[c]
        ##dfs->每个matchsentence(dog,door,does···)
        def dfs(curr,s):
            #
            if curr.end_times>0:
                matchs.append(sentenceInfo(s, curr.end_times))
            #
            for c,node in curr.children.items():#key:.items() #[c,node]也行  
                dfs(node,s+c)
            
        dfs(curr, s)
        return matchs


# Your AutocompleteSystem object will be instantiated and called as such:
# obj = AutocompleteSystem(sentences, times)
# param_1 = obj.input(c)

lc 421【剑指 067】 :数组中两个数的最大异或值
https://leetcode.cn/problems/maximum-xor-of-two-numbers-in-an-array/
提示:
1 <= nums.length <= 2 * 10^5
0 <= nums[i] <= 2^31 - 1

在这里插入图片描述

在这里插入图片描述

#二进制前缀树
class Node:
    def __init__(self):
        self.zero=None
        self.one=None

#o(30)->o(1)
class Trie:
    def __init__(self):
        self.root=Node()
    
    #不断添加,构造前缀树
    def add(self,num):
        curr=self.root
        #从左往右取二进制位
        for k in range(30,-1,-1):
            bit=(num>>k)&1
            if bit==0:
                if not curr.zero:
                    curr.zero=Node()
                curr=curr.zero
            else:
                if not curr.one:
                    curr.one=Node()
                curr=curr.one

    
    #不断在树中,查询最大异或值
    def maxXor(self,num):
        #
        x=0
        curr=self.root
        for k in range(30,-1,-1):
            bit=(num>>k)&1
            #key
            if bit==0:
                if curr.one!=None:
                    curr=curr.one
                    x=2*x +1
                else:
                    curr=curr.zero
                    x=2*x
            else:
                if curr.zero!=None:
                    curr=curr.zero
                    x=2*x +1
                else:
                    curr=curr.one
                    x=2*x
        return x
                         
#O(n),o(n)
class Solution:
    def findMaximumXOR(self, nums: List[int]) -> int:
        #
        res=0
        trie=Trie()
        #
        for i in range(1,len(nums)):
            trie.add(nums[i-1])
            res=max(res,trie.maxXor(nums[i]))
        return res

lc 440 :字典序的第K小数字
https://leetcode.cn/problems/k-th-smallest-in-lexicographical-order/
提示:
1 <= k <= n <= 10^9
在这里插入图片描述

#抽象为十叉树
class Solution:
    def findKthNumber(self, n: int, k: int) -> int:
        curr=1
        k-=1 #key?
        #
        while k>0:
            nums=self.calnodes(n,curr,curr+1)
            if nums-1 <k: #不在当前curr前缀树
                curr +=1 #去‘下一树’
                k -=nums
            else:#在当前curr前缀树
                #去‘下一层’
                curr *=10 
                k-=1
        return curr


    #计算当前curr“前缀树”中(小于n)的总节点数
    def calnodes(self,n,curr,next):
        nums=0
        while curr<=n:
            nums+=min(n+1,next)-curr
            #去“下一层”
            curr *=10
            next *=10
        return nums
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值