Python数据结构实现(二)
一:栈
1. 用数组实现一个顺序栈
class ArrayStack:
'''
顺序栈,栈顶指针指向的是栈顶元素
'''
def __init__(self, capacity=20): # 默认栈容量为20
self.capacity = capacity
self.data = [None] * self.capacity
self.top = -1
self.size = 0
def push(self, value):
'''
元素进栈
'''
if self.top == self.capacity - 1:
raise Exception('push failed: stack is full')
else:
self.top += 1
self.data[self.top] = value
self.size += 1
def pop(self):
'''
元素出栈,并返回该元素
'''
if self.top == -1:
raise Exception('pop failed: the stack is empty')
else:
elem = self.data[self.top]
self.top -= 1
self.size -= 1
return elem
def isEmpty(self):
'''判断栈是否为空'''
return self.top == -1
def isFull(self):
'''判断是否为满栈'''
return self.top == self.capacity - 1
2. 用链表实现一个链式栈
class ListNode:
def __init__(self, value, pnext=None):
self.data = value
self.next = pnext
class LinkedlistStack:
'''链栈'''
def __init__(self):
self.head = ListNode(None)
self.size = 0
def push(self, value):
'''元素入栈:前插结点'''
node = ListNode(value)
node.next = self.head.next
self.head.next = node
self.size += 1
def pop(self):
'''元素出栈并返回'''
if self.isEmpty():
raise Exception('pop failed: the stack is empty')
else:
p = self.head.next
self.head.next = p.next
self.size -= 1
return p
def isEmpty(self):
return self.size == 0
stack = LinkedlistStack()
stack.push(3)
stack.push(4)
stack.push(5)
stack.push(6)
q = stack.pop()
3.编程模拟实现一个浏览器的前进、后退功能
class ListNode:
'''链表结点'''
def __init__(self, value, pnext=None):
self.data = value
self.next = pnext
class LinkedlistStack:
'''链栈'''
def __init__(self):
self.head = ListNode(None)
self.size = 0
def push(self, value):
'''元素入栈:前插结点'''
node = ListNode(value)
node.next = self.head.next
self.head.next = node
self.size += 1
def pop(self):
'''元素出栈并返回'''
if self.isEmpty():
raise Exception('pop failed: the stack is empty')
else:
p = self.head.next
self.head.next = p.next
self.size -= 1
return p
def isEmpty(self):
if self.size == 0:
return True
else:
return False
class Simulation:
'''
模仿浏览器的前进后退
用两个链栈currentStack和helperStack来存储浏览的网页地址
currentStack的栈顶元素为当前浏览的网页地址
'''
def __init__(self, mainPage): # mainPage为浏览器主页网址
self.currentStack = LinkedlistStack()
self.helperStack = LinkedlistStack()
self.currentStack.push(mainPage)
def show(self):
'''显示当前网址的网页内容'''
website = self.currentStack.head.next.data
print('Showing the content of the current website:%s' % website)
def goNew(self, newWebsite):
'''访问之前未访问过的新网址'''
self.currentStack.push(newWebsite)
self.helperStack.head.next = None # 当浏览了新网址,就不能再进行前进操作,故清空helperStack
def goFoward(self, nextWebsite):
if self.helperStack.isEmpty():
return None # 当helperStack为空栈时,无法进行GoForward操作
else:
p = self.helperStack.pop()
self.currentStack.push(p)
self.show()
def goBack(self, preWebsite):
if self.currentStack.size == 1:
return None # 当currentStack只有一个网址时,无法进行GoBack操作
else:
p = self.currentStack.pop()
self.helperStack.push(p)
self.show()
4. leetcode相关习题
(1). Valid Parentheses(有效的括号)
用栈结构实现
class Solution:
def isValid(self, s: str) -> bool:
stack = [None] * len(s)
top = -1
for _ in s:
if _ == '(':
top += 1
stack[top] = _
elif _ == '[':
top += 1
stack[top] = _
elif _ == '{':
top += 1
stack[top] = _
elif _ == ')':
if stack[top] != '(':
return False
else:
top -= 1
elif _ == ']':
if stack[top] != '[':
return False
else:
top -= 1
elif _ == '}':
if stack[top] != '{':
return False
else:
top -= 1
return top == -1
(2). Longest Valid Parentheses(最长有效的括号)
class Solution:
def longestValidParentheses(self, s: str) -> int:
'''
遍历字符串,设置一个judge数组,将符合条件的左右括号在judge数组中的对应位置上置为True
遍历judge数组,求出最大连续True的长度
'''
stack, judge = [], [0] * len(s)
for index, parentthese in enumerate(s):
if parentthese == '(':
stack.append(index)
else:
if stack:
judge[index] = judge[stack.pop()] = True
maxnum = count = 0
for n in judge:
if n:
count += 1
else:
maxnum = max(maxnum, count)
count = 0
return max(maxnum, count) # 若以合法括号的')'结尾的情况,此时的值在最后一次的count里
(3). Evaluate Reverse Polish Notatio(逆波兰表达式求值)
class Solution:
def evalRPN(self, tokens):
stack = [None] * len(tokens); top = -1
operators = ['+', '-', '*', '/']
for token in tokens:
if token in operators:
if top == -1:
return
else:
num1 = stack[top]; top -= 1
print(num1)
num2 = stack[top]; top -= 1
result = Solution.operation(num1, num2, token)
top += 1; stack[top] = result
else:
top += 1; stack[top] = int(token)
return stack[0]
def operation(num1, num2, op):
if op == '+':
return num2 + num1
elif op == '-':
return num2 - num1
elif op == '*':
return num2 * num1
else:
if num1 != 0:
return int(num2 / num1) # 若用//则除负数时结果会出错,如7 // -3python的结果为-3,而我们要求的值为-2
二:队列
1. 用数组实现一个顺序队列
class Queue:
'''
数组实现顺序队列,头尾指针初始时都为0
头指针指向队头元素,尾指针指向队尾元素的下一个位置
当头尾指针指向同一处时,队空
'''
def __init__(self, capacity=20):
self.capacity = capacity
self.size = 0
self.front = 0
self.rear = 0
self.data = [None] * self.capacity
def enqueue(self, value):
'''入队'''
if self.queueFull():
raise Exception('Enqueue failed: Queue is full')
else:
self.data[self.rear] = value
self.rear += 1
self.size += 1
def dequeue(self):
'''出队并返回出队元素'''
if self.queueEmpty():
raise Exception('Dequeue failed: Queue is empty')
else:
x = self.data[self.front]
self.front += 1
self.size -= 1
return x
def queueEmpty(self):
return self.size == 0
def queueFull(self):
return self.size == self.capacity
2. 用python实现一个链式队列
class ListNode:
def __init__(self, value, pnext=None):
self.data = value
self.next = pnext
class LinkedlistQueue:
'''
链表实现带头结点的链队
头指针始终指向头结点,尾指针指向队尾元素
当头指针和尾指针都指向头结点时表示队空
'''
def __init__(self, headNode):
self.front = self.rear = headNode
self.size = 0
def isEmpty(self):
return self.rear == self.front
def enqueue(self, value):
'''入队'''
node = ListNode(value)
self.rear.next = node
self.rear = node
self.size += 1
def dequeue(self):
'''出队并返回出队元素,分别考虑链队只有一个结点和队空的情况'''
if self.size == 1: # 只有一个结点,出队后头尾指针都指向头结点
x = self.rear.data
self.rear = self.front
self.front.next = None
self.size -= 1
return x
elif self.size == 0:
raise Exception('Dequeue failed: Queue is empty')
else:
x = self.front.next
self.front.next = x.next
self.size -= 1
return x.data
3. 用python实现一个循环队列
class RecyQueue:
'''
顺序结构循环队列
头指针指向队头元素,尾指针指向队尾元素的下一个位置
牺牲一个空间位置用于区分队空队满
队空:front == rear
队满:(rear + 1) % capacity == front
'''
def __init__(self, capacity=20):
self.capacity = capacity
self.data = [None] * self.capacity
self.front = self.rear = 0
def isEmpty(self):
return self.front == self.rear
def isFull(self):
return (self.rear + 1) % self.capacity == self.front
def enqueue(self, value):
if self.isFull():
raise Exception('Enqueue failed: Queue is full')
else:
self.data[self.rear] = value
self.rear = (self.rear + 1) % self.capacity
def dequeue(self):
if self.isEmpty():
raise Exception('Dequeue failed: Queue is empty')
else:
x = self.data[self.front]
self.front = (self.front + 1) % self.capacity
return x
4.LeetCode练习题
(1). Design Circular Deque(设计一个双端队列)
class MyCircularDeque:
'''
采用循环队列实现双端队列
队头指针指向队头元素
队尾指针指向队尾元素的下一个位置
增加一个size变量统计队列的有效元素,利用全部的存储空间
'''
def __init__(self, k: int):
"""
Initialize your data structure here. Set the size of the deque to be k.
"""
self.capacity = k
self.data = [None] * self.capacity
self.size = 0
self.front = self.rear = 0
def insertFront(self, value: int) -> bool:
"""
Adds an item at the front of Deque. Return true if the operation is successful.
"""
if not self.isFull():
self.front = (self.front - 1) % self.capacity # 队头入队,减一
self.data[self.front] = value
self.size += 1
return True
else:
return False
def insertLast(self, value: int) -> bool:
"""
Adds an item at the rear of Deque. Return true if the operation is successful.
"""
if not self.isFull():
self.data[self.rear] = value
self.rear = (self.rear + 1) % self.capacity
self.size += 1
return True
else:
return False
def deleteFront(self) -> bool:
"""
Deletes an item from the front of Deque. Return true if the operation is successful.
"""
if not self.isEmpty():
self.front = (self.front + 1) % self.capacity
self.size -= 1
return True
else:
return False
def deleteLast(self) -> bool:
"""
Deletes an item from the rear of Deque. Return true if the operation is successful.
"""
if not self.isEmpty():
self.rear = (self.rear - 1) % self.capacity
self.size -= 1
return True
else:
return False
def getFront(self) -> int:
"""
Get the front item from the deque.
"""
if not self.isEmpty():
return self.data[self.front]
else:
return -1
def getRear(self) -> int:
"""
Get the last item from the deque.
"""
if not self.isEmpty():
return self.data[(self.rear-1)%self.capacity]
else:
return -1
def isEmpty(self) -> bool:
"""
Checks whether the circular deque is empty or not.
"""
return self.size == 0
def isFull(self) -> bool:
"""
Checks whether the circular deque is full or not.
"""
return self.size == self.capacity
# Your MyCircularDeque object will be instantiated and called as such:
# obj = MyCircularDeque(k)
# param_1 = obj.insertFront(value)
# param_2 = obj.insertLast(value)
# param_3 = obj.deleteFront()
# param_4 = obj.deleteLast()
# param_5 = obj.getFront()
# param_6 = obj.getRear()
# param_7 = obj.isEmpty()
# param_8 = obj.isFull()
(2). Sliding Window Maximum(滑动窗口最大值)
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
if nums:
if k == 1:
return nums
else:
a = 0; result = []
length = len(nums)
currWindow = nums[:k]
maxnum = max(currWindow) # 第一个窗口的最大值
a += 1
result.append(maxnum)
while a+k-1 < length:
currWindow = nums[a:a+k]
if nums[a-1] != maxnum: # 若出队的元素不是最大值,则比较新进元素与当前最大值(当然同一个窗口中可能有多个最大值,只是一定程度上减少计算量)
if currWindow[-1] > maxnum:
maxnum = currWindow[-1]
else: # 若最大值出队,则重新求得当前窗口的最大值
maxnum = max(currWindow)
result.append(maxnum)
a += 1
return result
else:
return []
三. 递归
1. 编程实现斐波那契数列求值 f(n)=f(n-1)+f(n-2)
def Fibonacci(n):
'''递归求斐波那契数列的第n个值'''
a = b = 1
if n == 0 or n == 1:
return 1
else:
return Fibonacci(n-1) + Fibonacci(n-2)
2. 编程实现求阶乘 n!
def factorial(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial(n-1)
3. 编程实现一组数据集合的全排列
def perm(nums, end):
'''生成nums[0]到nums[end]的全排列'''
if end == 0:
for num in nums:
print(num,end=' ')
print('\n')
else:
for i in range(end+1):
nums[i], nums[end] = nums[end], nums[i]
perm(nums, end-1)
nums[i], nums[end] = nums[end], nums[i] # 恢复数组
perm([1,2,3],2)
4. LeetCode练习题
(1) Climbing Stairs(爬楼梯)
由于递归函数重复计算量很大,在LeetCode上直接用递归思路的话会超时,好在python的小‘trick’确实多。这里采用了python的缓存机制,将递归函数的计算结果都存储起来,之后直接调用即可。
@lru_cache(num) :num为存储的数量,默认为128
from functools import lru_cache
class Solution:
@lru_cache(10**8)
def climbStairs(self, n: int) -> int:
if n == 1:
return 1
elif n == 2:
return 2
else:
return self.climbStairs(n-1) + self.climbStairs(n-2) # 分别对应第一步选择走一步和走两步的情况