当前位置: 首页 > article >正文

python算法和数据结构刷题[2]:链表、队列、栈

链表

链表的节点定义:

class Node():
    def __init__(self,item,next=None):
        self.item=item
        self.next=None

删除节点:

删除节点前的节点的next指针指向删除节点的后一个节点

添加节点:

单链表

class Node():
    """单链表的结点"""

    def __init__(self, item):
        # item存放数据元素
        self.item = item
        # next是下一个节点的标识
        self.next = None
class SingleLinkList():
    """单链表"""

    def __init__(self):
        self._head = None

    def is_empty(self):
        """判断链表是否为空"""
        return self._head is None

    def length(self):
        """链表长度"""
        # 初始指针指向head
        cur = self._head
        count = 0
        # 指针指向None 表示到达尾部
        while cur is not None:
            count += 1
            # 指针下移
            cur = cur.next
        return count

    def items(self):
        """遍历链表"""
        # 获取head指针
        cur = self._head
        # 循环遍历
        while cur is not None:
            # 返回生成器
            yield cur.item
            # 指针下移
            cur = cur.next

    def add(self, item):
        """向链表头部添加元素"""
        node = Node(item)
        # 新结点指针指向原头部结点
        node.next = self._head
        # 头部结点指针修改为新结点
        self._head = node

    def append(self, item):
        """尾部添加元素"""
        node = Node(item)
        # 先判断是否为空链表
        if self.is_empty():
            # 空链表,_head 指向新结点
            self._head = node
        else:
            # 不是空链表,则找到尾部,将尾部next结点指向新结点
            cur = self._head
            while cur.next is not None:
                cur = cur.next
            cur.next = node

    def insert(self, index, item):
        """指定位置插入元素"""
        # 指定位置在第一个元素之前,在头部插入
        if index <= 0:
            self.add(item)
        # 指定位置超过尾部,在尾部插入
        elif index > (self.length() - 1):
            self.append(item)
        else:
            # 创建元素结点
            node = Node(item)
            cur = self._head
            # 循环到需要插入的位置
            for i in range(index - 1):
                cur = cur.next
            node.next = cur.next
            cur.next = node

    def remove(self, item):
        """删除节点"""
        cur = self._head
        pre = None
        while cur is not None:
            # 找到指定元素
            if cur.item == item:
                # 如果第一个就是删除的节点
                if not pre:
                    # 将头指针指向头节点的后一个节点
                    self._head = cur.next
                else:
                    # 将删除位置前一个节点的next指向删除位置的后一个节点
                    pre.next = cur.next
                return True
            else:
                # 继续按链表后移节点
                pre = cur
                cur = cur.next

    def find(self, item):
        """查找元素是否存在"""
        return item in self.items()

双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。

 循环链表:链表首尾相连。

链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。

class Node(object):
    """单链表的结点"""

    def __init__(self, item):
        # item存放数据元素
        self.item = item
        # next是下一个节点的标识
        self.next = None
class SingleCycleLinkList(object):

    def __init__(self):
        self._head = None

    def is_empty(self):
        """判断链表是否为空"""
        return self._head is None

    def length(self):
        """链表长度"""
        # 链表为空
        if self.is_empty():
            return 0
        # 链表不为空
        count = 1
        cur = self._head
        while cur.next != self._head:
            count += 1
            # 指针下移
            cur = cur.next
        return count

    def items(self):
        """ 遍历链表 """
        # 链表为空
        if self.is_empty():
            return
        # 链表不为空
        cur = self._head
        while cur.next != self._head:
            yield cur.item
            cur = cur.next
        yield cur.item

    def add(self, item):
        """ 头部添加结点"""
        node = Node(item)
        if self.is_empty():  # 为空
            self._head = node
            node.next = self._head
        else:
            # 添加结点指向head
            node.next = self._head
            cur = self._head
            # 移动结点,将末尾的结点指向node
            while cur.next != self._head:
                cur = cur.next
            cur.next = node
        # 修改 head 指向新结点
        self._head = node

    def append(self, item):
        """尾部添加结点"""
        node = Node(item)
        if self.is_empty():  # 为空
            self._head = node
            node.next = self._head
        else:
            # 寻找尾部
            cur = self._head
            while cur.next != self._head:
                cur = cur.next
            # 尾部指针指向新结点
            cur.next = node
            # 新结点指针指向head
            node.next = self._head

    def insert(self, index, item):
        """ 指定位置添加结点"""
        if index <= 0:  # 指定位置小于等于0,头部添加
            self.add(item)
        # 指定位置大于链表长度,尾部添加
        elif index > self.length() - 1:
            self.append(item)
        else:
            node = Node(item)
            cur = self._head
            # 移动到添加结点位置
            for i in range(index - 1):
                cur = cur.next
            # 新结点指针指向旧结点
            node.next = cur.next
            # 旧结点指针 指向 新结点
            cur.next = node

    def remove(self, item):
        """ 删除一个结点 """
        if self.is_empty():
            return
        cur = self._head
        pre = Node
        # 第一个元素为需要删除的元素
        if cur.item == item:
            # 链表不止一个元素
            if cur.next != self._head:
                while cur.next != self._head:
                    cur = cur.next
                # 尾结点指向 头部结点的下一结点
                cur.next = self._head.next
                # 调整头部结点
                self._head = self._head.next
            else:
                # 只有一个元素
                self._head = None
        else:
            # 不是第一个元素
            pre = self._head
            while cur.next != self._head:
                if cur.item == item:
                    # 删除
                    pre.next = cur.next
                    return True
                else:

                    pre = cur  # 记录前一个指针
                    cur = cur.next  # 调整指针位置
        # 当删除元素在末尾
        if cur.item == item:
            pre.next = self._head
            return True

    def find(self, item):
        """ 查找元素是否存在"""
        return item in self.items()

 160. 相交链表 - 力扣(LeetCode)

哈希表(集合)

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        s = set()
        p, q = headA, headB
        while p:
            s.add(p)
            p = p.next
        while q:
            if q in s:
                return q
            q = q.next
        return None

时间复杂度:O(n)

空间复杂度:O(n)

双指针

短的链表会先指向另外一个链表的头节点使得步差消除

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

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        pA, pB = headA, headB
        while pA != pB:
            pA = headB if not pA else pA.next
            pB = headA if not pB else pB.next
        return pA

时间复杂度:O(N+M)

空间复杂度:O(1)

206. 反转链表 - 力扣(LeetCode)

反转链表其实是一个反转指针方向的过程

迭代

迭代需要三个指针,pre,cur,nxt,分别按顺序指向三个节点
三个指针的初始化:pre指向空节点,cur指向头结点head,nxt指向head.next
因为head.next可能不存在,nxt在循环中定义,这样如果head为空就不会进入循环
迭代过程
nxt指向cur.next
cur.next指向pre


pre移动到cur位置


cur移动到nxt位置

开始新迭代

当cur为空时,返回pre

def reverseList(self, head: ListNode) -> ListNode:
    prev, curr = None, head
    while curr is not None:
        next = curr.next
        curr.next = prev
        prev = curr
        curr = next
    return prev
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)(三个变量都是必要的,并且在整个函数执行过程中它们的数量是固定的,不会随着输入链表的大小而改变)

递归

每一层递归,该层递归中的head会让下一个节点指向自己,head.next.next = head;然后head自己指向空。以此达到反转的目的。

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        newHead = self.reverseList(head.next)
        head.next.next = head
        head.next = None
        return newHead
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

 

234. 回文链表 - 力扣(LeetCode)

翻转链表,再和原链表各元素按顺序对比;
将链表各节点值加入数组,再利用左右指针向中间同时滑动,判断左右指针位置处的值是否相等;
递归法,后序遍历链表,判断倒序的节点值和正序的节点值是否相同
快慢指针先找到链表的中间位置,将后半段链表翻转,再按顺序对比各元素是否相同,最终恢复原链表(再进行一次翻转)
这几种方法的时间复杂度都是O(N),前三种方法的空间复杂度是O(N),第四种方法的空间复杂度是O(1)

递归法

链表是一种兼顾迭代和递归的数据结构,因此链表可以进行先序遍历和后序遍历(树就是多叉链表)。后序遍历链表,可以使链表隐式的倒序访问节点,访问过程中,维护一个正序的指针即可。

class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        # 初始化左指针,指向链表头部
        self.left = head

        # 递归函数,用于检查链表是否为回文
        def check_palindrome(right):
            # 如果右指针为空,说明已经到达链表末尾,返回True
            if not right:
                return True
            
            # 递归检查下一个节点,如果发现不是回文,则直接返回False
            res = check_palindrome(right.next)
            
            # 比较当前左指针和右指针的值,如果不相等,则不是回文
            if self.left.val != right.val:
                return False
            
            # 将左指针向右移动一位
            self.left = self.left.next
            
            # 返回子问题的结果
            return res

        # 调用递归函数检查整个链表是否为回文
        return check_palindrome(head)

 在第一次回溯的时候 self.left 指向第一个节点,right 指向第五个节点

双指针快慢指针

先利用快慢指针,找到链表的后半段,将链表的后半段翻转,再按顺序对比前半段和后半段的值是否一致,最终恢复原链表(后半段再翻转一次)即可。

注意:链表长度有奇数和偶数两种情况,对于奇数,如1->2->3->2->1,此时快指针fast会停在最后的1处,满指针slow停在中间的3处,这时需要对slow.next的链表进行翻转。

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

class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        # 辅助函数,用于反转链表的一部分
        def reverse(node):
            prev = None
            current = node
            while current:
                next_node = current.next
                current.next = prev
                prev = current
                current = next_node
            return prev
        
        # 辅助函数,用于找到链表的中间节点
        def find_middle(node):
            slow = fast = node
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
            return slow
        
        # 如果链表为空或只有一个节点,则直接返回True
        if not head or not head.next:
            return True
        
        # 使用快慢指针找到链表的中间节点
        middle = find_middle(head)
        
        # 反转链表的后半部分
        second_half = reverse(middle)
        
        # 比较前半部分和反转后的后半部分
        first_half = head
        is_palindrome = True
        while is_palindrome and second_half:
            if first_half.val != second_half.val:
                is_palindrome = False
            first_half = first_half.next
            second_half = second_half.next
        
        # 可选:将链表恢复到原始状态
        reverse(second_half)
        
        return is_palindrome

# 使用示例
# solution = Solution()
# head = ListNode(1, ListNode(2, ListNode(2, ListNode(1))))
# print(solution.isPalindrome(head))  # 输出: True

 141. 环形链表 - 力扣(LeetCode)

哈希表

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        seen = set()
        while head:
            if head in seen:
                return True
            seen.add(head)
            head = head.next
        return False

时间复杂度:O(N),其中 N 是链表中的节点数。最坏情况下我们需要遍历每个节点一次。

空间复杂度:O(N),其中 N 是链表中的节点数。主要为哈希表的开销,最坏情况下我们需要将每个节点插入到哈希表中一次。

双指针:快慢指针

定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        if not head or not head.next:
            return False
        
        slow = head
        fast = head.next

        while slow != fast:
            if not fast or not fast.next:
                return False
            slow = slow.next
            fast = fast.next.next
        
        return True

 142. 环形链表 II - 力扣(LeetCode)

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

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return None
        
        slow = head
        fast = head.next

        while slow != fast:
            if not fast or not fast.next:
                return None
            slow = slow.next
            fast = fast.next.next
        
        # 当slow和fast相遇时,使用一个新指针ptr从头开始,与slow以相同速度移动
        ptr = head
        while ptr != slow:
            ptr = ptr.next
            slow = slow.next
        
        # ptr和slow相遇的节点即为环的起始节点
        return ptr

 21. 合并两个有序链表 - 力扣(LeetCode)

递归

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if l1 is None:
            return l2
        elif l2 is None:
            return l1
        elif l1.val < l2.val:
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1, l2.next)
            return l2

迭代

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

class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        # 创建一个哨兵节点,以简化合并操作
        prehead = ListNode(-1)
        
        # 初始化prev指针,它将指向合并后链表的最后一个节点
        prev = prehead
        
        # 当两个链表都不为空时,迭代比较它们
        while l1 and l2:
            if l1.val < l2.val:
                # 如果l1的当前节点值小于l2的当前节点值,将l1的节点链接到prev的后面
                prev.next = l1
                l1 = l1.next  # 移动l1指针到下一个节点
            else:
                # 如果l2的当前节点值小于或等于l1的当前节点值,将l2的节点链接到prev的后面
                prev.next = l2
                l2 = l2.next  # 移动l2指针到下一个节点
            prev = prev.next  # 移动prev指针到合并链表的最后一个节点
        
        # 如果l1链表还有剩余的节点,将它们链接到合并链表的末尾
        if l1:
            prev.next = l1
        
        # 如果l2链表还有剩余的节点,将它们链接到合并链表的末尾
        if l2:
            prev.next = l2
        
        # 返回哨兵节点的下一个节点,即合并后链表的头节点
        return prehead.next

2. 两数相加 - 力扣(LeetCode)

递归

    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode, carry: int = 0) -> ListNode:
        # 计算当前位的和以及进位
        val = l1.val + l2.val + carry
        carry = val // 10
        
        # 创建当前位的节点
        current = ListNode(val % 10)
        
        # 如果两个链表的下一位都不为空,递归计算下一位的和
        if l1.next or l2.next:
            if not l1.next:
                l1.next = ListNode(0)  # 如果l1短,补0
            if not l2.next:
                l2.next = ListNode(0)  # 如果l2短,补0
            current.next = self.addTwoNumbers(l1.next, l2.next, carry)
        
        # 如果进位不为0,且两个链表的下一位都为空,则添加一个新节点
        elif carry:
            current.next = ListNode(carry)
        
        return current

 19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

循环迭代

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy = ListNode(0)
        dummy.next = head 

        #step1: 获取链表长度
        cur, length = head, 0 
        while cur:
            length += 1
            cur = cur.next 
        
        #step2: 找到倒数第N个节点的前面一个节点
        cur = dummy
        for _ in range(length - n):
            cur = cur.next
        
        #step3: 删除节点,并重新连接
        cur.next = cur.next.next
        return dummy.next 

 双指针

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy = ListNode(0)
        dummy.next = head 
        
        #step1: 快指针先走n步
        slow, fast = dummy, dummy
        for _ in range(n):
            fast = fast.next 

        #step2: 快慢指针同时走,直到fast指针到达尾部节点,此时slow到达倒数第N个节点的前一个节点
        while fast and fast.next:
            slow, fast = slow.next, fast.next 
        
        #step3: 删除节点,并重新连接
        slow.next = slow.next.next 
        return dummy.next 

 递归迭代

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

class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        if not head:
            self.count = 0
            return head
        head.next = self.removeNthFromEnd(head.next, n)
        self.count += 1
        if self.count == n:
            return head.next
        else:
            return head

 24. 两两交换链表中的节点 - 力扣(LeetCode)

递归和迭代之后再看

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

class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        # 如果链表为空或者只有一个节点,则无法交换,直接返回 head
        if not head or not head.next:
            return head
        
        # 保存当前 head 的下一个节点,这个节点将成为新的头节点
        newHead = head.next
        
        # 递归地交换剩下的节点,并将返回的节点作为当前 head 的下一个节点
        # 例如,在第一次调用时,这里将交换 C 和 D,并将 D 作为 A 的下一个节点
        head.next = self.swapPairs(newHead.next)
        
        # 将新的头节点(原 head 的下一个节点)的下一个节点设置为 head
        # 这一步完成了两个节点的交换
        newHead.next = head
        
        # 返回新的头节点,这是交换后的第二个节点
        return newHead
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
node1.next = node2
node2.next = node3
node3.next = node4

# 实例化 Solution 并调用 swapPairs
solution = Solution()
new_head = solution.swapPairs(node1)

# 打印交换后的链表
current = new_head
while current:
    print(current.val, end=" -> ")
    current = current.next

148. 排序链表 - 力扣(LeetCode)

学排序的时候看

138. 随机链表的复制 - 力扣(LeetCode)

146. LRU 缓存 - 力扣(LeetCode)

 O(1) 时间复杂度内完成

在 Python 语言中,有一种结合了哈希表与双向链表的数据结构 OrderedDict,只需要短短的几行代码就可以完成本题。OrderedDict保持元素插入顺序的特性,允许我们将最近使用的元素移动到字典的末尾

from collections import OrderedDict

class LRUCache(OrderedDict):

    def __init__(self, capacity: int):
        super().__init__()
        self.capacity = capacity  # 初始化缓存容量

    def get(self, key: int) -> int:
        # 如果键不存在于缓存中,返回-1
        if key not in self:
            return -1
        # 将访问的键移动到字典的末尾,表示最近使用
        self.move_to_end(key)
        # 返回键对应的值
        return self[key]

    def put(self, key: int, value: int) -> None:
        # 如果键已存在,则移动到末尾
        if key in self:
            self.move_to_end(key)
        # 更新或添加键值对
        self[key] = value
        # 如果当前大小超过容量,则删除最久未使用的元素
        if len(self) > self.capacity:
            self.popitem(last=False)  # last=False 表示删除第一个元素,即最久未使用的元素

# 示例使用
lru = LRUCache(2)
lru.put(1, 1)
lru.put(2, 2)
print(lru.get(1))  # 输出 1
lru.put(3, 3)      # 这将使得键 2 被移除
print(lru.get(2))  # 输出 -1 (因为键 2 已经被移除)
lru.put(4, 4)      # 这将使得键 1 被移除
print(lru.get(1))  # 输出 -1 (因为键 1 已经被移除)
print(lru.get(3))  # 输出 3
print(lru.get(4))  # 输出 4

哈希表和双向链表 

class DLinkedNode:
    def __init__(self, key=0, value=0):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:

    def __init__(self, capacity: int):
        self.cache = dict()  # 哈希表,用于存储键和对应双向链表节点的映射
        # 使用伪头部和伪尾部节点来简化边界条件处理
        self.head = DLinkedNode()
        self.tail = DLinkedNode()
        self.head.next = self.tail
        self.tail.prev = self.head
        self.capacity = capacity  # 缓存容量
        self.size = 0  # 当前缓存中的元素数量

    def get(self, key: int) -> int:
        # 如果键不在缓存中,返回-1
        if key not in self.cache:
            return -1
        # 定位到键对应的节点,并将其移动到头部
        node = self.cache[key]
        self.moveToHead(node)
        return node.value  # 返回节点的值

    def put(self, key: int, value: int) -> None:
        # 如果键不在缓存中,创建新节点并添加到头部
        if key not in self.cache:
            node = DLinkedNode(key, value)
            self.cache[key] = node
            self.addToHead(node)
            self.size += 1
            # 如果超出容量,移除尾部节点
            if self.size > self.capacity:
                removed = self.removeTail()
                self.cache.pop(removed.key)
                self.size -= 1
        else:
            # 如果键在缓存中,更新值并将其移动到头部
            node = self.cache[key]
            node.value = value
            self.moveToHead(node)
    
    def addToHead(self, node):
        # 将节点添加到双向链表的头部
        node.prev = self.head
        node.next = self.head.next
        self.head.next.prev = node
        self.head.next = node
    
    def removeNode(self, node):
        # 从双向链表中移除节点
        node.prev.next = node.next
        node.next.prev = node.prev

    def moveToHead(self, node):
        # 移除节点后将其添加到头部
        self.removeNode(node)
        self.addToHead(node)

    def removeTail(self):
        # 移除尾部节点并返回
        node = self.tail.prev
        self.removeNode(node)
        return node

23. 合并 K 个升序链表 - 力扣(LeetCode)

优先队列

25. K 个一组翻转链表 - 力扣(LeetCode)


队列和栈

class Stack( ):
    # 初始化栈为空列表
    def __init__(self):
        self.items = []

    # 判断栈是否为空,返回布尔值
    def is_empty(self):
        return self.items == []

    # 返回栈顶元素
    def peek(self):
        return self.items[len(self.items) - 1]

    # 返回栈的大小
    def size(self):
        return len(self.items)

    # 把新的元素堆进栈里面(程序员喜欢把这个过程叫做压栈,入栈,进栈……)
    def push(self, item):
        self.items.append(item)

    # 把栈顶元素丢出去(程序员喜欢把这个过程叫做出栈……)
    def pop(self, item):
        return self.items.pop()


if __name__=="__main__":
    # 初始化一个栈对象
    my_stack = Stack()
    # 把'h'丢进栈里
    my_stack.push('h')
    # 把'a'丢进栈里
    my_stack.push('a')
    my_stack.push('c')
    my_stack.push('d')
    my_stack.push('e')
    # 看一下栈的大小(有几个元素)
    print(my_stack.size())
    # 打印栈顶元素
    print(my_stack.peek())
    # 把栈顶元素丢出去,并打印出来
    #print(my_stack.pop())
    # 再看一下栈顶元素是谁
    print(my_stack.peek())
    # 这个时候栈的大小是多少?
    print(my_stack.size())
    # 再丢一个栈顶元素
    #print(my_stack.pop())
    # 看一下栈的大小
    print(my_stack.size)
    # 栈是不是空了?
    print(my_stack.is_empty())

队列

from queue import Queue         #先进先出队列                                                          #LILO队列
q = Queue() #创建队列对象
q.put(0)    #在队列尾部插入元素
q.put(1)
q.put(2)
print('LILO队列',q.queue)  #查看队列中的所有元素
print(q.get())  #返回并删除队列头部元素
print(q.queue)

from queue import LifoQueue #LIFO队列 后进先出(Last In, First Out, LIFO)的队列
lifoQueue = LifoQueue()
lifoQueue.put(1)
lifoQueue.put(2)
lifoQueue.put(3)
print('LIFO队列',lifoQueue.queue)
lifoQueue.get() #返回并删除队列尾部元素
lifoQueue.get()
print(lifoQueue.queue)

from queue import PriorityQueue #优先队列
#素的出队顺序不是按照它们的入队顺序,而是按照它们的优先级来决定的。优先级最高的元素会最先被移除。
priorityQueue = PriorityQueue() #创建优先队列对象
priorityQueue.put(3)    #插入元素
priorityQueue.put(78)   #插入元素
priorityQueue.put(100)  #插入元素
print(priorityQueue.queue)  #查看优先级队列中的所有元素
priorityQueue.put(1)    #插入元素
priorityQueue.put(2)    #插入元素
print('优先级队列:',priorityQueue.queue)  #查看优先级队列中的所有元素
priorityQueue.get() #返回并删除优先级最低的元素
print('删除后剩余元素',priorityQueue.queue)
priorityQueue.get() #返回并删除优先级最低的元素
print('删除后剩余元素',priorityQueue.queue)  #删除后剩余元素
priorityQueue.get() #返回并删除优先级最低的元素
print('删除后剩余元素',priorityQueue.queue)  #删除后剩余元素
priorityQueue.get() #返回并删除优先级最低的元素
print('删除后剩余元素',priorityQueue.queue)  #删除后剩余元素
priorityQueue.get() #返回并删除优先级最低的元素
print('全部被删除后:',priorityQueue.queue)  #查看优先级队列中的所有元素

from collections import deque   #双端队列
dequeQueue = deque(['Eric','John','Smith'])
print(dequeQueue)
dequeQueue.append('Tom')    #在右侧插入新元素
dequeQueue.appendleft('Terry')  #在左侧插入新元素
print(dequeQueue)
dequeQueue.rotate(2)    #循环右移2次
print('循环右移2次后的队列',dequeQueue)
dequeQueue.popleft()    #返回并删除队列最左端元素
print('删除最左端元素后的队列:',dequeQueue)
dequeQueue.pop()    #返回并删除队列最右端元素
print('删除最右端元素后的队列:',dequeQueue)

20. 有效的括号 - 力扣(LeetCode)

栈 哈希表

当遇到匹配的最小括号对时,我们将这对括号从栈中删除(即出栈),如果最后栈为空,那么它是有效的括号,反之不是。

  • 时间复杂度:O(N)。遍历了一遍字符串。
  • 空间复杂度:O(N)。最坏情况下,假如输入是 (((((((,栈的大小将是输入字符串的长度。

(]

class Solution:
    def isValid(self, s: str) -> bool:
        # 使用字典来映射匹配的括号
        bracket_map = {')': '(', ']': '[', '}': '{'}
        # 使用列表作为栈来存储未匹配的左括号
        stack = []

        # 遍历字符串中的每个字符
        for char in s:
            # 如果字符是右括号
            if char in bracket_map:
                # 如果栈不为空且栈顶元素与当前右括号匹配
                if stack and stack[-1] == bracket_map[char]:
                    stack.pop()  # 弹出栈顶元素,表示匹配成功
                else:
                    return False  # 如果不匹配或栈为空,则返回False
            else:
                # 如果字符是左括号,将其推入栈中
                stack.append(char)

        # 如果栈为空,则所有括号都匹配成功,返回True
        return not stack

155. 最小栈 - 力扣(LeetCode)

class MinStack(object):

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        

    def push(self, x):
        """
        :type x: int
        :rtype: void
        """
        if not self.stack:
            self.stack.append((x, x))
        else:
            self.stack.append((x, min(x, self.stack[-1][1])))
        

    def pop(self):
        """
        :rtype: void
        """
        self.stack.pop()
        

    def top(self):
        """
        :rtype: int
        """
        return self.stack[-1][0]
        

    def getMin(self):
        """
        :rtype: int
        """
        return self.stack[-1][1]

作者:负雪明烛

 232. 用栈实现队列 - 力扣(LeetCode)

class MyQueue(object):

    def __init__(self):
        self.stack1 = []
        self.stack2 = []

    def push(self, x):
        self.stack1.append(x)

    def pop(self):
        if not self.stack2:#非空
            while self.stack1:
                self.stack2.append(self.stack1.pop())
        return self.stack2.pop()

    def peek(self):
        if not self.stack2:
            while self.stack1:
                self.stack2.append(self.stack1.pop())
        return self.stack2[-1]

    def empty(self):
        return not self.stack1 and not self.stack2#非空时返回true

作者:负雪明烛

 时间复杂度:push() 时间复杂度是 O(1);peek()/pop() 均摊时间复杂度是 O(1)

间复杂度:空间复杂度是 O(N),因为总的占用了 N 个元素的空间。

394. 字符串解码 - 力扣(LeetCode)

class Solution:
    def decodeString(self, s: str) -> str:
        stack = []  # (str, int) 记录之前的字符串和括号外的上一个数字
        num = 0
        res = ""  # 实时记录当前可以提取出来的字符串
        for c in s:
            if c.isdigit():#检查字符串中的所有字符是否都是数字
                num = num * 10 + int(c)
            elif c == "[":
                stack.append((res, num))
                res, num = "", 0
            elif c == "]":
                top = stack.pop()
                res = top[0] + res * top[1]
            else:
                res += c
        return res

作者:Rogers

 739. 每日温度 - 力扣(LeetCode)

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

单调栈

class Solution:
    def dailyTemperatures(self, T: List[int]) -> List[int]:
        # 可以维护一个存储下标的单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减。
        # 如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标。
        ans = [0] * len(T)
        stack = []
        for i in range(len(T)):
            while stack and T[stack[-1]] < T[i]:    # 栈不为空 && 栈顶温度小于当前温度
                ans[stack[-1]] = i - stack[-1]
                stack.pop()
            stack.append(i)
        return ans

 84. 柱状图中最大的矩形 - 力扣(LeetCode)

暴力枚举 - 左右端点法(TLE)

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        n, ans = len(heights), 0
        if n != 0:
            ans = heights[0]
        for i in range(n):
            height = heights[i]
            for j in range(i, n):
                height = min(height, heights[j])
                ans = max(ans, (j - i + 1) * height)
        return ans

单调栈

from typing import List

class Solution:
    def largestRectangleArea(self, heights: List[int]) -> int:
        # 在高度数组的开头和结尾各添加一个高度为0的哨兵,以便处理边界情况
        n = len(heights)
        heights = [0] + heights + [0]
        
        # 初始化栈和最大面积变量
        stack = []  # 使用 stack 替代 st 以提高可读性
        max_area = 0  # 使用 max_area 替代 ans 以提高可读性
        
        # 遍历整个高度数组(包括哨兵)
        for i in range(n + 2):
            # 当栈不为空且当前高度小于栈顶索引对应的高度时,进行面积计算
            while stack and heights[stack[-1]] > heights[i]:
                # 弹出栈顶元素,该元素是当前矩形的高度
                height = heights[stack.pop()]
                
                # 计算宽度,当前索引 i 减去新的栈顶索引再减 1
                width = i - stack[-1] - 1
                
                # 更新最大面积
                max_area = max(max_area, height * width)
            
            # 将当前索引压入栈中
            stack.append(i)
        
        # 返回计算得到的最大矩形面积
        return max_area

42. 接雨水 - 力扣(LeetCode)

class Solution:
    def trap(self, height: List[int]) -> int:
        ans = 0
        st = []
        for i, h in enumerate(height):
            while st and h >= height[st[-1]]:
                bottom_h = height[st.pop()]
                if not st:  # len(st) == 0
                    break
                left = st[-1]
                dh = min(height[left], h) - bottom_h  # 面积的高
                ans += dh * (i - left - 1)
            st.append(i)
        return ans


http://www.kler.cn/a/526344.html

相关文章:

  • rust学习-rust中的保留字
  • vim操作简要记录
  • 机器人抓取与操作概述(深蓝)——1
  • 生成模型:扩散模型(DDPM, DDIM, 条件生成)
  • C++:PTA L2-003 月饼
  • 深入理解Pytest中的Setup和Teardown
  • Springboot使用AOP时,需不需要引入AspectJ?
  • 在CRM中,怎么进行精细化的客户管理?
  • 在EVE-NG中部署juniper实验环境
  • 【概念版】交叉编译相关介绍
  • 基于Java+Swing实现天气预报系统
  • Hive:窗口函数(1)
  • DeepSeek介绍
  • UE求职Demo开发日志#16 实现物品合成表读取和合成逻辑
  • [LeetCode]day4 977.有序数组的平方
  • 【Python】深入理解Python中的装饰器链:创建组合装饰器的技巧与实践
  • 【Block总结】动态蛇形卷积,专注于细长和弯曲的局部结构|即插即用
  • STM32 PWMI模式测频率占空比
  • (持续更新中~~)3、原来可以这样理解C语言_分⽀和循环上(3)条件操作符
  • 使用Python进行大模型的测试与部署
  • 8642 快速排序
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.18 逻辑运算引擎:数组条件判断的智能法则
  • Java中的注解与反射:深入理解getAnnotation(Class<T> annotationClass)方法
  • 在 Linux 上安装 Microsoft TrueType 字体:ttf-mscorefonts-installer 指南
  • 数据结构:线性表查找的三种方式
  • 向下调整算法(详解)c++