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:
输入: "()"
输出: 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<Node> 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}
。
该图总共有三个节点, 被两个分隔符 #
分为三部分。
- 第一个节点的标签为
0
,存在从节点0
到节点1
和节点2
的两条边。 - 第二个节点的标签为
1
,存在从节点1
到节点2
的一条边。 - 第三个节点的标签为
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]