线性结构-栈
栈(Stack)它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
ADT定义: stack()/ push(data)/ pop()/ peek()/ isEmpty()/ size()
class Stack():
#定义栈类型 先进后出 last-in first-out(LIFO)
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def push(self,item):
self.items.append(item)
def pop(self):
return self.items.pop()
def peek(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
栈的应用举例
1) 进制转换
在十进制转常见的二进制、八进制、十六进制的过程中,需要重复对余数入栈,直至商为0,最后再将栈内的数据进行出栈操作,输出的字符串就是转换后的结果。
十进制转二进制函数如下:
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
print(divideBy2(15))
稍通用的一个十进制转换的函数如下:
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 + str(digits[remstack.pop()])
return newString
print(baseConverter(255, 2))
print(baseConverter(255, 16))
2) 表达式转换
表达式分为中缀表达式、前缀表达式、后缀表达式。比如中缀表达A+BC, 换成前缀表达式就是+ABC, 换成后缀表达式是ABC+ ;再比如中缀表达式(A+B)(C+D), 换成前缀表达式为 +AB+CD, 换成后缀表达式为 AB+CD+ ;另外中缀表达A+BC 转换成全括号表达式为 (A+(BC))。
稍复杂表达式的举例: (A+B)C-(D-E)(F+G) ==> -+ABC-DE+FG (前缀) ==> AB+CDE-FG+- (后缀) 。
编写中缀表达式转后缀表达式函数,扫描操作过程如下:
- 如果单词是操作数,直接添加到后缀表达式列表的未尾;
- 如果单词是"(",则入栈操作;
- 如果单词是")",则反复出栈,并追回到后缀表达式中,直到遇到"("为止;
- 如果遇到操作符,则入栈操作(入栈时如果栈不为空,且栈顶操作符优先级大于当前操作符优先级,需要先将栈顶弹出并添加到后缀表达式中,然后再将当前操作符入栈);
代码如下:
def matches(open, close):
opens = "([{"
closers = ")]}"
return opens.index(open) == closers.index(close)
def convert(str):
opdata = "ABCDEFGIHKLMNOPQRSTUVWXYZ"
opsigns = {}
opsigns["*"] = 3
opsigns["/"] = 3
opsigns["+"] = 2
opsigns["-"] = 2
opsigns["("] = 1
orgstr = str.split()
postfixList = []
opstack = Stack()
for token in orgstr:
if token in opdata:
postfixList.append(token)
if token == "(":
opstack.push(token)
if token == ")":
top = opstack.pop()
while top != "(":
postfixList.append(top)
top = opstack.pop()
if token in "+-*/":
while not opstack.isEmpty() and opsigns[opstack.peek()] >= opsigns[token]:
postfixList.append(opstack.pop())
opstack.push(token)
while not opstack.isEmpty():
postfixList.append(opstack.pop())
return(" ".join(postfixList))
print(convert("A + ( B - C ) / D"))
后缀表达式的求值 (求值时需要将扫描到的操作数入栈)
编写后缀表达式求值函数,操作过程如下:
- 创建operandStack用于暂存操作数
- 将后缀表达式拆分成单词列表
- 从左向右扫描单词列表. 如果扫描到操作数,将其转换int,并入栈.
- 如果扫描到一个操作符,连续弹出两个操作数,并开始求值,最后将值再次入栈.
- 扫描结束时,栈顶的值就是表达式的值
代码如下:
def doMath(opsigns, opdata1, opdata2):
if opsigns == "*":
return opdata1 * opdata2
elif opsigns == "/":
return opdata1 / opdata2
elif opsigns == "+":
return opdata1 + opdata2
else:
return opdata1 - opdata2
def postfixEval(postfixExpr):
operandStack = Stack()
tokenList = postfixExpr.split()
for token in tokenList:
if token in '0123456789':
operandStack.push(int(token))
else:
opdata2 = operandStack.pop()
opdata1 = operandStack.pop()
result = doMath(token, opdata1, opdata2)
operandStack.push(result)
return operandStack.pop()
print(postfixEval("5 7 3 - 2 / +"))
线性数据结构-队列(Queue)
队列(Queue)是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队首。
常见的有: 打印队列、操作系统的任务进程队列、键盘输入队列等。
ADT定义: Queue()/ enqueue(item)/ dequeue()/ isEmpty()/ size()
class Queue():
#定义队列类,先进先出First-in First-out(FIFO)
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def enqueue(self, item):
self.items.insert(0, item)
def size(self):
return len(self.items)
def dequeue(self):
return self.items.pop()
队列的应用举例
1) 约瑟夫(热土豆)问题;类似问题一般通过队列模拟的方式显示结果。
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()
print(hotPotato(["Eric", "Hellen", "Robin", "Tom", "Elision", "Jack"], 7))
2) 打印任务
一个实验室,在做生意的一个小时内,大约有10名学生在场,这一小时中,每人会发起2次左右的打印每次1~20页。打印机支持两种模式:以草稿模式 每分钟10页;以正常模式 每分钟5页。问题:如何设定打印机的模式,让大家都不会等太久的前提下尽量提高打印质量?
此问题需要一段程序来模拟这种打印任务场景,然后对程序运行的结果进行分析,以支持对打印机模式设定的决策。
问题建模:
首先对问题进行抽象,确定相关的对象和过程。
对象:打印任务、打印队列、打印机。
- 打印任务的属性:提交时间 、打印页数;
- 打印队列的属性: 具有FIFO性质的队列;
- 打印机的属性: 打印速度、是否忙;
过程:生成和提交打印任务。 - 确定生成概率:实例为每小时会有10个学生提交的20个作业,这样,概率是每180秒会有1个作业生成 并提交,概率为每秒 1/180. <=== 20作业/3600秒
- 确定打印页数:实例是1~20页,那么就是1~20页之间概率相同。
- 实施打印:
a) 当前的打印作业:正在打印的作业 ;
b) 打印结束倒计时:新作业开始打印时,开始倒计时,回0表示打印完毕,可以处理下一个作业;
模拟时间
统一的时间框架:以秒均匀流逝的时间,设定结束时间
同步所有过程:在一个时间单位内,对生成打印任务和实施打印两个过程各处理一次。
模拟流程
- 创建打印队列对象;
- 时间按照秒的单位流逝:
a) 按照概率生成打印作业,加入打印队列;
b) 如果打印机空闲,且队列不空,则取出队首作业打印,记录此作业等待时间 ;
c) 如果打印机忙,则按照打印速度进行秒打印;
d) 如果当前作业打印完成,则打印机进入空闲; - 时间用尽,开始统计平均等待时间 ;
代码如下:
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
import random
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.pages
def waitTime(self,currenttime):
return currenttime - self.timestamp
def newPrintTask():
num = random.randrange(1, 181) # 以1/180的机率 生成新的打印任务
if num == 180:
return True
else:
return False
def simulation(numSeconds,pagesPerMinute):
labprinter = Printer(pagesPerMinute)
printQueque = Queue()
waitingtimes = []
for currentSecond in range(numSeconds):
if newPrintTask():
task = Task(currentSecond)
printQueque.enqueue(task)
if (not labprinter.busy()) and (not printQueque.isEmpty()):
nexttask = printQueque.dequeue()
waitingtimes.append(nexttask.waitTime(currentSecond))
labprinter.startNext(nexttask)
labprinter.tick()
averageWait=sum(waitingtimes)/len(waitingtimes)
print("Average Wait %6.2f secs %3d tasks remaining." %(averageWait,printQueque.size()))
for i in range(10):
simulation(3600,10)
双端队列(Deque)
双端队列队首、队尾都可以添加或删除数据。
ADT定义 Deque()/ addFront(item)/ addRear(item)/ removeFront()/ removeRear/ isEmpty()/ size()
代码如下:
class Deque:
# 本例list[0]为尾端, list[-1]为首端
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def addFront(self, item):
self.items.append(item)
def addRear(self, item):
self.items.index(0, item)
def removeFront(self):
return self.items.pop()
def removeRear(self):
return self.items.pop(0)
def size(self):
return len(self.items)
双端队列应用举例 - 回文词的判断
回文词的判断: 两端同时出队并判断是否相同,最后队列长度为1或0
代码如下:
def palchecher(aString):
chardeque = Deque()
for char in aString:
chardeque.addFront(char)
stillEqual = True
while chardeque.size() > 1 and stillEqual:
if chardeque.removeFront() != chardeque.removeRear():
stillEqual = False
return stillEqual
print(palchecher("abcdcba"))
print(palchecher("abcdcbe"))
线性数据结构-链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
1) 无序列表(unordered list)
#ADT定义 List()/ add(item)/ remove(item)/ search(item)/ isEmpty()/ size()/ append(item)/ index(item)/ pop()/ pop(pos)
使用链表实现无序表, 代码如下:
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 add(self, item):
#本例的add方法将数据插入到链表第一项
newNode = Node(item)
newNode.setNext(self.head)
self.head = newNode
def size(self):
current = self.head
count = 0
while current:
count += 1
current = current.getNext()
return count
def search(self, item):
current = self.head
found = False
while current and not found:
if current.data == item:
found = True
else:
current = current.getNext()
return found
def remove(self, item):
current = self.head
previous = Node
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())
def traverse(self):
current = self.head
while current:
print(current.getData(), end= " ")
current = current.getNext()
unorderedseq = UnorderedList()
unorderedseq.add(10)
unorderedseq.add(20)
unorderedseq.add(30)
unorderedseq.remove(20)
unorderedseq.traverse()
print(unorderedseq.search(10))
print(unorderedseq.size())
2) 有序列表(orderedList)
ADT定义 OrderedList() / add(item)/ remove(item)/ search(item)/ size()/ index(item)/ pop()/ pop(pos)
使用链表实现有序列表,代码如下:
class OrderedList:
def __init__(self):
self.head = None
def add(self, item):
current = self.head
previous = None
stop = False
while current and not stop:
if item <= current.data:
stop = True
else:
previous = current
current = current.getNext()
newNode = Node(item)
if previous == None:
newNode.setNext(self.head)
self.head = newNode
else:
newNode.setNext(current)
previous.setNext(newNode)
def remove(self, item):
current = self.head
previous = None
found = False
while current and not found:
if current.data == item:
found = True
else:
previous = current
current = current.getNext()
if previous == None:
self.head = self.head.getNext()
else:
previous.setNext(current.getNext)
def search(self, item):
current = self.head
found = False
while current and not found:
if current.data == item:
found = True
elif current.data > item :
break
else:
current = current.getNext()
return found
def traverse(self):
current = self.head
while current:
print(current.data, end=" ")
current = current.getNext()
orderedseq = OrderedList()
orderedseq.add(50)
orderedseq.add(30)
orderedseq.add(35)
orderedseq.remove(30)
orderedseq.traverse()
print(orderedseq.search(45))
链表的另一种代码风格
代码如下:
class LinkedNode:
# 定义单链表中的节点
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
# 定义链表类
def __init__(self):
self._head = None
def isEmpty(self):
return self._head == None
def addheader(self, data):
newNode = LinkedNode(data)
newNode.next = self._head
self._head = newNode
def append(self, data):
currentNode = self._head
newNode = LinkedNode(data)
if self._head == None:
self._head = newNode
else:
while currentNode.next != None:
currentNode = currentNode.next
currentNode.next = newNode
def tailpos(self):
currentNode = self._head
while currentNode.next :
currentNode = currentNode.next
return currentNode
def pop(self):
currentNode = self._head
previousNode = None
while currentNode.next:
previousNode = currentNode
currentNode = currentNode.next
previousNode.next = None
return currentNode.data
def size(self):
currentNode = self._head
count = 0
while currentNode:
count += 1
currentNode = currentNode.next
return count
def searchpos(self, pos):
currentNode = self._head
count = 1
if pos < 1:
return None
else:
while currentNode and count != pos:
currentNode = currentNode.next
count += 1
return currentNode
def search(self, data):
currentNode = self._head
while currentNode.data != data and currentNode:
currentNode = currentNode.next
if currentNode.data == data:
return currentNode
elif currentNode == None:
return None
def traverse(self):
currentNode = self._head
while currentNode:
print(currentNode.data, end=' ')
currentNode = currentNode.next
print()
应用举例: 将两个有序链表合并且重新排序,最后输出排序后的结果
方法1,比较出两个原链表中较小的数据,将较小的数据再生成新node,将新node追加到新链表中(牺牲了空间,保留了原链表的结构)。
代码如下:
def orderMerge(lista,listb,listc):
currentNodea = lista._head
currentNodeb = listb._head
currentNodec = listc._head
while currentNodea and currentNodeb:
if currentNodea.data < currentNodeb.data:
newNode = LinkedNode(currentNodea.data)
currentNodea = currentNodea.next #链表A的当前节点向后移动一个
else:
newNode = LinkedNode(currentNodeb.data)
currentNodeb = currentNodeb.next #链表B的当前节点向后移动一个
if listc._head == None:
listc._head = newNode
else:
currentNodec.next = newNode # 首先让链表C当前节点的next指向newNode
currentNodec = newNode # 然后再将链表C当前节点对象点至newNode
if currentNodea:
currentNodec.next = currentNodea
else:
currentNodec.next = currentNodeb
方法2,采摘结点法:将较小对象链接到链表C当前节点的next,再将链表C当前节点跳至最小对象,最后最小对象(a或b)当前结点的指针向后移动一位,节省了空间,同时也改变了原链表的结构。
代码如下:
def orderMerge2(lista,listb,listc):
currentNodea = lista._head
currentNodeb = listb._head
currentNodec = listc._head
while currentNodea and currentNodeb:
if currentNodea.data < currentNodeb.data:
if listc._head == None:
listc._head = currentNodea
else:
currentNodec.next = currentNodea
currentNodec = currentNodea
currentNodea = currentNodea.next
else:
if listc._head == None:
listc._head = currentNodeb
else:
currentNodec.next = currentNodeb
currentNodec = currentNodeb
currentNodeb = currentNodeb.next
if currentNodea:
currentNodec.next = currentNodea
else:
currentNodec.next = currentNodeb
测试如下:
linkedA = LinkedList()
linkedB = LinkedList()
source1 = [1, 3, 5]
for i in source1:
linkedA.append(i)
source2 = [2, 4, 6, 8, 10, 11]
for i in source2:
linkedB.append(i)
linkedC = LinkedList()
orderMerge(linkedA, linkedB, linkedC)
linkedC.traverse()
linkedA.traverse()
linkedB.traverse()
linkedB.pop()
print(linkedB.tailpos().data)
print(linkedB.size())
注: 纯属个人笔记,不喜勿喷。