1 栈的含义

栈是一种类似摞起来的盘子的数据结构,性质是后进者先出(Last In First Out, LIFO),也就是你只能拿走最上面的盘子,一个一个,然后拿走下面的盘子。

栈是一种方法或者说是思想,它可以用数组或者链表等等数据结构实现。

2 数组实现

基于python用数组实现一个Stack类,包含如下方法:

  • push - 向栈顶增加一个元素
  • pop - 删除栈顶元素并返回该值
  • size - 返回栈的长度
  • top - 返回栈顶元素的值
  • is_empty - 判断栈是否为空,是则返回True,否则False

1. 创建并初始化Stack类

  • Define a class named Stack and add the __init__ method
  • Initialize the arr attribute with an array containing 10 elements, like this: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  • Initialize the next_index attribute
  • Initialize the num_elements attribute
1
2
3
4
5
class Stack(object):
def __init__(self, size=10):
self.arr = [0 for i in range(size)]
self.next_index = 0
self.num_elements = 0

测试用例:

1
2
3
foo = Stack()
print(foo.arr)
print("Pass" if foo.arr == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] else "Fail")
2. 增加 push 方法

  • The method will need to have a parameter for the value that you want to push
  • Remember that next_index will have the index for where the value should be added
  • Once you've added the value, you'll want to increment both next_index and num_elements
1
2
3
4
5
6
7
8
9
10
class Stack(object):
def __init__(self, size=10):
self.arr = [0 for i in range(size)]
self.next_index = 0
self.num_elements = 0

def push(self, value):
self.arr[self.next_index] = value
self.next_index += 1
self.num_elements += 1

测试用例:

1
2
3
4
foo = Stack()
foo.push("Test!")
print(foo.arr)
print("Pass" if foo.arr[0] == "Test!" else "Fail")
3. 处理栈的数组容量满的问题

虽然python中的list是动态数组实现的,可以随意增加不用增加扩容处理,但是这里只是用list来代表一般的数组即定义时数组长度就固定了。 当栈的数组满的时候如果再增加元素就是Index out of range,所以需要进行扩容。

define the _handle_stack_capacity_full method:

  • Define an old_arr variable and assign it the current (full) array
  • Create a new (larger) array and assign it to arr.
  • Iterate over the values in the old array and copy them to the new array.

Then, in the push method:

  • Add a conditional to check if the array is full; if it is, call the _handle_stack_capacity_full

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class Stack:

    def __init__(self, initial_size = 10):
    self.arr = [0 for _ in range(initial_size)]
    self.next_index = 0
    self.num_elements = 0

    def push(self, data):
    if self.next_index == len(self.arr):
    print("Out of space! Increasing array capacity ...")
    self._handle_stack_capacity_full()

    self.arr[self.next_index] = data
    self.next_index += 1
    self.num_elements += 1

    def _handle_stack_capacity_full(self):
    odd_arr = self.arr
    self.arr = [0 for i in range(2*self.num_elements)]
    for i in range(self.num_elements):
    self.arr[i] = odd_arr[i]
    self.next_index = self.num_elements
    self.num_elements += 1
    测试用例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    foo = Stack()
    foo.push(1)
    foo.push(2)
    foo.push(3)
    foo.push(4)
    foo.push(5)
    foo.push(6)
    foo.push(7)
    foo.push(8)
    foo.push(9)
    foo.push(10) # The array is now at capacity!
    foo.push(11) # This one should cause the array to increase in size
    print(foo.arr) # Let's see what the array looks like now!
    print("Pass" if len(foo.arr) == 20 else "Fail") # If we successfully doubled the array size, it should now be 20.
    4. 增加sizeis_empty方法

  • Add a size method that returns the current size of the stack

  • Add an is_empty method that returns True if the stack is empty and False otherwise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Stack:

def __init__(self, initial_size = 10):
self.arr = [0 for _ in range(initial_size)]
self.next_index = 0
self.num_elements = 0

def push(self, data):
if self.next_index == len(self.arr):
print("Out of space! Increasing array capacity ...")
self._handle_stack_capacity_full()

self.arr[self.next_index] = data
self.next_index += 1
self.num_elements += 1

# TODO: Add the size method
def size(self):
return self.num_elements

# TODO: Add the is_empty method
def is_empty(self):
return True if self.num_elements == 0 else False


def _handle_stack_capacity_full(self):
old_arr = self.arr

self.arr = [0 for _ in range( 2* len(old_arr))]
for index, value in enumerate(old_arr):
self.arr[index] = value

测试用例:

1
2
3
4
5
6
foo = Stack()
print(foo.size()) # Should return 0
print(foo.is_empty()) # Should return True
foo.push("Test") # Let's push an item onto the stack and check again
print(foo.size()) # Should return 1
print(foo.is_empty()) # Should return False
5. 增加pop方法

  • Check if the stack is empty and, if it is, return None
  • Decrement next_index and num_elements
  • Return the item that is being "popped"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Stack:

def __init__(self, initial_size = 10):
self.arr = [0 for _ in range(initial_size)]
self.next_index = 0
self.num_elements = 0

def push(self, data):
if self.next_index == len(self.arr):
print("Out of space! Increasing array capacity ...")
self._handle_stack_capacity_full()

self.arr[self.next_index] = data
self.next_index += 1
self.num_elements += 1

# TODO: Add the pop method
def pop(self):
if self.num_elements == 0:
return None
self.num_elements -= 1
self.next_index -= 1
value = self.arr[self.next_index]
self.arr[self.next_index] = 0
return value

def size(self):
return self.num_elements

def is_empty(self):
return self.num_elements == 0

def _handle_stack_capacity_full(self):
old_arr = self.arr

self.arr = [0 for _ in range( 2* len(old_arr))]
for index, value in enumerate(old_arr):
self.arr[index] = value

测试用例:

1
2
3
4
foo = Stack()
foo.push("Test") # We first have to push an item so that we'll have something to pop
print(foo.pop()) # Should return the popped item, which is "Test"
print(foo.pop()) # Should return None, since there's nothing left in the stack
————

3 栈的应用

合法的出栈序列

问题描述:已知从1到n的数字序列,按顺序入栈,每个数字入栈后即可出栈,也可以在栈中停留,等待后面的数字入栈出栈后,该数字再出栈。求该数字序列的出栈序列是否合法。

思路:将1-n的数字序列一个一个入栈,每入一个就与“出栈序列”的队头进行比较,相同则出栈,不同则继续入栈。如果最后栈里面的元素无法弹空就说明不合法,否则合法。

括号匹配

在现实生活中经常用到栈来解决问题,例如数学公式中括号是否匹配的问题: \(((3^2 + 8)\*(5/2))/(2+6).\)

实现equation_checker函数,输入为字符串,如果括号都是匹配的返回True,否则返回False

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Stack:
def __init__(self):
self.items = []

def size(self):
return len(self.items)

def push(self, item):
self.items.append(item)

def pop(self):
if self.size()==0:
return None
else:
return self.items.pop()

def equation_checker(equation):
stack = Stack()
for i in range(len(equation)):
if equation[i] == '(':
stack.push('(')
elif equation[i] == ')':
if stack.size() == 0:
return False
else:
stack.pop()
if stack.size() != 0:
return False
else:
return True

测试用例:

1
2
print ("Pass" if (equation_checker('((3^2 + 8)*(5/2))/(2+6)')) else "Fail")
print ("Pass" if not (equation_checker('((3^2 + 8)*(5/2))/(2+6))')) else "Fail")
————

最小括号反转

给定仅仅包含{}的字符串,给出为了让括号匹配的最少的反转次数。

  • For input_string = "}}}}, the number of reversals required is 2.

  • For input_string = "}{}}, the number of reversals required is 1.

If the brackets cannot be balanced, return -1 to indicate that it is not possible to balance them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def minimum_bracket_reversals(input_string):
stack = []
counter = 0
for i in input_string:
if i == '{':
stack.append('{')
if i == '}':
if len(stack) > 0:
stack.pop()
else:
stack.append('{')
counter += 1
if len(stack) > 0:
if len(stack) % 2 == 0:
counter += len(stack)//2
else:
return -1
return counter

测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def test_function(test_case):
input_string = test_case[0]
expected_output = test_case[1]
output = minimum_bracket_reversals(input_string)

if output == expected_output:
print("Pass")
else:
print("Fail")

test_case_1 = ["}}}}", 2]
test_function(test_case_1)

test_case_2 = ["}}{{", 2]
test_function(test_case_2)

test_case_3 = ["{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}}}}}", 13]
test_function(test_case_3)

test_case_4= ["}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{", 2]
test_function(test_case_4)

test_case_5 = ["}}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", 1]
test_function(test_case_5)
————

后缀表达式

Reverse Polish Notation(逆波兰式)也叫后缀表达式,在后缀表达式中运算符( +, -, *, and /)出现在运算数的后面,即:3 1 + 4 *,也就是(3 + 1) * 4 (中缀表达式)。后缀表达式的优点就是对于计算机而言可以直接利用内存的栈式结构执行后进先出的顺序(遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。)。

实现evaluate_post_fix函数,输入为后缀表达式(python的list),求出表达式的值并返回。

python中的/进行浮点数的除法,结果为浮点数,利用int()将结果转为整数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
## 利用python的list模拟栈
def evaluate_post_fix(input_list):
stack = []
for i in input_list:
if i == '+':
tmp = stack[len(stack)-2] + stack[len(stack)-1]
stack.pop()
stack.pop()
stack.append(tmp)
elif i == '-':
tmp = stack[len(stack)-2] - stack[len(stack)-1]
stack.pop()
stack.pop()
stack.append(tmp)
elif i == '*':
tmp = stack[len(stack)-2] * stack[len(stack)-1]
stack.pop()
stack.pop()
stack.append(tmp)
elif i == '/':
tmp = stack[len(stack)-2] / stack[len(stack)-1]
stack.pop()
stack.pop()
stack.append(int(tmp))
else:
stack.append(int(i))
return stack.pop()

测试用例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def test_function(test_case):
output = evaluate_post_fix(test_case[0])
print(output)
if output == test_case[1]:
print("Pass")
else:
print("Fail")

test_case_1 = [["3", "1", "+", "4", "*"], 16]
test_function(test_case_1)

test_case_2 = [["4", "13", "5", "/", "+"], 6]
test_function(test_case_2)

test_case_3 = [["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"], 22]
test_function(test_case_3)
————

4 LeetCode栈相关题目

LeetCode 225.用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通队列的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

注意:

你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。

你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:

输入:

["MyStack", "push", "push", "top", "pop", "empty"] [[], [1], [2], [], [], []]

输出:

[null, null, null, 2, 2, false]

解释:

1
2
3
4
5
6
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False
提示:

  • 1 <= x <= 9
  • 最多调用100 次 push、pop、top 和 empty
  • 每次调用 pop 和 top 都保证栈不为空

我的解法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#
# @lc app=leetcode.cn id=225 lang=python3
#
# [225] 用队列实现栈
#

# @lc code=start
class MyStack:
"""
整体思路:采用两个队列queue1、queue2,将有元素进入时将其放入queue1队列;元素出栈时,首先从queue1中开始,将queue1中的除了最后一个元素,
全部入队到queue2中,最后一个元素返回;如果queue1空,就将queue2的最后一个元素弹出,方法就是将queue2前面的元素入队到queue1。
"""

def __init__(self):
"""
Initialize your data structure here.
"""
self.queue1 = list()
self.queue2 = list()

def push(self, x: int) -> None:
"""
Push element x onto stack.
"""
self.queue1.append(x)

def pop(self) -> int:
"""
Removes the element on top of the stack and returns that element.
"""
if len(self.queue1) > 0:
i = 0
length = len(self.queue1)
while i < length:
if i != length - 1:
self.queue2.append(self.queue1.pop(0))
else:
return self.queue1.pop(0)
i += 1
else: ## len(self.queue2) > 0
i = 0
length = len(self.queue2)
while i < length:
if i != length - 1:
self.queue1.append(self.queue2.pop(0))
else:
return self.queue2.pop(0)
i += 1

def top(self) -> int:
"""
Get the top element.
"""
if len(self.queue1) > 0:
i = 0
length = len(self.queue1)
while i < length:
val = self.queue1.pop(0)
self.queue2.append(val)
if i == length - 1:
return val
i += 1
else: ## len(self.queue2) > 0
i = 0
length = len(self.queue2)
while i < length:
val = self.queue2.pop(0)
self.queue1.append(val)
if i == length - 1:
return val
i += 1

def empty(self) -> bool:
"""
Returns whether the stack is empty.
"""
if len(self.queue1) > 0 or len(self.queue2) > 0:
return False
else:
return True

——————

LeetCode 232.用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

说明:

  • 你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

我的解法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#
# @lc app=leetcode.cn id=232 lang=python3
#
# [232] 用栈实现队列
#

# @lc code=start
class MyQueue:

def __init__(self):
"""
Initialize your data structure here.
"""
self.stack_in = list()
self.stack_out = list()

def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
self.stack_in.append(x)

def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if len(self.stack_out) == 0:
length = len(self.stack_in)
while length:
self.stack_out.append(self.stack_in.pop())
length -= 1
val = self.stack_out.pop()
return val


def peek(self) -> int:
"""
Get the front element.
"""
if len(self.stack_out) == 0:
length = len(self.stack_in)
while length:
self.stack_out.append(self.stack_in.pop())
length -= 1
val = self.stack_out[-1]
return val


def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
if len(self.stack_in) == 0 and len(self.stack_out) == 0:
return True
return False

—————

LeetCode 155. 最小栈

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

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

示例:

输入:["MinStack","push","push","push","getMin","pop","top","getMin"] [[],[-2],[0],[-3],[],[],[],[]]

输出:[null,null,null,null,-3,null,0,-2]

解释:

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

提示:

pop、top 和 getMin 操作总是在 非空栈 上调用。

我的解法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#
# @lc app=leetcode.cn id=155 lang=python3
#
# [155] 最小栈
#

# @lc code=start
class MinStack:

def __init__(self):
"""
initialize your data structure here.
"""
self.stack = list()
self.min_val = list()

def push(self, val: int) -> None:
self.stack.append(val)
if len(self.min_val) == 0:
self.min_val.append(val)
else:
if val < self.min_val[-1]:
self.min_val.append(val)
else:
self.min_val.append(self.min_val[-1])

def pop(self) -> None:
val = self.stack.pop()
self.min_val.pop()
return val

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

def getMin(self) -> int:
return self.min_val[-1]

LeetCode 224.基本计算器

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

示例 1:

输入:s = "1 + 1"

输出:2

示例 2:

输入:s = " 2-1 + 2 "

输出:3

示例 3:

输入:s = "(1+(4+5+2)-3)+(6+8)"

输出:23

提示:

  • 1 <= s.length <= 3 * 105
  • s 由数字、'+'、'-'、'('、')'、和 ' ' 组成
  • s 表示一个有效的表达式

思路:

  • 1 准备工作:

    两个栈:数字栈和操作符栈。

    栈中的操作:数字栈弹出两个数字,操作符栈出栈一个操作符,然后进行计算。

  • 2 细节处理

    左右括号:左括号出现时不能进行计算操作,直到右括号出现再进行计算。——>方法就是:可以使用一个变量compute_flag记录是否可以计算。

      - 当遇到"+/-"时就可以计算,compute_flag = 1;
      - 当遇到'('时就不可以计算,compute_flag = 0;

    字符串处理:采用状态机的思路,具体处理可见代码。

解法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#
# @lc app=leetcode.cn id=224 lang=python3
#
# [224] 基本计算器
#

# @lc code=start
class Solution:
def compute(self, number_stack, operation_stack):
if len(number_stack) < 2 and len(operation_stack) == 0: ## 单个数字又不是负数的情况
return
num1 = number_stack.pop()
if len(number_stack) == 0:
num2 = 0
else:
num2 = number_stack.pop()
if len(operation_stack) == 0:
return
op = operation_stack[-1]
if op == '+':
num = int(num2)+int(num1)
else:
num = int(num2)-int(num1)
number_stack.append(num)
operation_stack.pop()

def calculate(self, s: str) -> int:
## 状态机变量
BEGIN_STATE = 0
NUMBER_STATE = 1
OPERATION_STATE = 2
##数字栈和操作符栈
number_stack = list()
operation_stack = list()

number = 0 ## 记录当前的数字
STATE = BEGIN_STATE ## 当前的状态变量
compute_flag = 0 ## 记录是否计算。0表示不可以计算,1表示可以计算。

i = 0 ## 字符串索引
while i < len(s):

if s[i] == ' ': ## 当前字符为空格,直接跳过
i += 1
continue

if STATE == BEGIN_STATE: ## 开始状态
if s[i] >= '0' and s[i] <='9':
STATE = NUMBER_STATE
else:
STATE = OPERATION_STATE
continue
if STATE == NUMBER_STATE: ## 数字状态
if s[i] >= '0' and s[i] <= '9':
number = number*10 + (ord(s[i]) - ord('0')) ## 得到应该要参加的数字
i += 1
else:
number_stack.append(number)
if compute_flag == 1:
self.compute(number_stack, operation_stack)
number = 0
STATE = OPERATION_STATE
continue
if STATE == OPERATION_STATE: ## 操作符状态
if s[i] == '+' or s[i] == '-':
operation_stack.append(s[i])
compute_flag = 1 ## 可计算状态
i += 1
elif s[i] == '(':
STATE = NUMBER_STATE
compute_flag = 0 ## 不可计算状态
i += 1
elif s[i] == ')':
self.compute(number_stack, operation_stack)
i += 1
elif s[i] >= '0' and s[i] <='9':
STATE = NUMBER_STATE
continue

if number != 0: ## 最后一个数字没有进行循环计算
number_stack.append(number)
self.compute(number_stack, operation_stack)
if number == 0 and len(number_stack) == 0:
return 0
return number_stack.pop()