3.0线性数据结构
3.0.1定义:对于一个数据集合,如果某个元素被添加进来之后,它与其他元素的相对位置没有发生改变,则称为线性数据结构;
3.0.2常见线性数据结构:栈,队列,双端队列,列表。
3.1栈的定义及其他
3.1.1栈是一种添加操作和移除操作都发生在同一端(顶端)的有序集合;
3.1.2LIFO:栈满足的排序原则,也就是后进先出:最新添加的元素将被最先移除;
3.1.3栈的python实现
#栈的实现
class Stack:
def __init__(self):
self.items = []
def isEmpty(self):#栈空否?
return self.items == []
def push(self, object):#栈添加
self.items.append(object)
def pop(self):#弹出
return self.items.pop()
def peek(self):#返回端口元素
return self.items[-1]
def size(self):#返回大小
return len(self.items)
f1 = Stack()
print(f1.isEmpty())
f1.push(1)
f1.push(2)
f1.push(3)
f1.push(4)
print(f1.items)
print(f1.peek())
print(f1.pop())
print(f1.items)
print(f1.size())
print(f1.isEmpty())
结果:
True
[1, 2, 3, 4]
4
4
[1, 2, 3]
3
False
3.2与栈有关的问题举例
3.2.1括号匹配问题
括号匹配是指每一个左括号都有与之对应的一个右括号,并且括号对有正确的嵌套关系。
from pythonds.basic import Stack
def parChecker(symbolString):
s1 = Stack()
flag = True
index = 0
while index < len(symbolString) and flag:
symbol = symbolString[index]
if symbol == "(":
s1.push(symbol)
else:
if s1.isEmpty():#(()))排除这种右括号多的情况
flag = False
else:
s1.pop()
index = index + 1
if flag and s1.isEmpty():#左括号不存在则为匹配
return True
else:
return False
print(parChecker("(()))))"))
print(parChecker("()()))(("))
结果:
False
False
3.2.2符号匹配问题
leetcode有效的括号https://leetcode.cn/problems/valid-parentheses/description/以下代码并不适用于leetcode题目哦
from pythonds.basic import Stack
def parChecker(symbolString):
s1 = Stack()
flag = True
index = 0
while index < len(symbolString) and flag:
symbol = symbolString[index]
if symbol in "({[":
s1.push(symbol)
else:
if s1.isEmpty():#(()))排除这种右括号多的情况
flag = False
else:
top = s1.pop()
if not matches(top,symbol):
flag = False
index = index + 1
if flag and s1.isEmpty():#左括号不存在则为匹配
return True
else:
return False
def matches(open,close):
opens = "{(["
closes = "})]"
return opens.index(open) == closes.index(close)
print(parChecker("{}[]()[{}]"))
print(parChecker("[[[]]{{{}}(((})))]"))
结果:
True
False
分析:
这段代码定义了两个函数:`parChecker` 和 `matches`。它们的作用是对一个包含括号、花括号和方括号的字符串 `symbolString` 检查其是否有效配对。主要思路是利用栈的数据结构来跟踪打开的括号。以下是详细解释:
1. `parChecker` 函数:
- 初始化一个空栈 `s1` 和标志变量 `flag` 设定为 `True`,代表默认认为字符串有效。
- 遍历输入字符串 `symbolString` 的每个字符,用索引 `index` 作为迭代器:
- 如果遇到开括号(如 `{`, `[`, 或 `(`),则推入栈 `s1`。
- 如果遇到闭合括号,检查栈是否为空:
- 若为空,意味着缺少对应的开括号,`flag` 设置为 `False`。
- 否则,弹出栈顶的开括号,通过 `matches` 函数比较是否匹配当前闭合括号。
- 如果 `matches` 返回 `False`,则字符串无效,`flag` 设置为 `False`。
- 遍历结束后,如果字符串有效(`flag` 仍为 `True`)并且栈也为空(说明所有的开括号都有相应的闭合括号),返回 `True`;否则返回 `False`。
2. `matches` 函数:
- 判断给定的开括号 `open` 是否有与其相配对的闭合括号。它通过比较 `opens` 和 `closes` 中对应位置的括号是否相同来确定。
最后,示例调用了这两个函数并打印结果,分别验证了两段字符串的有效性。
3.2.3十进制转换为二进制问题
from pythonds.basic import Stack
def divideBy2(decNumber):
remstack = Stack()
while decNumber > 0:
rem = decNumber % 2
remstack.push(rem)
decNumber = decNumber // 2#整除
binString = ""
while not remstack.isEmpty():
binString = binString + str(remstack.pop())
return binString
3.2.4十进制转换为任意进制问题
from pythonds.basic import Stack
def baseConverter(decNumber,base):
digits = "0123456789ABCDEF"
remstack = Stack()
while decNumber > 0:
rem = decNumber % base
remstack.push(rem)
decNumber = decNumber // base
newString = ""
while not remstack.isEmpty():
newString = newString + digits[remstack.pop()]
return newString
这段Python代码定义了一个名为 `baseConverter` 的函数,用于将十进制数(decNumber)转换成任意基数(base)的表示法。它使用了栈(Stack)数据结构来辅助计算。步骤如下:
1. 定义一个字符数组 `digits`,包含从0到15的十六进制数字,因为通常base <= 16。
2. 创建一个空的栈 `remstack`,用于存储余数(decimal number除以base后的余数)。
3. 当原始十进制数 `decNumber` 大于0时,继续进行以下操作:
a. 计算 `decNumber` 除以 `base` 的余数,并将其压入栈中。
b. 更新 `decNumber` 为商,即整除 `base` 后的结果。
4. 当余数栈不再为空时(说明已经处理完所有位),开始弹出栈顶元素,对应的是高位的数字,并拼接到新的字符串 `newString` 之前。
5. 最终返回转换后的字符串 `newString`,它是从高位到低位按顺序组成的。
举个例子,如果你传入 `decNumber = 13` 和 `base = 2`,这个函数将返回 "1101" (二进制表示),因为 13 转换成二进制就是 1101。
3.3队列的定义及其他
3.3.1队列是指添加发生在尾部,移除发生在头部的有序集合。即:新元素通过添加从尾部进入,再通过移除一步一步移动到头部,直到成为被移除的元素。
3.3.2FIFO:先进先出
3.3.3python实现队列
class Queue:
def __init__(self):
self.queue = []
def enqueue(self,item): #发生在尾部的添加操作
self.queue.insert(0,item)
def dequeue(self): #发生在头部的移除操作
return self.queue.pop()
def isEmpty(self):
return self.queue == []
def size(self):
return len(self.queue)
3.4与队列有关的问题举例
3.4.1传土豆问题
假设有六个人围成一个圈,先从一号开始传递,1>2>3>4>5>6>1······
from pythonds.basic import Queue
def hotPotato(namelist,num):
simqueue = Queue()
for name in namelist:#初始化
simqueue.enqueue(name)
while simqueue.size() > 1:
for i in range(num):#头部删除,尾部又添加,形成一个环
simqueue.enqueue(simqueue.dequeue())
simqueue.dequeue()
return simqueue.dequeue()
这段Python代码实现了一个名为 "hotPotato" 的游戏模拟。这个游戏通常涉及一个土豆(在这里作为字符串名称)和一组玩家。游戏规则是这样的:玩家们围成一圈,然后逐次传递土豆,当音乐停止时,手里拿着土豆的人会被淘汰。这个过程会一直持续,直到只剩下一个人为止。
代码首先创建一个空的模拟队列 `simqueue`,并将所有玩家名字 `namelist` 入队。然后进入一个while循环,只要队列的大小大于1(意味着还有超过一个玩家),就按照以下步骤进行:
1. 遍历 `num`(表示传球次数)次,每次都从队头删除一个元素(模拟传球)并将其添加回队尾,形成一个闭环。这样做使得最后一个放入队列的人成为下一个被淘汰者。
2. 循环结束后,由于 `num` 次传递后只剩下了最后一个人,所以再次从队头删除(此时队列只有一个元素)并返回,这就是最终的赢家。
整个过程模拟了一轮又一轮的淘汰,直到只剩一人为止。
3.4.2打印机进程
import random
from pythonds.basic import Queue
class Printer:#模拟打印机
def __init__(self,ppm):
self.pagerate = ppm#定义打印机每秒可以打印的页数
self.currentTask = None
self.timeRemaining = 0
def tick(self):
if self.currentTask != None:
self.timeRemaining = self.timeRemaining -1
if self.timeRemaining <= 0:
self.currentTask = None
def busy(self):
if self.currentTask != None:
return True
else:
return False
def startNext(self,newtask):
self.currentTask = newtask
self.timeRemaining = newtask.getPages() * 60 /self.pagerate
class Task:#模拟单个打印任务
def __init__(self,time):
self.timestamp = time#记录单个打印任务的时间戳
self.pages = random.randrange(1,21)#对产生的任务初始化随机页数
def getStamp(self):
return self.timestamp
def getPages(self):
return self.timestamp
def waitTime(self,currenttime):
return currenttime - self.timestamp
#主程序函数
def simulation(numSeconds,pagesPerMinute):
labprinter = Printer(pagesPerMinute)#初始化打印机,定义每秒打印速度
printQueue = Queue()#初始化打印队列
waitingtimes = []#初始化等待时间列表,方便后续使用
for currentSecond in range(numSeconds):#模拟每秒钟的情况
if newPrintTask():#若调用newPrintTask返回True,即该秒下有新任务产生,执行if条件语句
task = Task(currentSecond)
printQueue.enqueue(task)#将该秒产生的任务导入打印队列
if(not labprinter.busy()) and (not printQueue.isEmpty()):#当打印机没有打印任务时 且 打印队列非空时执行if条件语句 也就是把打印队列里的打印任务压入打印机执行
nexttask = printQueue.dequeue()#弹出打印队列头部任务
waitingtimes.append(nexttask.waitTime(currentSecond))#
labprinter.startNext(nexttask)
labprinter.tick()
averageWait = sum(waitingtimes) / len(waitingtimes)
print("Average Wait {:.2f} seconds {} tasks remaining".format(averageWait,printQueue.size()))
def newPrintTask():#每秒钟是否产生新任务,如果产生,num == 180
num = random.randrange(1,181)
if num == 180:
return True
else:
return False
for i in range(10):
simulation(3600,10)
ai分析:
这个Python代码片段定义了一个简单的打印任务调度系统。它主要包括两个类:`Printer` 和 `Task`。`Printer` 类模拟了打印机的行为,包括打印速率、任务处理时间和状态。`Task` 类则代表打印任务,包含任务开始时间、页面数量以及计算等待时间的功能。
`simulation` 函数是主程序,接受两个参数:总运行秒数和每分钟的打印页数。函数通过循环模拟每秒的情况:
1. 检查是否有新的打印任务(由 `newPrintTask()` 决定),如果有,则创建一个新的 `Task` 并加入打印队列。
2. 如果打印机当前没有任务并且打印队列非空,从队列中取出第一个任务给打印机,并记录等待时间。
3. 每秒递减当前任务剩余时间,直到完成。
4. 统计所有任务的平均等待时间并报告剩余的任务数量。
`newPrintTask` 函数会随机生成1到180之间的整数,180表示这一秒会产生新的打印任务。
在这个场景中,循环运行 `simulation` 函数10次,每次运行3600秒,以此进行多次模拟测试。
3.5双端队列的定义及其他
3.5.1双端队列指:移除与添加操作发生在任何一端都可以的有序集合;
3.5.2双端队列的排序原则不同于栈的先进后出,队列的先进先出原则,具体排序原则根据使用者的需求规定;
3.5.3双端队列在前端发生的操作时间复杂度皆为常数阶,后端发生的操作皆为n阶。(注意:不论是栈、队列或者双端队列,前端都指列表的尾部,后端都指列表的前部)
3.5.4双端队列的python实现
class Deque:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def size(self):
return len(self.items)
def addFront(self,item):
self.items.append(item)
def addRear(self,item):
self.items.insert(0,item)
def removeFront(self):
return self.items.pop()
def removeRear(self):
return self.items.pop(0)
3.6与双端队列有关的问题举例
3.6.1回文检测器
from pythonds.basic import Deque
def palchecker(aString):
chardeque = Deque()
for ch in aString:
chardeque.addRear(ch)
stillEqual = True
while chardeque.size() > 1 and stillEqual :
first = chardeque.removeFront()
last = chardeque.removeRear()
if first != last:
stillEqual = False
return stillEqual
print(palchecker("1112x2111"))
3.7列表
3.7.1无序列表
指其中每个元素相对与其他元素都有自己的一个位置,所以并不是真的无序
而以下两种都是基于链式结构实现
3.7.2利用链表实现无序列表(链式结构)
class Node:#节点类
def __init__(self,initdata):
self.data = initdata
self.next = None#初始化下一节点不指向任何引用
def getData(self):#访问该节点数据
return self.data
def getNext(self):#访问下一个节点数据
return self.next
def setData(self,newdata):#重新赋值本节点数据
self.data = newdata
def setNext(self,newnext):
self.next = newnext
class UnorderedList:#链表类,不包含任何节点对象,只包含第一个节点的引用
def __init__(self):
self.head = None
def isEmpty(self):#检查链表是否为空
return self.head == None
def add(self,item):#添加的这个元素位于这个链表的首端
temp = Node(item)
temp.setNext(self.head)
self.head = temp
def length(self):
current = self.head
count = 0
while current != None:
count += 1
current = current.getNext()
return count
def search(self,item):
current = self.head
found = False
while current != None and not found:
if current.getData() == item:
found = True
else:
current = current.getNext()
return found
def remove(self,item):
current = self.head
previous = None
found = False
while not found:
if current.getData() == item:
found = True
else:
previous = current
current = current.getNext()
if previous == None:
self.head = current.getNext()
else:
previous.setNext(current.getNext())
注意:有关此部分的重点说明都在注释里。
3.7.3利用链表实现有序列表(链式结构)
class Node:#节点类
def __init__(self,initdata):
self.data = initdata
self.next = None#初始化下一节点不指向任何引用
def getData(self):#访问该节点数据
return self.data
def getNext(self):#访问下一个节点数据
return self.next
def setData(self,newdata):#重新赋值本节点数据
self.data = newdata
def setNext(self,newnext):
self.next = newnext
class UnorderedList:#链表类,不包含任何节点对象,只包含第一个节点的引用
def __init__(self):
self.head = None
def isEmpty(self):#检查链表是否为空
return self.head == None
def search(self,item):
current = self.head
found = False
stop = False
while current != None and not found and not stop:
if current.getData() == item:
found = True
else:
if current.getData() > item:
stop = True
else:
current = current.getNext()
return found
def add(self,item):
current = self.head
previous = None
stop = False
while current != None and not stop:
if current.getData() > item:
stop = True
else:
previous = current
current = current.getNext()
temp = Node(item)
if previous == None:
temp.setNext(self.head)#注意顺序不能颠倒,否则会失去head
self.head = temp
else:
temp.setNext(current)
previous.setNext(temp)
注意:以上方式皆为单链表哦