【leetcode】探索栈

3.栈(LIFO)

3.1 设计一个最小栈

设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。

  • push(x) -- 将元素 x 推入栈中。
  • pop() -- 删除栈顶的元素。
  • top() -- 获取栈顶元素。
  • getMin() -- 检索栈中的最小元素。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.
class MinStack:

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

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

    def pop(self):
        """
        :rtype: void
        """
        del self.minstack[-1]
        del self.stack[-1]

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

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

一开始我的代码设计的getmin里面是直接min(list)的,执行需要900+ms;如上面这样设计的话,只需72ms,在push、pop过程中使min也在更新,不过就是牺牲了空间来换时间。

3.2 有效的括号

给定一个只包括 '('')''{''}''['']' 的字符串,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。

注意空字符串可被认为是有效字符串。

示例 1:

输入: "()"
输出: true

示例 2:

输入: "()[]{}"
输出: true

示例 3:

输入: "(]"
输出: false

示例 4:

输入: "([)]"
输出: false

示例 5:

输入: "{[]}"
输出: true

我的解答,只用了40ms哈哈哈哈:

class Solution:
    def isValid(self, s):
        """
        :type s: str
        :rtype: bool
        """
        stack=[]
        if len(s)==1:
            return False
        for i in s:
            if i=="(":
                #term_supposed=")"
                stack.append(")")
            elif i=="{":
                #term_supposed="}"
                stack.append("}")
            elif i=="[":
                stack.append("]")           
            else:
                if len(stack)==0:
                    return False
                if stack.pop(-1)!=i:
                    return False
        return stack==[]

3.3 每日温度

根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高的天数。如果之后都不会升高,请输入 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的都是 [30, 100] 范围内的整数。

实在想不出怎么实现,对栈的掌握不足,来看看大神的简洁答案:

class Solution:
    def dailyTemperatures(self, T):
        """
        :type T: List[int]
        :rtype: List[int]
        """
        """
        l=[]
        stack=[]
        for i in range(len(T)):
            l.append(0)
            if stack and T[i]>stack[-1][0]:
                self.comparison(stack,T[i],i,l)
            stack.append((T[i],i))
        return l
    def comparison(self,stack,nub,index,l):
        if stack and nub>stack[-1][0]:
            tup=stack.pop()
            l[tup[1]]=index-tup[1]
            self.comparison(stack,nub,index,l)

我觉得就是我没有考虑到index,即每个元素的索引,这个也可以巧妙地利用进来!这段算法的思想就是“栈顶元素遇到大的T[i],执行出栈操作,直至栈顶元素不小于T[i]或stack里为空”

3.4逆波兰表达式求值

根据逆波兰表示法,求表达式的值。

有效的运算符包括 +-*/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:

  • 整数除法只保留整数部分。
  • 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1:

输入: ["2", "1", "+", "3", "*"]
输出: 9
解释: ((2 + 1) * 3) = 9

示例 2:

输入: ["4", "13", "5", "/", "+"]
输出: 6
解释: (4 + (13 / 5)) = 6

示例 3:

输入: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]
输出: 22
解释: 
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
class Solution:
    def evalRPN(self, tokens):
        """
        :type tokens: List[str]
        :rtype: int
        """
        stack=[]
        signal=set(('+','-','*','/'))
        for i in range(len(tokens)):
            if stack and tokens[i] in signal:
                last=stack.pop()
                first=stack.pop()
                temp=str(int(eval(first+tokens[i]+last)))
                stack.append(temp)
                continue
            stack.append(tokens[i])
        return int(stack[0])

模板I

/*
 * Return true if there is a path from cur to target.
 */
boolean DFS(Node cur, Node target, Set visited) {
    return true if cur is target;
    for (next : each neighbor of cur) {
        if (next is not in visited) {
            add next to visted;
            return true if DFS(next, target, visited) == true;
        }
    }
    return false;
}

3.5岛屿的个数

原来就是前面一模一样的问题。

3.6克隆图

克隆一张无向图,图中的每个节点包含一个 label (标签)和一个 neighbors (邻接点)列表 。

OJ的无向图序列化:

节点被唯一标记。

我们用 # 作为每个节点的分隔符,用 , 作为节点标签和邻接点的分隔符。

例如,序列化无向图 {0,1,2#1,2#2,2}

该图总共有三个节点, 被两个分隔符  # 分为三部分。 

  1. 第一个节点的标签为 0,存在从节点 0 到节点 1 和节点 2 的两条边。
  2. 第二个节点的标签为 1,存在从节点 1 到节点 2 的一条边。
  3. 第三个节点的标签为 2,存在从节点 2 到节点 2 (本身) 的一条边,从而形成自环。

我们将图形可视化如下:

       1
      / \
     /   \
    0 --- 2
         / \
         \_/

说实话,我一开始看不懂题目想要我干什么,看了其他人代码之后才知道原来这么简单......就是简单地遍历图,遍历的过程中将节点及节点的邻居都定义好,最后返回一个头结点,但是这个头结点的邻居和邻居的邻居都定义好了。

# Definition for a undirected graph node
# class UndirectedGraphNode:
#     def __init__(self, x):
#         self.label = x
#         self.neighbors = []

class Solution:
    # @param node, a undirected graph node
    # @return a undirected graph node
    def __init__(self):
        self.d={}
        
    def cloneGraph(self, node):
        if not node:
            return node
        head=UndirectedGraphNode(node.label)
        self.d[node]=head
        for neighbor in node.neighbors:
            if neighbor in self.d:
                head.neighbors.append(self.d[neighbor])
            else:
                temp=self.cloneGraph(neighbor)
                head.neighbors.append(temp)
        return head

可以大概的观察出,如果只是单纯的遍历图,则运用“栈”+DFS是比较快的,如果是想要找到最短路径,则“队列”+BFS是更好的选择。

3.7目标和

给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例 1:

输入: nums: [1, 1, 1, 1, 1], S: 3
输出: 5
解释: 

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

一共有5种方法让最终目标和为3。

这是我的方法,根据所学的DFS栈,因为时间运行限制,根本无法通过:

class Solution(object):
    def __init__(self):
        self.t=0
    def findTargetSumWays(self, nums, S):
        """
        :type nums: List[int]
        :type S: int
        :rtype: int
        """
        length=len(nums)
        def rec(_sum,i):
            if i==length:
                if _sum==S:
                    self.t=self.t+1
            else:
                rec(_sum+nums[i],i+1)
                rec(_sum-nums[i],i+1)
        rec(0,0)
        return self.t

所以这个教程对应的问题好像有点奇怪,也可能是让我们明白很多问题单纯地去遍历是很浪费时间的。大神用的是动态规划的思想做的,我还不会:

class Solution:
    def findTargetSumWays(self, nums, S):
        """
        :type nums: List[int]
        :type S: int 
        :rtype: int
        """
        # P -> sum(正项) N -> sum(负向)
        # 有 P + N = sum(nums) 
        #    P - N = S
        #  => P = (S + sum(nums)) / 2
        #     sum(nums) >= P >= 0 and P 整数
        numsSum = sum(nums)
        if (S + numsSum) % 2 != 0:
            # P无解
            return 0
        sumP = (S + numsSum) // 2
        if sumP > numsSum:
            return 0
        return self.findTargetSumPWays(nums, sumP)
        
    def findTargetSumPWays(self, nums, sumP):
        # 动态规划,只考虑正项和, 有sumP + 1种可能(含0)
        # dp[i] 表示和为i的可能数
        dp = [0] * (sumP + 1)
        dp[0] = 1
        for i in nums:
            for j in range(sumP, i - 1, -1):
                dp[j] += dp[j - i]
        return dp[sumP]

 

你可能感兴趣的:(leetcode,栈)