【leetcode刷题之路】面试经典150题(4)——栈+链表

news/2024/7/7 21:33:42

文章目录

      • 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 False
        return 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 + x2
                    num.append(str(x))
                elif tokens[i] == "-":
                    x = x1 - x2
                    num.append(str(x))
                elif tokens[i] == "*":
                    x = x1 * x2
                    num.append(str(x))
                elif tokens[i] == "/":
                    x = x1 / x2
                    num.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,1
        stack = []

        for c in s:
            if c >= "0" and c <= "9":
                num = num*10 + int(c)
            elif c == "+" or c == "-":
                ans += num*sign
                num = 0
                sign = 1 if c == "+" else -1
            elif c == "(":
                stack.append(ans)
                stack.append(sign)
                ans = 0
                sign = 1
            elif c == ")":
                ans += num*sign
                num = 0
                ans = ans * stack.pop()
                ans = ans + stack.pop()
        ans += num*sign
        return 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 = None

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        fp,sp = head,head
        while fp and fp.next:
            fp = fp.next.next
            sp = sp.next
            if sp == fp:
                return True
        return 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,0
        l1p,l2p = l1,l2
        i,j = 1,1
        while l1p:
            x1 += i*l1p.val
            l1p = l1p.next
            i *= 10
        while l2p:
            x2 += j*l2p.val
            l2p = l2p.next
            j *= 10
        x = x1 + x2
        ans = l = ListNode()
        if x == 0:
            l.next = ListNode(0)
        else:
            while x:
                l.next = ListNode(x%10)
                x = x // 10
                l = l.next
        return 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 = 0

        while l1 or l2:
            l1_num = l1.val if l1 else 0
            l2_num = l2.val if l2 else 0
            num = l1_num + l2_num + sign
            l.next = ListNode(num%10)
            sign = num//10
            l = l.next
            if l1:
                l1 = l1.next
            if l2:
                l2 = l2.next
        if 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,list2
        ans = l = ListNode()
        while l1p and l2p:
            if l1p.val <= l2p.val:
                l.next = ListNode(l1p.val)
                l1p = l1p.next
                l = l.next
            else:
                l.next = ListNode(l2p.val)
                l2p = l2p.next
                l = l.next
        while l1p:
            l.next = ListNode(l1p.val)
            l1p = l1p.next
            l = l.next
        while l2p:
            l.next = ListNode(l2p.val)
            l2p = l2p.next
            l = l.next
        return 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 = next
        self.random = random
"""

class Solution:
    def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
        dict = {}
        cur = head
        while cur:
            dict[cur] = Node(cur.val)
            cur = cur.next
        cur = head
        while cur:
            dict[cur].next = dict.get(cur.next)
            dict[cur].random = dict.get(cur.random)
            cur = cur.next
        if not head:
            return head
        else:
            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 = head
        cnt = 1

        while pre.next and cnt < left:
            pre = pre.next
            cnt += 1
        
        cur = pre.next
        tail = cur
        while cur and cnt <= right:
            nxt = cur.next
            cur.next = pre.next
            pre.next = cur
            tail.next = nxt
            cur = nxt
            cnt += 1
        return 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 length
        length = 0
        tmp = head
        while tmp:
            length += 1
            tmp = tmp.next
        # reverse
        ans = pre = ListNode()
        ans.next = head
        left,right = 1,k

        while right <= length:
            pre = ans
            cnt = 1
            while pre.next and cnt < left:
                pre = pre.next
                cnt += 1
            cur = pre.next
            tail = cur
            while cur and cnt <= right:
                nxt = cur.next
                cur.next = pre.next
                pre.next = cur
                tail.next = nxt
                cur = nxt
                cnt += 1
            left += k
            right += k
        return 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 = head
        cnt = 0
        while cnt < n:
            right = right.next
            cnt += 1
        
        if not right:
            return head.next
        
        while right.next:
            left = left.next
            right = right.next
        left.next = left.next.next
        return 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 = next

class Solution:
    def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
        ans = ListNode()
        ans.next = head

        slow,fast = ans,ans.next
        while fast:
            while fast and fast.next and fast.val == fast.next.val:
                repute_num = fast.val
                while fast and fast.val == repute_num:
                    fast = fast.next
                slow.next = fast
            if fast:
                slow = fast
                fast = fast.next
        return 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]:
        # length
        length = 0
        tmp = head
        while tmp:
            length += 1
            tmp = tmp.next
        if length != 0:
            k %= length
        # particular situation
        if not head or not head.next or k == 0:
            return head
        # rotate list
        slow,fast = head,head
        while k:
            fast = fast.next
            k -= 1
        while fast.next:
            slow = slow.next
            fast = fast.next
        ans = slow.next
        slow.next = None
        fast.next = head
        return 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 head
        sml = sml_list = ListNode()
        big = big_list = ListNode()
        while head:
            if head.val < x:
                sml.next = head
                sml = sml.next
            else:
                big.next = head
                big = big.next
            head = head.next
        sml.next = big_list.next
        big.next = None
        return 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 = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.hashmap = {}
        # 头结点head和尾结点tail
        self.head = ListNode()
        self.tail = ListNode()
        # 初始化双向链表
        self.head.next = self.tail
        self.tail.prev = self.head

    # 将链表中的某个结点移到链表末尾tail
    def move_to_tail(self, key):
        node = self.hashmap[key]
        # 取出node结点
        node.next.prev = node.prev
        node.prev.next = node.next
        # 移到末尾
        node.next = self.tail
        node.prev = self.tail.prev
        self.tail.prev.next = node
        self.tail.prev = node

    def get(self, key: int) -> int:
        if key in self.hashmap:
            self.move_to_tail(key)
        res = self.hashmap.get(key)
        if res == None:
            return -1
        else:
            return res.value

    def put(self, key: int, value: int) -> None:
        if key in self.hashmap:
            self.hashmap[key].value = value
            self.move_to_tail(key)
        else:
            if len(self.hashmap) == self.capacity:
                # 删除最久未访问结点
                self.hashmap.pop(self.head.next.key)
                self.head.next = self.head.next.next
                self.head.next.prev = self.head
            newNode = ListNode(key, value)
            self.hashmap[key] = newNode
            newNode.prev = self.tail.prev
            newNode.next = self.tail
            self.tail.prev.next = newNode
            self.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)

http://lihuaxi.xjx100.cn/news/2041673.html

相关文章

穿越Redis单线程迷雾:从面试场景到技术内核的解读

目录 ​编辑 前言 Redis中的多线程 I/O多线程 Redis中的多进程 结论 延伸阅读 前言 很多人都遇到过这么一道面试题&#xff1a;Redis是单线程还是多线程&#xff1f;这个问题既简单又复杂。说他简单是因为大多数人都知道Redis是单线程&#xff0c;说复杂是因为这个答案…

五、全局scss变量定义及使用

定义 variable.scss 存放全局变量 // base color $blue:#324157; $light-blue:#3A71A8; $red:#C03639; $pink: #E65D6E; $green: #30B08F; $tiffany: #4AB7BD; $yellow:#FEC171; $panGreen: #30B08F;// 默认菜单主题风格 $base-menu-color:#bfcbd9; $base-menu-color-active:#f…

Linux:Jenkins用户权限和管理

1.下载插件 由于Jenkins的默认权限管理并不是很精细所以我们安装一个插件进行权限的一个管理 插件名称为&#xff1a;Role-based Authorization Strategy 安装完插件我们再去配置一下 进入全局安全配置 选择这个Role-Based Strategy策略然后保存 2.创建角色 我们这里主要使…

dp入门(模板题)

解法一&#xff1a; 双指针&#xff0c;没必要开数组直接边输边算&#xff0c;max至少要2个数&#xff0c;补一个数时的特判 #include <iostream> #include <vector> #include <algorithm> using namespace std; #define endl \nint main() {ios::sync_wit…

C语言自定义类型:结构体的使用及其内存对齐【超详细建议点赞收藏】

目录 1. 结构体类型的声明1.1 结构的声明1.2 结构体变量的创建和初始化1.3 结构的特殊声明---匿名结构体1.4 结构的自引用 2.结构体内存对齐&#xff08;重点&#xff01;&#xff01;&#xff09;2.1 对齐规则2.2 例题讲解2.3 为什么存在内存对齐&#xff1f;2.4 修改默认对齐…

二进制、十进制、八进制、十六进制 各代表的英文字母是什么

二进制、十进制、八进制、十六进制 各代表的英文字母是什么 AllenLeungX 于 2019-07-08 08:42:47 发布 分类专栏&#xff1a; 其它 文章标签&#xff1a; 二进制、十进制、八进制、十六进制 英文和缩写 二进制是Binary&#xff0c;简写为B 八进制是Octal&#xff0c;简写为O 十…

MYSQL--存储过程操作

一&#xff1a;概念&#xff1a; 存储过程实际上对标了JAVA当中的方法&#xff0c;两者是相似的&#xff0c;同时需要注意的一点是&#xff0c;MYSQL仅仅在5.0版本之后才出现这种存储操作的过程&#xff1b; 优点&#xff1a; 1.存储过程能够让运行的速度变得更加迅速&#xff…

Cartographer框架简述

catographer框架分为前端和后端 前端包括雷达数据处理&#xff1b;位姿预测&#xff1b;扫描匹配和栅格地图更新。 后端包括后端&#xff1a;线程池任务与调度&#xff1b;向位姿图添加节点&#xff0c;计算节点的子图内约束和子图间约束&#xff08;回环检测&#xff09;&…