翻转链表:
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 )