• [总结]链表与栈


    链表与栈也是高频出现的面试题,这里将他们放在一篇讨论。

    链表

    链表最关键的在于边界条件的处理,这个只有在不断训练中熟悉与掌握。

    [leetcode]24.Swap Nodes in Pairs

    分别可以用递归和迭代来实现。对于迭代实现,还是需要建立dummy节点。要对头结点进行操作时,考虑创建哑节点dummy,使用dummy->next表示真正的头节点。这样可以避免处理头节点为空的边界问题。

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def swapPairs(self, head: 'ListNode') -> 'ListNode':
            if not head or not head.next:return head
            new = head.next
            head.next = self.swapPairs(new.next)
            new.next = head
            return new
    
    [leetcode]25.Reverse Nodes in k-Group

    递归。首先找到第k+1个节点,也就是这一段需要翻转的链表的尾部相连的那个节点。如果找不到第k+1个节点,说明这一段链表长度不足k,无需进行翻转。在找到第k+1个节点的情况下,首先递归求后面直接相连的链表翻转之后的的头结点。然后再将这个头结点之前的需要翻转的链表节点逐个重新连接,进行翻转。最终,返回翻转之后的链表的头结点。

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def reverseKGroup(self, head: 'ListNode', k: 'int') -> 'ListNode':
            # if not head or not head.next:return head
            node = head
            for i in range(k):
                if not node:return head
                node = node.next
            new = self.reverse(head,node)
            head.next = self.reverseKGroup(node,k)
            return new
    
        def reverse(self,start,end):
            pre = end
            while start!=end:
                nextNode = start.next
                start.next = pre
                pre = start
                start = nextNode
            return pre
    
    [leetcode]138.Copy List with Random Pointer

    此题有两种方法,一种是按照原链表next的顺序依次创建节点,并处理好新链表的next指针,同时把原节点与新节点的对应关系保存到一个hash_map中,然后第二次循环将random指针处理好。这种方法的时间复杂度是O(n),空间复杂度也是O(n)。
    第二种方法则是在原链表的每个节点之后插入一个新的节点,这样原节点与新节点的对应关系就已经明确了,因此不需要用hash_map保存,但是需要第三次循环将整个链表拆分成两个。这种方法的时间复杂度是O(n),空间复杂度是O(1)。
    但是利用hash_map的方法具有其他的优点,如果在循环中加入一个判断,就可以检测出链表中是否有循环;而第二种方法则不行,会陷入死循环。

    """
    # Definition for a Node.
    class Node:
        def __init__(self, val, next, random):
            self.val = val
            self.next = next
            self.random = random
    """
    class Solution:
        def copyRandomList(self, head: 'Node') -> 'Node':
            if not head:return
            self.CloneNodes(head)
            self.ConnectRandomNodes(head)
            return self.ReconnectNodes(head)
    
    
        def CloneNodes(self, pHead):
            pNode = pHead
            while pNode:
                pCloned = Node(pNode.val,pNode.next,None)
                # pCloned.random = None         #不需要写这句话, 因为创建新的结点的时候,random自动指向None
                pNode.next = pCloned
                pNode = pCloned.next
    
        # 将复制后的链表中的复制结点的random指针链接到被复制结点random指针的后一个结点
        def ConnectRandomNodes(self, pHead):
            pNode = pHead
            while pNode:
                pCloned = pNode.next
                if pNode.random != None:
                    pCloned.random = pNode.random.next
                pNode = pCloned.next
    
        # 拆分链表, 将原始链表的结点组成新的链表, 复制结点组成复制后的链表
        def ReconnectNodes(self, pHead):
            pNode = pHead
            pClonedHead = pClonedNode = pNode.next
            pNode.next = pClonedHead.next
            pNode = pNode.next
    
            while pNode:
                pClonedNode.next = pNode.next
                pClonedNode = pClonedNode.next
                pNode.next = pClonedNode.next
                pNode = pNode.next
    
            return pClonedHead
    
    

    栈除了解决计算器,括号优先级的问题,也常用于求左/右边第一个比选定索引大/小的问题,对于第二类问题,最常用的就算单调栈。

    [leetcode]32.Longest Valid Parentheses

    使用stack记录未匹配的括号位置。特别要注意当栈pop后为空的情况,即0-i满足条件时,res的更新。

    class Solution(object):
            def longestValidParentheses(self, s):
                stack = []
                res = 0
                for i in range(len(s)):
                    if s[i] == ')' and stack!=[] and s[stack[-1]] == '(':
                        stack.pop()
                        if stack == []:res = i+1
                        else:res = max(res,i-stack[-1])
                    else:
                         stack.append(i)
                return res
    
    [leetcode]42.Trapping Rain Water

    使用单调栈。我们对低洼的地方感兴趣,就可以使用一个单调递减栈,将递减的边界存进去,一旦发现当前的数字大于栈顶元素了,那么就有可能会有能装水的地方产生。此时我们当前的数字是右边界,我们从栈中至少需要有两个数字,才能形成一个坑槽,先取出的那个最小的数字,就是坑槽的最低点,再次取出的数字就是左边界,我们比较左右边界,取其中较小的值为装水的边界,然后此高度减去水槽最低点的高度,乘以左右边界间的距离就是装水量了。由于需要知道左右边界的位置,所以我们虽然维护的是递减栈,但是栈中数字并不是存递减的高度,而是递减的高度的坐标。

    class Solution:
        def trap(self, height):
          stack = []
          res = 0
          for i in range(len(height)):
              while stack and height[i]>height[stack[-1]]:
                  t = stack.pop()
                  if stack:
                      l = stack[-1]
                      h = min(height[i],height[l])-height[t]
                      w = i-l-1
                      res += w*h
              stack.append(i)
          return res
    
    [leetcode]84.Largest Rectangle in Histogram

    用栈来模拟,遍历heights数组,如果大于栈顶元素,就push进去;否则,持续弹栈来计算从栈顶点到降序点的矩阵大小。然后将这一部分全部替换为降序点的值,即做到了整体依然是有序非降的。

    class Solution(object):
        def largestRectangleArea(self, height):
            """
            :type heights: List[int]
            :rtype: int
            """
            stack = [-1]
            ans = 0
            height.append(0)
            for i in range(len(height)):
                while height[stack[-1]] > height[i]:  #若数组非递增
                    top = stack.pop()
                    w = i - (stack[-1]+1)
                    h = height[top]
                    ans = max(ans, w * h)
                stack.append(i)
            # height.pop()
            return ans
    
    [leetcode]224.Basic Calculator

    对包括+,-,(,)的字符串,模拟计算器操作。
    一般对于计算器的模拟,使用的都是栈的操作。这里主要用四步来做:
    1.若为数字直接加到后面
    2.若为'(',入符号栈
    3.若为运算符,则将优先级大于等于它的运算符先弹出并记录带答案,再将其入栈,本题运算符只有+,-,优先级相同
    4.若为')',弹出运算符直到遇到‘(’

    class Solution:
        def calculate(self, s: str) -> int:
            res, num, sign = 0, 0, 1
            stack = []
            for c in s:
                if c.isdigit():
                    num = 10 * num + int(c)
                elif c == "+" or c == "-":
                    res = res + sign * num
                    num = 0
                    sign = 1 if c == "+" else -1
                elif c == "(":
                    stack.append(res)
                    stack.append(sign)
                    res = 0
                    sign = 1
                elif c == ")":
                    res = res + sign * num
                    num = 0
                    res *= stack.pop()
                    res += stack.pop()
            res = res + sign * num
            return res
    
    [leetcode]227.Basic Calculator II

    对包括+,-,*,/且包含正数的字符串,模拟计算器操作。

    class Solution:
        def calculate(self, s: str) -> int:
            stack = []
            pre_op = '+'
            num = 0
            for i, c in enumerate(s):
                if c.isdigit():
                    num = 10 * num + int(c)
                if i == len(s) - 1 or c in '+-*/':
                    if pre_op == '+':
                        stack.append(num)
                    elif pre_op == '-':
                        stack.append(-num)
                    elif pre_op == '*':
                        stack.append(stack.pop() * num)
                    elif pre_op == '/':
                        top = stack.pop()
                        stack.append(int(top / num))
                    pre_op = c
                    num = 0
            return sum(stack)
    
  • 相关阅读:
    计算机编码规则之:Base64编码
    java高级用法之:在JNA中将本地方法映射到JAVA代码中
    java高级用法之:JNA类型映射应该注意的问题
    netty系列之:netty中的核心MessageToMessage编码器
    netty系列之:netty中的核心MessageToByte编码器
    ClickHouse遇到的一些配置导致的报错问题记录
    ClickHouse如何引用多个配置文件
    DM达梦数据库License的替换安装
    【高并发】不得不说的线程池与ThreadPoolExecutor类浅析
    【高并发】深度解析线程池中那些重要的顶层接口和抽象类
  • 原文地址:https://www.cnblogs.com/hellojamest/p/11695278.html
Copyright © 2020-2023  润新知