- 正在学习的算法课程:极客时间的王争老师的《数据结构与算法之美》
- 传送门: https://time.geekbang.org/column/126
- 代码传送门:https://github.com/Lebhoryi/Algorithms/tree/master/3.%E6%A0%88%E5%92%8C%E9%98%9F%E5%88%97
- 目前学到第八讲,很良心,共56讲
- 2019/10/07 - 2019/10/17
- 栈一般用顺序表实现,队列一般用链表实现
一、栈
1. 定义
栈(stack)是限定仅在表尾进行插入和删除操作的线性表。
- 何时选择栈?
当某个数据集合只涉及在一段插入和删除数据,并且满足后进先出、先进后出的特性,首选栈
2. 栈实现
栈的主要操作有两个:
-
入栈:栈顶插入一个数据
-
出栈:栈顶删除一个数据
根据实现方式不同,分为:
-
顺序栈:用数组实现的栈
-
链式栈:用链表实现的栈
时间复杂度和空间复杂度均是O(1)
3. 栈的应用
最常见的是函数调用栈
int main() {
int a = 1;
int ret = 0;
int res = 0;
ret = add(3, 5);
res = a + ret;
printf("%d", res);
reuturn 0;
}
int add(int x, int y) {
int sum = 0;
sum = x + y;
return sum;
}
4. 递归
- 递归的秘诀:
将大问题分解为小问题,写出递推公式和终止条件。
- 警惕:
-
不要用人脑去分解递归的每个调用关系,即每一个步骤
-
不要栈溢出
-
不要重复计算
5. 代码实现栈的操作123
-
创建空栈
-
push(入栈)
-
pop(出栈)
-
top(取出栈最后压入的元素)
- 顺序栈,python用表实现,表尾为栈顶:
# coding=utf-8
'''
@ Summary: python中的list可以满足栈的操作,但是多了栈所没有的操作
缺乏安全性,因此把list作为类的内部,作为实现基础
@ Update:
@ file: 3-1.栈的顺序表实现.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-7 下午6:17
'''
class SStack(object):
def __init__(self):
self._elem = []
def is_empty(self):
# 是否空栈
return self._elem == []
def top(self):
# 取栈顶元素
if self.is_empty():
return None
return self._elem[-1]
def push(self, elem):
# 入栈
return self._elem.append(elem)
def pop(self):
# 出栈
if self.is_empty():
return None
return self._elem.pop()
if __name__ == "__main__":
st1 = SStack()
st1.push(3)
# print(st1.top())
st1.push(5)
while not st1.is_empty():
print(st1.pop())
- 链接表实现栈,表头为栈顶:
# coding=utf-8
'''
@ Summary: 用链表实现栈的相关操作
@ Update:
@ file: 3-2.栈的链接表实现.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-7 下午6:29
'''
class Node(object):
def __init__(self, val):
self.val = val
self.next = None
def __repr__(self):
return str(self.val)
class LStack(object):
@ staticmethod
def c_link(vals):
# create link
if not vals:
return None
head = Node(len(vals)) # head node
move = head # move node
for val in vals:
tmp = Node(val)
move.next = tmp
move = tmp
return head.next
@ staticmethod
def p_link(link):
# print link
if not link:
return None
while link:
yield link.val
link = link.next
@ staticmethod
def top(link):
# 取出栈顶元素
if not link:
return None
return link.val
@ staticmethod
def push(link, elem):
# 入栈
elem = Node(elem)
elem.next = link
return elem
@ staticmethod
def pop(link):
# 出栈
if not link:
return None
p = link
link = link.next
return p.val, link
if __name__ == "__main__":
_list = list(range(1, 7))
# link = None
# for i in _list:
# link = LStack.push(link, i)
# for node in LStack.p_link(link):
# print(node, end=" ")
top = LStack.c_link(_list)
for node in LStack.p_link(top):
print(node, end=" ")
print()
# output: 1 2 3 4 5 6
top = LStack.push(top, 10)
for node in LStack.p_link(top):
print(node, end=" ")
print()
# output: 10 1 2 3 4 5 6
elem, top = LStack.pop(top)
print(elem)
for node in LStack.p_link(top):
print(node, end=" ")
print()
elem = LStack.top(top)
print(elem)
- 代码练习1:二进制转十进制
# coding=utf-8
'''
@ Summary: 二进制转十进制,用栈实现
@ Update:
@ file: 3-3.二进制转十进制.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-7 下午7:16
'''
class SStack(object):
def __init__(self):
self._elem = []
def is_empty(self):
# 是否空栈
return self._elem == []
def push(self, elem):
# 入栈
return self._elem.append(elem)
def pop(self):
# 出栈
if self.is_empty():
return None
return self._elem.pop()
if __name__ == "__main__":
_input = 1010110
_list = list(map(int, str(_input)))
st1 = SStack()
for i in _list:
st1.push(i)
print(st1._elem)
sum = count = 0
while not st1.is_empty():
# print(st1.pop(), end=" ")
_pop = st1.pop()
sum = sum + _pop * 2**count
count += 1
print(sum)
- 代码练习2:二进制转八进制
# coding=utf-8
'''
@ Summary: 用栈实现二进制转八进制
@ Update:
@ file: 3-3.二进制转八进制.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-7 下午10:05
'''
class SStack(object):
def __init__(self):
self._elem = []
def is_empty(self):
# 是否空栈
return self._elem == []
def push(self, elem):
# 入栈
return self._elem.append(elem)
def pop(self):
# 出栈
if self.is_empty():
return None
return self._elem.pop()
if __name__ == "__main__":
_input = 11001001 # 初始值
_list = list(map(int, str(_input))) # int to list
while len(_list) % 3 != 0: # 三的倍数,不够补0
_list.insert(0, 0)
_list.insert(0, 0) # 为了后面的while 循环到栈底的时候继续运行,增加一个0
st1 = SStack() # 二进制的栈
st2 = SStack() # 八进制的栈
for i in _list: # 二进制digui入栈
st1.push(i)
print(st1._elem)
# 二进制 三个弹出,计算,压入八进制栈
tmp = [] # 储存三个二进制数
count = 1 # 计数
while not st1.is_empty():
if count < 4:
# 找到三个二进制数
tmp.append(str(st1.pop()))
count += 1
else:
num = "".join(tmp)
# 二进制转八进制
num = int(num[2]) * 2**2 + int(num[1]) * 2**1 + \
int(num[0]) * 2**0
st2.push(num) # 入栈
count = 1
tmp = []
print(st2._elem)
- 代码练习3:逆波兰表达式
# coding=utf-8
'''
@ Summary: 逆波兰表达式,又称后缀表达式
@ Update:
@ file: 3-4.逆波兰表达式.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-8 下午3:29
'''
class Node(object):
def __init__(self, val):
self.val = val
self.next = None
def __repr__(self):
return str(self.val)
class LStack(object):
def __init__(self):
self.top = None
def push(self, elem):
# 入栈
elem = Node(elem)
elem.next = self.top
self.top = elem
def pop(self):
# 出栈
if not self.top:
return None
p = self.top
self.top = self.top.next
return p.val
def p_link(self):
if not self.top:
return None
while self.top:
print(self.top.val, end=" ")
self.top = self.top.next
def RPN(_list):
# _int = [str(i) for i in range(20)]
operators = "+-*/"
st1 = LStack()
for item in _list:
try:
item = int(item)
st1.push(item)
except ValueError:
if item in operators:
x2 = st1.pop()
x1 = st1.pop()
if item == "+":
st1.push(x1 + x2)
elif item == "-":
st1.push(x1 - x2)
elif item == "*":
st1.push(x1 * x2)
elif item == "/":
st1.push(x1 / x2)
return st1.top
if __name__ == "__main__":
# _input = "1 2 - 4 5 + *" # -9
# _input = "5 6 7 + 8 * - 9 4 / +" # -96.75
_input = "3 5 - 6 17 4 * + * 3 /"
_list = _input.split(" ") # str to list
print(RPN(_list))
---
- 代码练习4: 中缀表达式转后缀表达式
```python
# coding=utf-8
'''
@ Summary: 中缀表达式转换为后缀表达式
@ Update:
@ file: 3-6.逆波兰进阶.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-8 下午4:43
'''
class Node(object):
def __init__(self, val):
self.val = val
self.next = None
def __repr__(self):
return str(self.val)
class LStack(object):
def __init__(self):
# 初始化 top node
self._top = None
def push(self, elem):
# 入栈 self._top 始终指向first node
elem = Node(elem)
elem.next = self._top
self._top = elem
def pop(self):
# 出栈 self._pop指向第二个节点, 返回第一个节点的值
if not self._top:
return None
p = self._top
self._top = self._top.next
return p.val
def mid_2_after(l):
# 中缀转后缀
# 优先级
ops_relu = {"+":1, "-":1,
"*":2, "/":2,
"(":1, ")":1}
stack = LStack() # 储存运算符和"()"的栈
lt = [] # 输出列表
for item in l:
# 遇到数字添加进列表中,非数字进行栈的相关操作
try:
item = int(item)
lt.append(item)
except ValueError:
if item != ")":
# 优先级顺序,栈顶的优先级高则弹出栈中所有元素
if stack._top and ops_relu[item] < ops_relu[stack._top.val]:
while stack._top:
lt.append(stack.pop())
stack.push(item) # 入栈
else:
# 遇到")", 弹出栈中的元素, 直到"("为止
while stack._top.val != "(":
lt.append(stack.pop())
stack.pop() # delete "("
# 弹出栈中所有元素
while stack._top:
lt.append(stack.pop())
return lt
if __name__ == "__main__":
_input = "1 + ( 2 - 3 ) * 4 + 10 / 5" # "1 2 3 - 4 * + 10 5 / +"
_list = _input.split(" ") # str to list
print("The origin list is: {}.".format(_list))
print(mid_2_after(_list))
- 代码练习4:斐波那契
最常见的栈:函数调用
# coding=utf-8
'''
@ Summary: 斐波那契
@ Update:
@ file: 3-7.斐波那契.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-8 下午10:18
'''
def fib(n):
# 递归
if n < 2:
return n
return fib(n-1) + fib(n-2)
def fib2(n):
# 生成器
if n < 0:
return None
a, b = 0, 1
for _ in range(n):
yield b
a, b = b, b + a
if __name__ == "__main__":
for i in fib2(5):
print(i)
- 代码练习5:字符串反转
# coding=utf-8
'''
@ Summary: 递归实现字符串翻转
@ Update:
@ file: 3-8.字符串翻转.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-9 上午9:05
'''
def reverse(s):
if len(s) <= 1:
return s
elif len(s) == 2:
return s[1] + s[0]
return s[-1] + reverse(s[:-1])
if __name__ == "__main__":
s = "ABC"
print(reverse(s))
- 代码练习6:汉诺塔
# coding=utf-8
'''
@ Summary: 递归实现汉诺塔
@ Update:
@ file: 3-9.汉诺塔.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-9 上午9:19
'''
def hanoi(n, x, y, z):
if n == 1:
print(x + " --> " + z)
else:
# 将n-1个从X移动到Y上,借助Z
hanoi(n-1, x, z, y)
# 将第n个从X移动到Z上
print(x + " --> " + z)
# 将n-1个从Y移动到Z上,借助X
hanoi(n-1, y, x, z)
if __name__ == "__main__":
hanoi(4, "X", "Y", "Z")
- 代码练习7:八皇后问题
- 代码练习8:括号匹配
# coding=utf-8
'''
@ Summary:
@ Update:
@ file: 括号匹配.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 10/15/19 9:55 PM
'''
def is_valid(s):
if not s:
return None
parent = "()[]{}"
opposite = {")":"(", "]":"[", "}":"{"}
st = [] # 栈
for item in s:
if item in parent:
st.append(item)
if item in opposite:
del_elem = st.pop()
if not st or opposite[del_elem] != st.pop():
return False
return not st
if __name__ == "__main__":
s = "9(){}"
if is_valid(s):
print("{} is OK.".format(s))
else:
print("{} is not OK.".format(s))
二、队列
1. 定义
先进先出,比如排队买票
2. 入队、出队
- 链式队列(链表表头为队头,表尾为队尾)
# coding=utf-8
'''
@ Summary: 链表实现队列,任意输入一串字符 # 结束
@ Update:
@ file: 队列.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 10/17/19 4:21 PM
'''
class Node(object):
def __init__(self, val):
self.val = val
self.next = None
def __repr__(self):
return str(self.val)
class LQueue(object):
def __init__(self):
self.front = Node(0)
self.rear = Node(0)
def is_empty(self):
# 判断是否为空
return self.front.val == self.rear.val
def enqueue(self, elem):
# 入队
elem = Node(elem)
if self.is_empty():
self.front.next = elem
self.rear.next = elem
self.rear = elem
def dequeue(self):
# 出队
if self.is_empty():
return None
del_node = self.front.next.val
if self.front.next == self.rear:
# 当只有一个元素的时候
self.front = self.rear
else:
self.front.next = self.front.next.next
return del_node
def get_head(self):
# 查看队头元素
if self.is_empty():
return None
return self.front.next
def p_queue(self):
# print queuep
if self.is_empty():
return None
tmp = self.front.next
while tmp:
print(tmp, end=" ")
tmp = tmp.next
if __name__ == "__main__":
# 创建队列
lq = LQueue()
data = input("请输入(#结束):")
while data != "#":
lq.enqueue(data)
data = input("请输入(#结束):")
lq.p_queue() # 最初的链式列表
- 顺序队列的出队和入队
# coding=utf-8
'''
@ Summary: 顺序队列
@ Update:
@ file: 队列2.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 10/18/19 12:19 AM
'''
class SQueue(object):
def __init__(self):
self._queue = []
def is_empty(self):
return self._queue == []
def en_queue(self, elem):
self._queue.insert(0, elem)
# self._queue.append(elem)
def de_queue(self):
if self.is_empty():
return None
del_node = self._queue.pop()
# del_node = self._queue.pop(0)
return del_node
def p_queue(self):
if self.is_empty():
return None
return self._queue
if __name__ == "__main__":
sq = SQueue()
data = input("请输入(输入# 结束):")
while data != "#":
sq.en_queue(data)
data = input("请输入(输入# 结束):")
print(sq.p_queue())
sq.de_queue()
print(sq.p_queue())
队列跟栈一样,也是一种操作受限的线性表数据结构。
3. 循环队列
# coding=utf-8
'''
@ Summary: 顺序表实现循环队列
@ Update:
@ file: 循环队列.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 10/18/19 10:24 AM
'''
class SQueue(object):
def __init__(self, maxsize):
self.maxsize = maxsize # 自定义队列长度
self._queue = [None] * maxsize
self.front = 0 # 指向队头
self.rear = 0 # 指向队尾元素的下一个位置
def is_empty(self):
# 判断是否为空
return self.front == self.rear
def q_length(self):
# 当前队列长度计算 (rear-front+maxsize)%maxsize
return (self.rear - self.front + self.maxsize) % self.maxsize
def enqueue(self, elem):
# 队尾入队 elem push
if (self.rear + 1) % self.maxsize == self.front:
# 判断队列是否填满 (rear+1)%maxsize == front
print("The queue is full!")
self._queue[self.rear] = elem
self.rear = (self.rear + 1) % self.maxsize
def dequeue(self):
# 队头出队 elem pull
if self.is_empty():
return None
del_elem = self._queue[self.front]
self._queue[self.front] = None
self.front = (self.front + 1) % self.maxsize
return del_elem
def p_queue(self):
# print queue
if self.is_empty():
return None
for i in range(self.maxsize):
print(self._queue[i], end=" ")
print()
if __name__ == "__main__":
s = "abcdde"
sq = SQueue(15) # 循环队列
for item in range(10):
# 入队
sq.enqueue(item)
sq.p_queue() # 原始队列
for i in range(5): # 删除队头的5个元素:0~4
sq.dequeue()
sq.p_queue()
for i in range(8): # 从队尾增加8个元素:0~7
sq.enqueue(i)
sq.p_queue()
4.代码练习:
- 用两个栈实现队列 (剑指offer和力扣)
# coding=utf-8
'''
@ Summary: 剑指Offer-用两个栈来实现一个队列,完成队列的Push和Pop操作
@ Update: (先进先出)
@ file: 3-15.2栈实现队列.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-21 下午10:00
'''
import pysnooper
class SStack(object):
def __init__(self):
self.stackA = []
self.stackB = []
def is_empty(self):
# 判断satackA是否为空
return self.stackA == []
def push(self, elem):
# stackA入栈
self.stackA.append(elem)
@pysnooper.snoop(watch=("self.stackA", "self.stackB"))
def s_pop(self):
# stackA 出栈 stackB 入栈
if self.stackB:
return self.stackB.pop()
while self.stackA:
self.stackB.append(self.stackA.pop())
return self.stackB.pop()
if __name__ == "__main__":
ss = SStack()
for i in range(7):
ss.push(i)
print(ss.s_pop())
ss.push(8)
print(ss.s_pop())
- 用两个队列实现栈(力扣)
# coding=utf-8
'''
@ Summary: 进栈:元素入队列A(后进先出)
出栈:判断如果队列A只有一个元素,则直接出队。否则,把队A中的元素出队并入队B,
直到队A中只有一个元素,再直接出队。为了下一次继续操作,互换队A和队B。
@ Update:
@ file: 3-16.2队列实现栈.py
@ version: 1.0.0
@ Author: Lebhoryi@gmail.com
@ Date: 19-10-22 下午7:12
'''
class SQueue(object):
def __init__(self):
self.queueA = []
self.queueB = []
def is_empty(self):
return self.queueA == [] and self.queueB == []
def enqueue(self, elem):
# 入队
self.queueA.append(elem)
def dequeue(self):
#出队
if self.is_empty():
return None
# if len(self.queueA) == 1:
# return self.queueA.pop()
while len(self.queueA) > 1:
self.queueB.append(self.queueA.pop(0))
self.queueA, self.queueB = self.queueB, self.queueA
return self.queueB.pop()
if __name__ == "__main__":
sq = SQueue()
for i in range(1, 7):
sq.enqueue(i)
print(sq.dequeue())
sq.enqueue(3)
print(sq.dequeue())
print(sq.dequeue())