5.20 优先队列(堆 每个元素插入堆删除的 时间复杂度都是O(logk),k是堆的长度) 链表 DFA(自动机)

翻转链表:
203 445 92

需要两个指针,前指针和当前指针
nxt=cur.next
cur.next=pre
pre=cur
cur=nxt

面试题22. 链表中倒数第k个节点
在这里插入图片描述

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        dummy=ListNode(0)
        tmp=cur=dummy
        dummy.next=head
        for i in range(k):
            tmp=tmp.next
        while tmp:
            tmp=tmp.next
            cur=cur.next
        return cur

优先队列和堆: 堆特别适合找第k大、K小的特殊数据结构

23 合并K个链表
在这里插入图片描述
构造一个小顶堆,将所有链表值放里面,自动弹出堆顶元素即最小值,直到堆为空

在这里插入图片描述

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

import heapq
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        dummy=ListNode(0)
        cur=dummy
        queue=[]
        for i in range(len(lists)):
            if lists[i]:
                heapq.heappush(queue,(lists[i].val,i))
                #第一个节点加入,将指针移到这个链表的第二个点
                lists[i]=lists[i].next
        while queue:
            val,idx=heapq.heappop(queue)
            cur.next=ListNode(val)
            cur=cur.next
            #节点来源链表如果下一个节点还存在,那么把下一个节点加入到堆中
            if lists[idx]:
                heapq.heappush(queue,(lists[idx].val,idx))
                #指针继续下移
                lists[idx]=lists[idx].next
        return dummy.next

215. 数组中的第K个最大元素
在这里插入图片描述
思路是创建一个大顶堆,将所有数组中的元素加入堆中,并保持堆的大小小于等于 k。这样,堆中就保留了前 k 个最大的元素。这样,堆顶的元素就是正确答案。
像大小为 k 的堆中添加元素的时间复杂度为 O(logk),我们将重复该操作 N 次,故总时间复杂度为 O(Nlogk)。
法一:
把 所有元素都放入一个最大堆中,然后再 pop() 出 k - 1 个元素,因为前 k - 1 大的元素都被弹出了,此时最大堆的堆顶元素就是数组中的第 k 个最大元素。
法二:
只用 k 个容量的优先队列,而不用全部 len 个容量。

import heapq
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        queue=[]
        #法一
        # for i in nums:
        #     heapq.heappush(queue,-i)
        # for i in range(k-1):
        #     heapq.heappop(queue)
        # return -queue[0]
        #法二
        for i in nums:
            #维护一个长度为k的堆,当新数据大于堆顶元素或者堆得长度不足k时,入堆
            if len(queue)<k or i>queue[0]:
                heapq.heappush(queue,i)
            #如果堆的长度大于k,提出此时堆顶元素(小顶堆)
            if len(queue)>k:
                heapq.heappop(queue)
        return heapq.heappop(queue)

347 前k个高频元素
在这里插入图片描述
heapq.nlargest(n, iterable[, key])¶
- Return a list with the n largest elements from the dataset defined by iterable.
堆中添加一个元素的复杂度是 O(log(k)),进行n次时间复杂度为O(nlog(k))
在这里插入图片描述

import heapq
class Solution:
    def topKFrequent(self, nums, k) :
        ans=defaultdict(int)
        for i in nums:
            ans[i]+=1
        res=heapq.nlargest(k,ans.items(),key=lambda x:x[1])
        return [i[0] for i in res]

692. 前K个高频单词
在这里插入图片描述
排序法:在这里插入图片描述
在这里插入图片描述
建堆:
在这里插入图片描述

from collections import Counter
import heapq
class Solution:
    def topKFrequent(self, words, k):
    #排序法 时间复杂度O(Nlog(N))
        # res=[]
        # ans=Counter(words).most_common()
        # ans.sort(key=lambda x:(-x[1],x[0]))
        # for i in range(k):
        #     res.append(ans[i][0])
        # return res
        #O(n log k) 时间复杂度和 O(n) 空间复杂度 使用堆,建堆和输出的时间复杂度是O(n log k)
        queue=[]
        for key,value in Counter(words).items():
            heapq.heappush(queue,(-value,key))
        return [heapq.heappop(queue)[1] for _ in range(k)]

面试题40. 最小的k个数
在这里插入图片描述

import heapq
class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        return heapq.nsmallest(k,arr)

K个最小用大顶堆,K个最大用小顶堆
维护一个长度为k的大根堆,堆长度大于k,就把堆顶的数弹出。最后将大根堆里的数存入数组返回即可。
在这里插入图片描述

class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        heap, res = [], []
        for x in arr:
            heapq.heappush(heap, -x) # Python 小根堆加负号,变成大根堆
            #维护堆的长度为k
            while len(heap) > k: 
                heapq.heappop(heap)
        while heap: 
            res.append(-heapq.heappop(heap))
        return res

264 丑数二
在这里插入图片描述
堆从数字1开始,第一步 弹出数字1 ,添加12,13,15,第二步弹出最小值2,添加22,23,25
用哈希表追踪出现过的数字,避免重复
每次弹出堆中的最小丑数,第n-1次弹出的即为第n个丑数
在这里插入图片描述

import heapq
class Solution:
    def nthUglyNumber(self, n: int) -> int:
        queue=[]
        hash={1}
        heapq.heappush(queue,1)
        for i in range(n):
            #每次弹出哈希表内的最小值
            num_ugly=heapq.heappop(queue)
            for i in [2,3,5]:
                new_ugly=num_ugly*i
                if new_ugly not in hash:
                    hash.add(new_ugly)
                    heapq.heappush(queue,new_ugly)
        return num_ugly

上面212s,下面的50s

import heapq
class ugly:
    def __init__(self):
        self.num_ugly=num_ugly=[]
        hash={1}
        queue=[]
        self.res=res=[]
        heapq.heappush(queue,1)
        for _ in range(1690):
            #每次弹出哈希表内的最小值
            num_ugly=heapq.heappop(queue)
            res.append(num_ugly)
            for i in [2,3,5]:
                new_ugly=num_ugly*i
                if new_ugly not in hash:
                    hash.add(new_ugly)
                    heapq.heappush(queue,new_ugly)
class Solution:
    ug=ugly()
    def nthUglyNumber(self, n: int) -> int:
        return self.ug.res[n-1]

1333 餐厅过滤器
在这里插入图片描述
在这里插入图片描述

class Solution:
    def filterRestaurants(self, restaurants: List[List[int]], veganFriendly: int, maxPrice: int, maxDistance: int) -> List[int]:
        dict={}
        res=[]
        for rest in restaurants:
            if rest[3]<=maxPrice and rest[4]<=maxDistance:
                if (veganFriendly and rest[2]) or not veganFriendly:
                    dict[rest[0]]=rest[1]
        dic=sorted(dict.items(),key=lambda x:(-x[1],-x[0]))
        for i in dic:
            res.append(i[0])
        return res

83. 删除排序链表中的重复元素
在这里插入图片描述
如果它是重复的,我们更改当前结点的 next 指针,以便它跳过下一个结点并直接指向下一个结点之后的结点。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head:
            return []
        cur=head
        while cur and cur.next:
            if cur.val==cur.next.val:
                cur.next=cur.next.next
            else:
                cur=cur.next
        return head

82. 删除排序链表中的重复元素 II
在这里插入图片描述

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        dummy=ListNode(0)
        dummy.next=head
        pre=dummy
        cur=head
        while cur:
            flag=False
            #一直找到与cur不等的节点,将pre指向该节点
            while cur.next and cur.val==cur.next.val:
                flag=True
                cur=cur.next
            if flag:
                pre.next=cur.next
            else:
                pre=cur
            cur=cur.next
        return dummy.next

面试题 02.05. 链表求和
在这里插入图片描述
逆向存储:


# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy=ListNode(0)
        p=dummy
        flag=False
        while l1 or l2:
            x1=l1.val if l1 else 0
            x2=l2.val if l2 else 0
            if flag:
                tmp=x1+x2+1
            else:
                tmp=x1+x2
            p.next=ListNode(tmp%10)
            if tmp>=10:
                flag=True
            else:
                flag=False
            if l1:
                l1=l1.next
            if l2:
                l2=l2.next
            p=p.next
        #最后多一个进位
        if flag:
            nxt=ListNode(1)
            p.next=nxt
            p=p.next
        return dummy.next

正向存储:
先将链表逆转,按照反向存储方法计算后在逆转链表

class Solution:
    def addTwoNumbersforwards(self,l1,l2):
        def reversed(l):
            if not root:
                return 
            pre=None
            cur=head
            while cur:
                tmp=cur.next
                cur.next=pre
                pre=cur
                cur=tmp
            return pre
        return reverse(self.addTwoNumbersforwards(reversed(l1),reversed(l2)))

    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy=ListNode(0)
        p=dummy
        flag=False
        while l1 or l2:
            x1=l1.val if l1 else 0
            x2=l2.val if l2 else 0
            if flag:
                tmp=x1+x2+1
            else:
                tmp=x1+x2
            p.next=ListNode(tmp%10)
            if tmp>=10:
                flag=True
            else:
                flag=False
            if l1:
                l1=l1.next
            if l2:
                l2=l2.next
            p=p.next
        #最后多一个进位
        if flag:
            nxt=ListNode(1)
            p.next=nxt
            p=p.next
        return dummy.next

8.字符串转换整数(atoi)
在这里插入图片描述
在这里插入图片描述
自动机:DFA
给定一个单词,判断该单词是否满足我们给定的单词描述规则,需要用到编译原理中词法分析的相关知识,其中涉及到的两个很重要的概念就是正规式(Regular Expression)和有穷自动机(Finite Automata)。正规式是描述单词规则的工具,首先要明确的一点是所有单词组成的是一个无穷的集合,而正规式正是描述这种无穷集合的一个工具;有穷自动机则是识别正规式的一个有效的工具,它分为确定的有穷自动机(Deterministic Finite Automata,DFA)和不确定的有穷自动机(Nondeterministic Finite Automata,NFA)。对于任意的一个单词,将其输入正规式的初始状态,自动机每次读入一个字母,根据单词的字母进行自动机中状态的转换,若其能够准确的到达自动机的终止状态,就说明该单词能够被自动机识别,也就满足了正规式所定义的规则。而DFA与NFA之间的差异就是对于某一个状态S,输入一个字符a,DFA能够到达的下一个状态有且仅有一个,即为确定的概念,而NFA所能到达的状态个数大于或等于一个,即不确定的概念。因为NFA为不确定的,我们无法准确的判断下一个状态是哪一个,因此识别一个正规式的最好的方式是DFA。那么,如何为一个正规式构造DFA就成了主要矛盾,解决了这个问题,词法分析器就已经构造完成。从正规式到DFA需要通过两个过程来完成:

①从正规式转NFA:对输入的正规式字符串进行处理转成NFA;

②从NFA转DFA:对NFA进行确定化处理转成DFA;
因此,为了有条理地分析每个输入字符的处理方法,我们可以使用自动机这个概念:
我们的程序在每个时刻有一个状态 s,每次从序列中输入一个字符 c,并根据字符 c 转移到下一个状态 s’。这样,我们只需要建立一个覆盖所有情况的从 s 与 c 映射到 s’ 的表格即可解决题目中的问题。
在这里插入图片描述
另外自动机也需要记录当前已经输入的数字,只要在 s’ 为 in_number 时,更新我们输入的数字,即可最终得到输入的数字

int_max=2**31-1
int_min=-2**31
class automation:
    def __init__(self):
        self.state='start'
        self.sign=1
        self.ans=0
        self.table={'start': ['start', 'signed', 'in_number', 'end'],
            'signed': ['end', 'end', 'in_number', 'end'],
            'in_number': ['end', 'end', 'in_number', 'end'],
            'end': ['end', 'end', 'end', 'end']}
    def get_col(self,c):
        if c==' ':
            return 0
        elif c=='+' or c=='-':
            return 1
        elif c.isdigit():
            return 2
        return 3
    def get(self,c):
        self.state=self.table[self.state][self.get_col(c)]
        if self.state=='in_number':
            self.ans=10*self.ans+int(c)
            self.ans=min(self.ans,int_max) if self.sign==1 else min(self.ans,-int_min)
        elif self.state=='signed':
            self.sign=1 if c=='+' else -1
            
class Solution:
    def myAtoi(self, str: str) -> int:
        auto=automation()
        for s in str:
            auto.get(s)
        return auto.sign*auto.ans

正则表达式解法
^ 表示匹配字符串开头,我们匹配的就是 ‘+’ ‘-’ 号,没有^,findall将会匹配到字符串中所有符合条件的数字

[] 表示匹配包含的任一字符,比如[0-9]就是匹配数字字符 0 - 9 中的一个

? 表示前面一个字符出现零次或者一次,这里用 ? 是因为 ‘+’ 号可以省略

\d 表示一个数字 0 - 9 范围

  • 表示前面一个字符出现一次或者多次,\d+ 合一起就能匹配一连串数字了

findall:在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

import re
class Solution:
    def myAtoi(self, str: str) -> int:
        int_max=2**31-1
        int_min=-2**31
        #去除左边空格
        str=str.lstrip(' ')
        # ^从头开始匹配, []:表示匹配包含的任意字符 ?:表示前面的一个字符出现零次或者1次,因为‘+’不显示 \d:表示一个数字范围 +:前面一个字符出现一次或多次 \d+:可以匹配一串数字
        num_re=re.compile(r'^[\+\-]?\d+')
        num=num_re.findall(str)
        #输出为列表,需要解包,int(*[])为0
        num=int(*num)
        return  max(min(num,int_max),int_min )
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值