zoukankan      html  css  js  c++  java
  • [总结]链表与栈

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

    链表

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

    [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)
    
  • 相关阅读:
    mysql修改数据表名
    HDU 5742 It's All In The Mind (贪心)
    HDU 5752 Sqrt Bo (数论)
    HDU 5753 Permutation Bo (推导 or 打表找规律)
    HDU 5762 Teacher Bo (暴力)
    HDU 5754 Life Winner Bo (博弈)
    CodeForces 455C Civilization (并查集+树的直径)
    CodeForces 455B A Lot of Games (博弈论)
    CodeForces 455A Boredom (DP)
    HDU 4861 Couple doubi (数论 or 打表找规律)
  • 原文地址:https://www.cnblogs.com/hellojamest/p/11695278.html
Copyright © 2011-2022 走看看