[总结]链表与栈

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

链表

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

[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)
原文地址:https://www.cnblogs.com/hellojamest/p/11695278.html