Leetcode 学习之旅 (一) 栈篇
本人比较菜,只是用这个记录下自己的学习经历,非计算机专业,没有系统的学习过算法以及数据结构。有什么错误的,请指正,勿喷。
代码使用python进行编写,很多也是借鉴了官网和评论的设计思路。这里只是进行记录学习的过程。
题库的学习是按照类型来划分的。比较适合初学者理解。
栈
栈就是一种线性表,简单的理解就是先进后出。先放进去的数据,只能最后拿出来。(这只是一般的运用)
这个概念比较好理解,我们直接来看题。
20.有效的括号
题目
给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 注意空字符串可被认为是有效字符串。
示例 1:
输入: “()” 输出: true
示例 2:
输入: “()[]{}” 输出: true
示例 3:
输入: “(]” 输出: false
示例 4:
输入: “([)]” 输出: false
示例 5:
输入: “{[]}” 输出: true
题目分析
首先,这是一道非常直观,可以使用栈思维来解决的算法问题,主要就是配对,左括号和右括号的配对。
我们先设置一个栈来存储数据,然后开始遍历整个输入。
算法的最基础的思想就是遇到左括号就放入到栈中(等待配对),而遇到右括号的话,开始判断栈顶的元素是否与该输入配对,配对则弹出栈顶元素,然后继续遍历,否则直接返回错误。
这就是最简单的栈思路。
下面放代码
class Solution(object):
def isValid(self, s):
"""
:type s: str
:rtype: bool
"""
stack = []
for s1 in s:
if s1 == '(' or s1 == '{' or s1 == '[':
stack.append(s1)
elif(len(stack)!=0 and ( (s1 == ')' and stack[-1] == '(') or (s1 == ']' and stack[-1] == '[') or ( s1 == '}' and stack[-1] == '{'))):
stack.pop()
else:
return False
return True if len(stack) == 0 else False
整个代码还是很简单的,我们还需要对空栈这个特殊情况进行判定,还是很好理解的。
225.用队列实现栈
题目
使用队列实现栈的下列操作:
push(x) – 元素 x 入栈 pop() – 移除栈顶元素 top() – 获取栈顶元素 empty() – 返回栈是否为空
注意:
你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty
这些操作是合法的。 你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 ,
只要是标准的队列操作即可。 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
题目分析
个人觉得这是一个非常经典的题目。队列和栈的区别就在于,队列是先进先出的规则来存放数据的.
方法一: 一个队列 复杂度:(压入-O(n) , 弹出-O(1))
我们肯定要在初始化函数中定义一个栈,然后开始编写栈的push pop top empty 这四个操作函数.
push() 函数的编写
首先我们举个例子.
栈中的数据 从栈底到栈顶是
1 2 3 4 5
那么出栈的顺序就是
5 4 3 2 1
这个很好理解,但我们是以队列的方式来实现这个栈的,那么正常的pop()顺序是
1 2 3 4 5
这两者是相反的.那么最简单的想法是我们在存储数据的过程中,人为地将队列中数据的顺序调换,那么队列输出的顺序,就是栈的出栈顺序,这就实现了用队列实现栈的操作.
我们假设队列中的数据是 1 2 3 4 5
此时我想将数字6压入栈内,那么我可以将6放入到队列的尾部,但是如果想要进行弹栈的操作,那么按照队列的逻辑,应该是数字1被弹出,而栈的逻辑是最近压入的数字6应该被弹出,所以最新的数字6应该在队列的首部,而不应该是尾部.
那么我们应该将队列中的数据整个反转.
6 5 4 3 2 1
同理,插入 7
6 5 4 3 2 1 7
反转前几个数据
7 6 5 4 3 2 1
实现这个操作的方法就是将原本的数据放入到新数据后面.
那么队列的首部就变成了栈顶.
具体代码
def push(self, x):
"""
Push element x onto stack.
:type x: int
:rtype: None
"""
# 把一个数据放入到队列中
self.q.put(x)
# 反转队列内容, 除最后一个外,把前面的提到后面去
for _ in range(self.q.qsize() - 1):
self.q.put(self.q.get())
pop() 函数的编写
这个直接就是把队列的首部提取出来就行.
def pop(self):
"""
Removes the element on top of the stack and returns that element.
:rtype: int
"""
return self.q.get()
top() 函数的编写
返回栈顶元素,也是队列首部的元素
def top(self):
"""
Get the top element.
:rtype: int
"""
r = self.q.get()
self.q.put(r)
for _ in range(self.q.qsize() - 1):
self.q.put(self.q.get())
return r
这种方法就是把队列的顺序,直接反序.
方法二: 一个队列 复杂度:(压入-O(n) , 弹出-O(1))
这个方法就是使用两个队列,队列1正常地存储数据,队列二则是保存队列一的反向数据.
初始化函数的编写
这里直接定义两个队列,q1存储正常的数据,q2是用来临时存储
def __init__(self):
"""
Initialize your data structure here.
"""
# 定义一个队列
self.q1 = Queue()
self.q2 = Queue()
self.top1 = 0
push()函数的编写
def push(self, x):
"""
Push element x onto stack.
:type x: int
:rtype: None
"""
# 把一个数据放入到队列中
self.q1.put(x)
self.top1 = x
pop()函数的编写
删除最新增加的数据
def pop(self):
while(self.q1.qsize()>1):
self.top1 = self.q1.get()
self.q2.put(self.top1)
# q1只剩下最新的元素
res = self.q1.get() #得到最新的数据
temp = Queue()
temp = self.q1
self.q1 = self.q2
self.q2 = temp
return res
top和 empty 函数的编写就不赘婿了.