文章目录
- 7 栈
- 7.1 【哈希表】有效的括号
- 7.2【栈】简化路径
- 7.3 【栈】最小栈
- 7.4 【栈】逆波兰表达式求值
- 7.5 【栈】基本计算器
- 8 链表
- 8.1 【双指针】环形链表
- 8.2 【双指针】两数相加
- 8.3 【双指针】合并两个有序链表
- 8.4 【哈希表】随机链表的复制
- 8.5 【链表】反转链表 II
- 8.6 【链表】K 个一组翻转链表
- 8.7 【双指针】删除链表的倒数第 N 个结点
- 8.8 【双指针】删除排序链表中的重复元素 II
- 8.9 【双指针】旋转链表
- 8.10 【双链表】分隔链表
- 8.11 【双向链表】【哈希表】LRU 缓存
7 栈
7.1 【哈希表】有效的括号
题目地址:https://leetcode.cn/problems/valid-parentheses/description/?envType=study-plan-v2&envId=top-interview-150
先构建一个括号的哈希表,引入 # \# #是防止只有右括号时无法与栈顶元素进行匹配。
class Solution:def isValid(self, s: str) -> bool:dict = {'(':')','{':'}','[':']','#':'#'}stack = ['#']for c in s:if c in dict:stack.append(c)else:if dict[stack.pop()] != c:return Falsereturn len(stack) == 1
7.2【栈】简化路径
题目地址:https://leetcode.cn/problems/simplify-path/description/?envType=study-plan-v2&envId=top-interview-150
按照规则构建栈即可,注意遇到 . . .. ..时要把栈顶元素出栈。
class Solution:def simplifyPath(self, path: str) -> str:stack = []items = path.split("/")for item in items:if stack and item == "..":stack.pop()else:if item not in [".","..",""]:stack.append(item)return "/" + "/".join(stack)
7.3 【栈】最小栈
题目地址:https://leetcode.cn/problems/min-stack/description/?envType=study-plan-v2&envId=top-interview-150
详见代码。
class MinStack:def __init__(self):self.num = []def push(self, val: int) -> None:self.num.append(val)def pop(self) -> None:self.num.pop()def top(self) -> int:return self.num[-1]def getMin(self) -> int:return min(self.num)# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(val)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.getMin()
7.4 【栈】逆波兰表达式求值
题目地址:https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/?envType=study-plan-v2&envId=top-interview-150
构建数字栈,遇到运算符将栈中最顶上的两个元素进行计算再压栈,最后输出栈顶元素。
class Solution:def evalRPN(self, tokens: List[str]) -> int:num = []for i in range(len(tokens)):if tokens[i] not in ["+","-","*","/"]:num.append(tokens[i])else:x2 = int(float(num.pop()))x1 = int(float(num.pop()))if tokens[i] == "+":x = x1 + x2num.append(str(x))elif tokens[i] == "-":x = x1 - x2num.append(str(x))elif tokens[i] == "*":x = x1 * x2num.append(str(x))elif tokens[i] == "/":x = x1 / x2num.append(str(x))return int(float(num.pop()))
7.5 【栈】基本计算器
题目地址:https://leetcode.cn/problems/basic-calculator/description/?envType=study-plan-v2&envId=top-interview-150
设置当前数字numnumnum和符号判断signsignsign(统一为加法):
- 如果当前是数字,更新当前数字 n u m num num;
- 如果当前是操作符 + + +或者 − - −,更新当前计算结果 a n s ans ans,并把当前数字 n u m num num设为 0 0 0, s i g n sign sign根据相应的操作符设置为 1 1 1或者 − 1 −1 −1,继续循环;
- 如果当前是 ( ( (,那么说明遇到了左括号,而后面括号里的内容需要先计算,这时把 a n s , s i g n ans,sign ans,sign进栈,更新 a n s ans ans和 s i g n sign sign为新的循环开始;
- 如果当前是 ) ) ),那么说明遇到了右括号,即当前括号里的内容已经计算完毕,所以要把之前的结果出栈,然后计算整个表达式的结果;
最后,当字符串遍历结束的时候,需要把最后的一个 n u m num num也更新到 a n s ans ans中。
class Solution:def calculate(self, s: str) -> int:ans,num,sign = 0,0,1stack = []for c in s:if c >= "0" and c <= "9":num = num*10 + int(c)elif c == "+" or c == "-":ans += num*signnum = 0sign = 1 if c == "+" else -1elif c == "(":stack.append(ans)stack.append(sign)ans = 0sign = 1elif c == ")":ans += num*signnum = 0ans = ans * stack.pop()ans = ans + stack.pop()ans += num*signreturn ans
8 链表
8.1 【双指针】环形链表
题目地址:https://leetcode.cn/problems/linked-list-cycle/description/?envType=study-plan-v2&envId=top-interview-150
设置快慢指针,快指针一次走两步,慢指针一次走一步,如果快慢指针相遇,则说明遇到了环。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = Noneclass Solution:def hasCycle(self, head: Optional[ListNode]) -> bool:fp,sp = head,headwhile fp and fp.next:fp = fp.next.nextsp = sp.nextif sp == fp:return Truereturn False
8.2 【双指针】两数相加
题目地址:https://leetcode.cn/problems/add-two-numbers/description/?envType=study-plan-v2&envId=top-interview-150
方法一:将两个链表的数字分别计算出来,相加,然后按照结果构造链表。
方法二:直接计算两个链表各自位的和,设置进位标志,然后诸位构造链表。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next# 方法一
class Solution:def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:x1,x2 = 0,0l1p,l2p = l1,l2i,j = 1,1while l1p:x1 += i*l1p.vall1p = l1p.nexti *= 10while l2p:x2 += j*l2p.vall2p = l2p.nextj *= 10x = x1 + x2ans = l = ListNode()if x == 0:l.next = ListNode(0)else:while x:l.next = ListNode(x%10)x = x // 10l = l.nextreturn ans.next
# 方法二
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:ans = l = ListNode()sign = 0while l1 or l2:l1_num = l1.val if l1 else 0l2_num = l2.val if l2 else 0num = l1_num + l2_num + signl.next = ListNode(num%10)sign = num//10l = l.nextif l1:l1 = l1.nextif l2:l2 = l2.nextif sign:l.next = ListNode(sign)return ans.next
8.3 【双指针】合并两个有序链表
题目地址:https://leetcode.cn/problems/merge-two-sorted-lists/description/?envType=study-plan-v2&envId=top-interview-150
详见代码。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:l1p,l2p = list1,list2ans = l = ListNode()while l1p and l2p:if l1p.val <= l2p.val:l.next = ListNode(l1p.val)l1p = l1p.nextl = l.nextelse:l.next = ListNode(l2p.val)l2p = l2p.nextl = l.nextwhile l1p:l.next = ListNode(l1p.val)l1p = l1p.nextl = l.nextwhile l2p:l.next = ListNode(l2p.val)l2p = l2p.nextl = l.nextreturn ans.next
8.4 【哈希表】随机链表的复制
题目地址:https://leetcode.cn/problems/copy-list-with-random-pointer/description/?envType=study-plan-v2&envId=top-interview-150
利用哈希表,首先构建链表中的每个结点,再根据哈希表中的结点分别构造 n e x t next next和 r a n d o m random random引用,相当于对原始链表遍历两次。
"""
# Definition for a Node.
class Node:def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):self.val = int(x)self.next = nextself.random = random
"""class Solution:def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':dict = {}cur = headwhile cur:dict[cur] = Node(cur.val)cur = cur.nextcur = headwhile cur:dict[cur].next = dict.get(cur.next)dict[cur].random = dict.get(cur.random)cur = cur.nextif not head:return headelse:return dict[head]
8.5 【链表】反转链表 II
题目地址:https://leetcode.cn/problems/reverse-linked-list-ii/description/?envType=study-plan-v2&envId=top-interview-150
超赞题解+配图:点击查看
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]:ans = pre = ListNode()ans.next = headcnt = 1while pre.next and cnt < left:pre = pre.nextcnt += 1cur = pre.nexttail = curwhile cur and cnt <= right:nxt = cur.nextcur.next = pre.nextpre.next = curtail.next = nxtcur = nxtcnt += 1return ans.next
8.6 【链表】K 个一组翻转链表
题目地址:https://leetcode.cn/problems/reverse-nodes-in-k-group/description/?envType=study-plan-v2&envId=top-interview-150
在上一题的基础上,先计算链表长度,然后按照 k k k进行分组,每一组进行翻转。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:# singly-linked lengthlength = 0tmp = headwhile tmp:length += 1tmp = tmp.next# reverseans = pre = ListNode()ans.next = headleft,right = 1,kwhile right <= length:pre = anscnt = 1while pre.next and cnt < left:pre = pre.nextcnt += 1cur = pre.nexttail = curwhile cur and cnt <= right:nxt = cur.nextcur.next = pre.nextpre.next = curtail.next = nxtcur = nxtcnt += 1left += kright += kreturn ans.next
8.7 【双指针】删除链表的倒数第 N 个结点
题目地址:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/?envType=study-plan-v2&envId=top-interview-150
设置双指针,先让一个指针往前走 n n n步,再让两个指针一起走, r i g h t right right指针走到头就是要删除的结点。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:left = right = headcnt = 0while cnt < n:right = right.nextcnt += 1if not right:return head.nextwhile right.next:left = left.nextright = right.nextleft.next = left.next.nextreturn head
8.8 【双指针】删除排序链表中的重复元素 II
题目地址:https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/description/?envType=study-plan-v2&envId=top-interview-150
设置快慢指针,记录重复的数字,然后删除相应结点,但是要注意链表要多设置一个头结点,防止出现链表中元素都一样的情况。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = nextclass Solution:def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:ans = ListNode()ans.next = headslow,fast = ans,ans.nextwhile fast:while fast and fast.next and fast.val == fast.next.val:repute_num = fast.valwhile fast and fast.val == repute_num:fast = fast.nextslow.next = fastif fast:slow = fastfast = fast.nextreturn ans.next
8.9 【双指针】旋转链表
题目地址:https://leetcode.cn/problems/rotate-list/description/?envType=study-plan-v2&envId=top-interview-150
先计算链表长度,找出右移的相对最小数,然后根据这个最小数设置双指针,找到倒数第 k k k个元素,将链表一分两半重新拼接。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:# lengthlength = 0tmp = headwhile tmp:length += 1tmp = tmp.nextif length != 0:k %= length# particular situationif not head or not head.next or k == 0:return head# rotate listslow,fast = head,headwhile k:fast = fast.nextk -= 1while fast.next:slow = slow.nextfast = fast.nextans = slow.nextslow.next = Nonefast.next = headreturn ans
8.10 【双链表】分隔链表
题目地址:https://leetcode.cn/problems/partition-list/description/?envType=study-plan-v2&envId=top-interview-150
设置两个链表,一个链表存放比 x x x小的元素,另一个链表存放大于等于 x x x的元素,然后将两个链表合并即可,最后注意要让 b i g big big的下一个结点为空,否则容易成环。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]:if not head or not head.next:return headsml = sml_list = ListNode()big = big_list = ListNode()while head:if head.val < x:sml.next = headsml = sml.nextelse:big.next = headbig = big.nexthead = head.nextsml.next = big_list.nextbig.next = Nonereturn sml_list.next
8.11 【双向链表】【哈希表】LRU 缓存
题目地址:https://leetcode.cn/problems/lru-cache/description/?envType=study-plan-v2&envId=top-interview-150
详见代码,通过构造双向链表和哈希表来模拟LRU缓存,要注意及时将最久未使用的结点移动到链表末尾,便于删除,这里采取的措施是定义一个函数,将链表中的某个结点移动到链表末尾,在 g e t get get和 p u t put put方法中都会遇到这种情况。
class ListNode:# 双向链表构建def __init__(self, key=None, value=None):self.key = keyself.value = valueself.prev = Noneself.next = Noneclass LRUCache:def __init__(self, capacity: int):self.capacity = capacityself.hashmap = {}# 头结点head和尾结点tailself.head = ListNode()self.tail = ListNode()# 初始化双向链表self.head.next = self.tailself.tail.prev = self.head# 将链表中的某个结点移到链表末尾taildef move_to_tail(self, key):node = self.hashmap[key]# 取出node结点node.next.prev = node.prevnode.prev.next = node.next# 移到末尾node.next = self.tailnode.prev = self.tail.prevself.tail.prev.next = nodeself.tail.prev = nodedef get(self, key: int) -> int:if key in self.hashmap:self.move_to_tail(key)res = self.hashmap.get(key)if res == None:return -1else:return res.valuedef put(self, key: int, value: int) -> None:if key in self.hashmap:self.hashmap[key].value = valueself.move_to_tail(key)else:if len(self.hashmap) == self.capacity:# 删除最久未访问结点self.hashmap.pop(self.head.next.key)self.head.next = self.head.next.nextself.head.next.prev = self.headnewNode = ListNode(key, value)self.hashmap[key] = newNodenewNode.prev = self.tail.prevnewNode.next = self.tailself.tail.prev.next = newNodeself.tail.prev = newNode# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)