Python数据结构与算法分析(笔记与部分作业)
最近為了給寫搬磚腳本增加一些算法知識(shí),腦殘的看起來算法書。Python數(shù)據(jù)結(jié)構(gòu)與算法分析,本人英語比較差,看的是翻譯版本的。
網(wǎng)上有免費(fèi)的原版的:https://runestone.academy/runestone/books/published/pythonds/index.html
不廢話,開筆記,第一章Python基礎(chǔ),最后的反向思路就稍微卡住了我一下。
第1章,導(dǎo)論
計(jì)算機(jī)科學(xué)的研究對(duì)象是問題、解決問題的過程,以及通過該過程得到的解決方案。算法就是解決方案。
計(jì)算機(jī)科學(xué)可以定義為:研究問題及其解決方案,以及研究目前無解的問題的科學(xué)。
編程是指通過編程語言將算法編碼以使其能被計(jì)算機(jī)執(zhí)行的過程。如果沒有算法,就不會(huì)有程序。
Python支持面向?qū)ο缶幊谭妒健_@意味著Python認(rèn)為數(shù)據(jù)是問題解決過程中的關(guān)鍵點(diǎn)。在Python以及其他所有面向?qū)ο缶幊陶Z言中,類都是對(duì)數(shù)據(jù)的構(gòu)成(狀態(tài))以及
數(shù)據(jù)能做什么(行為)的描述。由于類的使用者只能看到數(shù)據(jù)項(xiàng)的狀態(tài)和行為,因此類與抽象數(shù)據(jù)類型相似的。
在面向?qū)ο缶幊谭妒街校瑪?shù)據(jù)項(xiàng)被稱為對(duì)象。一個(gè)對(duì)象就是類的一個(gè)實(shí)例。
上兩個(gè)書中的完整代碼:
def gcd(m,n):
while m%n != 0:
oldm = m
oldn = n
m = oldn
n = oldm%oldn
return n
class Fraction:
def __init__(self,top,bottom):
self.num = top
self.den = bottom
def __str__(self):
return str(self.num)+"/"+str(self.den)
def show(self):
print(self.num,"/",self.den)
def __add__(self,otherfraction):
newnum = self.num*otherfraction.den +
self.den*otherfraction.num
newden = self.den * otherfraction.den
common = gcd(newnum,newden)
return Fraction(newnum//common,newden//common)
def __eq__(self, other):
firstnum = self.num * other.den
secondnum = other.num * self.den
return firstnum == secondnum
x = Fraction(1,2)
y = Fraction(2,3)
print(x+y)
print(x == y)
class LogicGate:
def __init__(self,n):
self.name = n
self.output = None
def getLabel(self):
return self.name
def getOutput(self):
self.output = self.performGateLogic()
return self.output
class BinaryGate(LogicGate):
def __init__(self,n):
super(BinaryGate, self).__init__(n)
self.pinA = None
self.pinB = None
def getPinA(self):
if self.pinA == None:
return int(input("Enter Pin A input for gate "+self.getLabel()+"-->"))
else:
return self.pinA.getFrom().getOutput()
def getPinB(self):
if self.pinB == None:
return int(input("Enter Pin B input for gate "+self.getLabel()+"-->"))
else:
return self.pinB.getFrom().getOutput()
def setNextPin(self,source):
if self.pinA == None:
self.pinA = source
else:
if self.pinB == None:
self.pinB = source
else:
print("Cannot Connect: NO EMPTY PINS on this gate")
class AndGate(BinaryGate):
def __init__(self,n):
BinaryGate.__init__(self,n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a==1 and b==1:
return 1
else:
return 0
class OrGate(BinaryGate):
def __init__(self,n):
BinaryGate.__init__(self,n)
def performGateLogic(self):
a = self.getPinA()
b = self.getPinB()
if a ==1 or b==1:
return 1
else:
return 0
class UnaryGate(LogicGate):
def __init__(self,n):
LogicGate.__init__(self,n)
self.pin = None
def getPin(self):
if self.pin == None:
return int(input("Enter Pin input for gate "+self.getLabel()+"-->"))
else:
return self.pin.getFrom().getOutput()
def setNextPin(self,source):
if self.pin == None:
self.pin = source
else:
print("Cannot Connect: NO EMPTY PINS on this gate")
class NotGate(UnaryGate):
def __init__(self,n):
UnaryGate.__init__(self,n)
def performGateLogic(self):
if self.getPin():
return 0
else:
return 1
class Connector:
def __init__(self, fgate, tgate):
self.fromgate = fgate
self.togate = tgate
# 這里是關(guān)鍵,將整個(gè)連接器作為后面端口的輸入。每個(gè)與非門都定義了該方法。
tgate.setNextPin(self)
def getFrom(self):
return self.fromgate
def getTo(self):
return self.togate
def main():
g1 = AndGate("G1")
g2 = AndGate("G2")
g3 = OrGate("G3")
g4 = NotGate("G4")
c1 = Connector(g1,g3)
c2 = Connector(g2,g3)
c3 = Connector(g3,g4)
print(g4.getOutput())
main()
第一個(gè)相對(duì)比較好理解,第二個(gè)理解也還好,書中由于篇幅限制,沒有寫全。
但自己寫真心寫不出來,這個(gè)一種反向思考的思路,只能看懂。
小結(jié):
計(jì)算機(jī)科學(xué)是研究如何解決問題的學(xué)科。
計(jì)算機(jī)科學(xué)利用抽象這一工具來表示過程和數(shù)據(jù)。
抽象數(shù)據(jù)類型通過隱藏?cái)?shù)據(jù)的細(xì)節(jié)來使程序員能夠管理問題的復(fù)雜度。
Python是一門強(qiáng)大、易用的面向?qū)ο缶幊痰恼Z言。
列表、元祖以及字符串使Python的內(nèi)建有序集合。
字典和集是無序集合。
類使得程序員能夠?qū)崿F(xiàn)抽象數(shù)據(jù)類型。
程序員既可以重寫標(biāo)準(zhǔn)方法,也可以構(gòu)建新的方法。
類可以通過繼承層次結(jié)構(gòu)來組織。
類的構(gòu)建方法總是先調(diào)用其父類的構(gòu)建方法,然后才處理自己的數(shù)據(jù)和行為。
編程練習(xí):
1、實(shí)現(xiàn)簡單的方法getNum和getDen,它們分別返回分?jǐn)?shù)的分子與分母
2、修改Fraction類的構(gòu)造方法,傳入就約分。
3、實(shí)現(xiàn)簡單算數(shù)運(yùn)算__sub__,__mul__,__truediv__
4、實(shí)現(xiàn)__gt__,__ge__
5、修改Fraction類的構(gòu)造方法,使其檢查并確保分子與分母均為整數(shù),不是就報(bào)錯(cuò)。
后面還有10題,后續(xù)補(bǔ)上
import numbers
def gcd(m, n):
while m % n != 0:
oldm = m
oldn = n
m = oldn
n = oldm % oldn
return n
class Fraction:
def __init__(self, top, bottom):
if all([isinstance(x, numbers.Integral) for x in (top, bottom)]):
common = gcd(top, bottom)
self.num = top // common
self.den = bottom // common
else:
raise TypeError('分子分母必須為整數(shù)RR')
def __str__(self):
return str(self.num) + "/" + str(self.den)
def show(self):
print(self.num, "/", self.den)
def __add__(self, otherfraction):
newnum = self.num * otherfraction.den +
self.den * otherfraction.num
newden = self.den * otherfraction.den
# common = gcd(newnum, newden)
# return Fraction(newnum // common, newden // common)
# 返回的值可以自己先約分了。
return Fraction(newnum, newden)
# 減法
def __sub__(self, otherfraction):
newnum = self.num * otherfraction.den -
self.den * otherfraction.num
newden = self.den * otherfraction.den
return Fraction(newnum, newden)
# 乘法
def __mul__(self, otherfraction):
newnum = self.num * otherfraction.num
newden = self.den * otherfraction.den
return Fraction(newnum, newden)
# 除法
def __truediv__(self, otherfraction):
newnum = self.num * otherfraction.den
newden = self.den * otherfraction.num
return Fraction(newnum, newden)
# 返回分子
def getNum(self):
return self.num
# 返回分母
def getDen(self):
return self.den
def __eq__(self, other):
firstnum = self.num * other.den
secondnum = other.num * self.den
return firstnum == secondnum
# 原則定義了__eq__可以不定義__ne__
def __ne__(self, other):
firstnum = self.num * other.den
secondnum = other.num * self.den
return firstnum != secondnum
# 大于
def __gt__(self, other):
firstnum = self.num * other.den
secondnum = other.num * self.den
return firstnum > secondnum
# 大于等于
def __ge__(self, other):
firstnum = self.num * other.den
secondnum = other.num * self.den
return firstnum >= secondnum
if __name__ == '__main__':
x = Fraction(2, 8)
y = Fraction(1, 3)
print(y.getNum())
print(y.getDen())
print(x + y)
print(x - y)
print(x * y)
print(x / y)
print(x == y)
print(x != y)
print(x >= y)
print(x <= y)
print(x < y)
第2章 算法分析
算法分析關(guān)系的是基于所使用的計(jì)算資源比較算法。
計(jì)算資源,一個(gè)是算法在解決問題時(shí)要占用的空間和內(nèi)存,還有一個(gè)是算法執(zhí)行所需的時(shí)間進(jìn)行分析和比較。
數(shù)量級(jí)(order of magnitude) 被稱為大O記數(shù)法,記作O(f(n))。它提供了步驟數(shù)的一個(gè)有用的近似方法。
常見的大O函數(shù)
f(n) 名稱
1 常數(shù)
logn 對(duì)數(shù)
n 線性
nlogn 線性對(duì)數(shù)
n2 平方
n3 立方
2的n次 指數(shù)
異序詞檢查示例
書中用了4個(gè)方案。
def anagramSolution1(s1, s2):
'''清點(diǎn)法,時(shí)間復(fù)雜度為O(n2)'''
# 將第二個(gè)字符串轉(zhuǎn)換為列表,初始化數(shù)據(jù),嚴(yán)謹(jǐn)一點(diǎn)應(yīng)該一開始判斷字符串長度。
alist = list(s2)
pos1 = 0
stillOK = True
# 開始對(duì)比字符串s1的每個(gè)字符
while pos1 < len(s1) and stillOK:
pos2 = 0
found = False
# 對(duì)s2的列表進(jìn)行逐一取字,取到了循環(huán)停止
while pos2 < len(alist) and not found:
if s1[pos1] == alist[pos2]:
found = True
else:
pos2 += 1
# 取到了把s2的那個(gè)相同的字換成None
if found:
alist[pos2] = None
# 否則外層的循環(huán)停止,stillOK為False
else:
stillOK = False
pos1 += 1
return stillOK
def anagramSolution2(s1, s2):
'''排序法,復(fù)雜度為O(n2)或者O(nlongn)'''
alist1 = list(s1)
alist2 = list(s2)
alist1.sort()
alist2.sort()
pos = 0
matches = True
while pos < len(s1) and matches:
if alist1[pos] == alist2[pos]:
pos += 1
else:
matches = False
return matches
'''
蠻力法,我覺得復(fù)雜度為2的n次,對(duì)數(shù)O(n2)
書中沒有寫函數(shù),我自己寫一個(gè)吧.
'''
def anagramSolution3(s1, s2):
alist1 = tuple(s1)
import itertools
matches = False
# 返回一個(gè)迭代器,取值為元祖
alist2 = itertools.permutations(s2, len(s2))
# 跟著書中寫了while循環(huán),真心用不慣while循環(huán)。
while not matches:
try:
if alist1 == next(alist2):
matches = True
except StopIteration:
break
return matches
def anagramSolution4(s1, s2):
'''記數(shù)法 復(fù)雜度為O(n),但這個(gè)算法用空間換來了時(shí)間'''
c1 = [0] * 26
c2 = [0] * 26
# 對(duì)列表類的每個(gè)字母進(jìn)行累加
for i in range(len(s1)):
pos = ord(s1[i]) - ord('a')
c1[pos] += 1
for i in range(len(s1)):
pos = ord(s2[i]) - ord('a')
c2[pos] += 1
j = 0
stillOK = True
# 對(duì)兩個(gè)列表的29個(gè)元素各個(gè)元素進(jìn)行逐一比對(duì)
while j < 26 and stillOK:
if c1[j] == c2[j]:
j += 1
else:
stillOK = False
return stillOK
if __name__ == '__main__':
print(anagramSolution4('abcde', 'abcea'))
列表生成的函數(shù)測(cè)試,用了timeit模塊
from timeit import Timer
def test1():
l = []
for i in range(1000):
l = l + [i]
def test2():
l = []
for i in range(1000):
l.append(i)
def test3():
l = [i for i in range(1000)]
def test4():
l = list(range(1000))
if __name__ == '__main__':
# 生成測(cè)試對(duì)象
t1 = Timer('test1()', "from __main__ import test1")
# 進(jìn)行測(cè)試
print("concat", t1.timeit(number=1000), "milliseconds")
t2 = Timer('test2()', "from __main__ import test2")
print("append", t2.timeit(number=1000), "milliseconds")
t3 = Timer('test3()', "from __main__ import test3")
print("comprehension", t3.timeit(number=1000), "milliseconds")
t4 = Timer('test4()', "from __main__ import test4")
print("list range", t4.timeit(number=1000), "milliseconds")
對(duì)列表進(jìn)行索引查尋、索引賦值、追加(append())、彈出(pop())的大O效率都為O(1)
抄寫書中代碼對(duì)比pop(0)與pop()執(zhí)行時(shí)間
import timeit
popzero = timeit.Timer('x1.pop(0)',
'from __main__ import x1')
popend = timeit.Timer('x2.pop()',
'from __main__ import x2')
x1 = list(range(1000000))
print(popzero.timeit(number=1000))
x2 = list(range(1000000))
print(popend.timeit(number=1000))
/usr/local/bin/python3.7 "/Users/shijianzhong/study/Problem Solving with Algorithms and Data Structures using Python/chapter_2/t2_10.py" 0.31737086999999997 6.246100000001364e-05
對(duì)字典進(jìn)行取值、賦值、刪除、包含的大O效率都為O(1)
抄寫書中比較列表與字典的包含操作
import timeit
import random
for i in range(10000, 1000001, 20000):
t = timeit.Timer('random.randrange(%d) in x' % i,
'from __main__ import random, x')
x = list(range(i))
lst_time = t.timeit(number=1000)
x = {j: None for j in range(i)}
d_time = t.timeit(number=1000)
print("%d, %10.3f, %10.3f" % (i, lst_time, d_time))
小結(jié):
算法分析是一種獨(dú)立于實(shí)現(xiàn)的算法度量方法。
大O記法使得算法可以根據(jù)隨問題規(guī)模增長而其主導(dǎo)作用的部分進(jìn)行歸類。
編程練習(xí):
設(shè)計(jì)一個(gè)試驗(yàn),證明列表的索引操作為常數(shù)階。
import timeit
import random
for i in range(10000, 1000001, 20000):
index_test = timeit.Timer('index = random.randrange(%d); x[index]' % i,
'from __main__ import x, random')
x = list(range(i))
res = index_test.timeit()
print('%d, %10.3f' % (i, res))
10000, 0.951 30000, 0.792 50000, 0.930 70000, 0.906 90000, 0.894 110000, 0.884
設(shè)計(jì)一個(gè)實(shí)驗(yàn),證明字典的取值操作和賦值操作為常數(shù)階
import timeit
import random
for i in range(10000, 1000001, 20000):
# 隨機(jī)取key 賦值None
dict_test = timeit.Timer('key = random.randrange(%d); x[key]=None' % i,
'from __main__ import x, random')
# 創(chuàng)建一個(gè)字典,value為True
x = {x: True for x in range(i)}
res = dict_test.timeit()
print('%d, %10.3f' % (i, res))
10000, 0.934 30000, 0.853 50000, 0.851 70000, 0.928 90000, 0.883 110000, 0.851 130000, 0.838
設(shè)計(jì)一個(gè)實(shí)驗(yàn),針對(duì)列表和字典比較del操作的性能
import timeit
import random
for i in range(10000, 1000001, 20000):
# 只做了列表的del測(cè)試,字典的用timeit感覺不好做.
l_test = timeit.Timer('del x1[0]',
'from __main__ import x1')
# d_test = timeit.Timer('del x2[0]',
# 'x2 = {x: None for x in range(%d)}' % i)
x1 = list(range(i))
res0 = l_test.timeit(number=1000)
# x2 = {x: None for x in range(i)}
# res1 = d_test.timeit(number=1)
# x1 = {x: True for x in range(i)}
# res1 = dict_test.timeit(number=50)
# 隨機(jī)取key 賦值None
print('%d, %10.3f' % (i, res0))
水平有限,只做了del的列表測(cè)試,字典不好測(cè)試,因?yàn)橹貜?fù)測(cè)試需要在同一個(gè)字典重復(fù)刪除key,如何制作不重復(fù)的key,讓我很困難。
給定一個(gè)數(shù)字列表,其中的數(shù)字隨機(jī)排列,編寫一個(gè)線性階算法,找出第k小的元素,并解釋為何該算法的階是線性的。
def find_k_num(in_list, k):
# 選擇排序
for i in range(len(in_list), 0, -1):
for n in range(i - 1):
if in_list[n] > in_list[n + 1]:
in_list[n], in_list[n+1] = in_list[n+1], in_list[n]
return in_list[k]
if __name__ == '__main__':
x = list(reversed(list(range(10))))
print(find_k_num(x, 3))
針對(duì)前一個(gè)練習(xí),能將算法的時(shí)間復(fù)雜度優(yōu)化到O(nlogn)嗎?
用快速排序
def run(in_list):
# 退出基線
if len(in_list) < 2:
return in_list
else:
base_num = in_list[0]
small_l = [i for i in in_list[1: len(in_list)] if i <= base_num]
large_l = [i for i in in_list[1: len(in_list)] if i > base_num]
# 進(jìn)入遞歸
return run(small_l) + [base_num] + run(large_l)
def fast_k_find(in_list, k):
res = run(in_list)
# print(res)
return res[k]
if __name__ == '__main__':
x = list(reversed(list(range(10))))
# print(x)
print(fast_k_find(x, 3))
第三章 基本數(shù)據(jù)結(jié)構(gòu)
棧、隊(duì)列、雙端隊(duì)列、和列表都是有序的數(shù)據(jù)集合,其元素的順序取決與添加順序或移出順序。一旦某個(gè)元素被添加進(jìn)來,它與前后元素的相對(duì)位置保持不變。這樣的數(shù)據(jù)集合經(jīng)常被稱為線性數(shù)據(jù)結(jié)構(gòu)。
Python定義的列表使用append與pop方法能夠很好的模擬棧的運(yùn)行。
class Stack:
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)
壓棧與彈棧的時(shí)間復(fù)雜度都為O(1)
利用棧來配備括號(hào)
from t3_1 import Stack
def parChecker(symbolString):
s = Stack()
balanced = True
index = 0
# 讀取內(nèi)部的每一個(gè)括號(hào)
while index < len(symbolString) and balanced:
symbol = symbolString[index]
# 左括號(hào)壓入
if symbol == '(':
s.push(symbol)
else:
# 在處理括號(hào)的時(shí)候,不應(yīng)該出現(xiàn)棧內(nèi)為空
if s.isEmpty():
balanced = False
# 右括號(hào)彈出
else:
s.pop()
index += 1
# 只有在處理完后的棧內(nèi)為空,才能說明括號(hào)配對(duì)
if balanced and s.isEmpty:
return True
else:
return False
if __name__ == '__main__':
print(parChecker('((())))'))
普通情況:匹配符號(hào)
from t3_1 import Stack
def matches(open, close):
opens = '([{'
closers = ')]}'
return opens.index(open) == closers.index(close)
def parChecker(symbolString):
s = Stack()
balanced = True
index = 0
# 讀取內(nèi)部的每一個(gè)括號(hào)
while index < len(symbolString) and balanced:
symbol = symbolString[index]
# ([{壓入
if symbol in '([{':
s.push(symbol)
else:
# 在處理括號(hào)的時(shí)候,不應(yīng)該出現(xiàn)棧內(nèi)為空
if s.isEmpty():
balanced = False
# 右括號(hào)彈出
else:
# 對(duì)取到的符號(hào)與彈出的符號(hào)進(jìn)行對(duì)比是否一對(duì)
if not matches(s.pop(), symbol):
balanced = False
index += 1
# 只有在處理完后的棧內(nèi)為空,才能說明括號(hào)配對(duì)
if balanced and s.isEmpty:
return True
else:
return False
if __name__ == '__main__':
print(parChecker('{([()])}'))
將十進(jìn)制數(shù)轉(zhuǎn)換成二進(jìn)制數(shù)
from t3_1 import Stack
def divideBy2(decNumber):
remstack = Stack()
# 除2取余數(shù),只要商大于0就可以
while decNumber >0:
rem = decNumber % 2
remstack.push(rem)
decNumber //= 2
binString = ''
# 彈棧取出各個(gè)數(shù)字
while not remstack.isEmpty():
binString += str(remstack.pop())
return binString
if __name__ == '__main__':
print(divideBy2(65))
裝換各種進(jìn)制的算法:
from t3_1 import Stack
def baseConverter(decNumber, base):
remstack = Stack()
# 考慮到16進(jìn)制的數(shù)字,做了一串?dāng)?shù)字給16進(jìn)制使用
digits = '0123456789ABCDEF'
# 除2取余數(shù),只要商大于0就可以
while decNumber > 0:
rem = decNumber % base
remstack.push(rem)
decNumber //= base
binString = ''
# 彈棧取出各個(gè)數(shù)字
while not remstack.isEmpty():
binString += digits[remstack.pop()]
return binString
if __name__ == '__main__':
print(baseConverter(165, 16))
書中通過棧的優(yōu)勢(shì)轉(zhuǎn)化并計(jì)算中序到后序的表達(dá)式。
from t3_1 import Stack
import string
def infixToPostfix(infixexpr):
prec = {}
prec['*'] = 3
prec['/'] = 3
prec['+'] = 2
prec['-'] = 2
prec['('] = 1
opStack = Stack()
postfixList = []
# 將等式切割
tokenList = infixexpr.split()
# print(tokenList)
for token in tokenList:
# print(token)
# 如果是大寫字母的話
if token in string.ascii_uppercase:
postfixList.append(token)
# 出現(xiàn)左小括號(hào),代表對(duì)應(yīng)的一個(gè)中序表達(dá)式
elif token == '(':
opStack.push(token)
# 碰到右括號(hào),說明這個(gè)中序表達(dá)式先轉(zhuǎn)換成后序表達(dá)式
elif token == ')':
topToken = opStack.pop()
while topToken != '(':
postfixList.append(topToken)
topToken = opStack.pop()
else:
# 如果棧里面的運(yùn)算符優(yōu)先級(jí)更高或相同,先從棧里面取出來,并將它們添加到列表的末尾
# 這個(gè)循環(huán)會(huì)把里面所有倒序的符號(hào)優(yōu)先級(jí)都對(duì)比一遍。
while (not opStack.isEmpty) and
(prec[opStack.peek()] >= prec[token]):
postfixList.append(opStack.pop())
opStack.push(token)
# 將符號(hào)表中的所有符號(hào)取出
while not opStack.isEmpty():
postfixList.append(opStack.pop())
print(postfixList)
return ' '.join(postfixList)
if __name__ == '__main__':
print(infixToPostfix('A + B * C'))
用Python實(shí)現(xiàn)后續(xù)表達(dá)式的計(jì)算
from t3_1 import Stack
def postfixEval(postfixExpr):
operandStack = Stack()
tokenList = postfixExpr.split()
for token in tokenList:
# 假如是數(shù)字就壓入,數(shù)字范圍設(shè)置不好,可以用isdecimal判斷更佳
if token in '0123456789':
operandStack.push(int(token))
else:
# 當(dāng)碰到符號(hào),取出最頂層的兩個(gè)數(shù)字進(jìn)行運(yùn)算,并將結(jié)果壓入
operator2 = operandStack.pop()
operator1 = operandStack.pop()
result = toMath(token, operator1, operator2)
operandStack.push(result)
# 最終的結(jié)果就是棧內(nèi)的最后一個(gè)數(shù)字
return operandStack.pop()
def toMath(op, op1, op2):
if op == '*':
return op1 * op2
elif op == '/':
return op1 / op2
elif op == '+':
return op1 + op2
else:
return op1 - op2
if __name__ == '__main__':
print(postfixEval('1 2 + 3 *'))
書中為了解釋棧的作用,lifo后進(jìn)先出的用處,用了例子還是非常不錯(cuò)的。
隊(duì)列
FIFO,先進(jìn)先出。
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
# 插入隊(duì)列
def enqueue(self, item):
self.items.index(0, item)
# 取出隊(duì)列元素
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
這個(gè)自定義的隊(duì)列,取元素的時(shí)間復(fù)雜度為O(1),插元素為O(n)
用隊(duì)列模擬傳土豆。
from t3_9 import Queue
def hotPotato(namelist, num):
simqueue = Queue()
# 先將人壓入隊(duì)列
for name in namelist:
simqueue.enqueue(name)
# 主要隊(duì)列里面人數(shù)大于兩個(gè)
while simqueue.size() > 1:
# 開始玩游戲,將第一個(gè)出來的放入尾部
for i in range(num):
simqueue.enqueue(simqueue.dequeue())
# 一圈下來拿到土豆的滾蛋
simqueue.dequeue()
# 將最后那個(gè)人返回
return simqueue.dequeue()
if __name__ == '__main__':
print(hotPotato(list('abcdefg'), 10))
書中的用隊(duì)列實(shí)現(xiàn)的模擬打印機(jī)任務(wù),書中看的很累,代碼抄下來后,發(fā)現(xiàn)很號(hào)理解了,用for循環(huán)里面的每一個(gè)數(shù)字代表秒速,確實(shí)很好的idea。
import random
class Queue:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
# 插入隊(duì)列
def enqueue(self, item):
self.items.insert(0, item)
# 取出隊(duì)列元素
def dequeue(self):
return self.items.pop()
def size(self):
return len(self.items)
class Printer:
def __init__(self, ppm):
self.pagerate = ppm
self.currentTask = None
self.timeRemaining = 0
def tick(self):
if self.currentTask != None:
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.pages
def waitTime(self, currenttime):
return currenttime - self.timestamp
def newPrintTask():
num = random.randrange(1, 181)
if num == 180:
return True
else:
return False
def simulation(numSeconds, pagesPerMinute):
labprinter = Printer(pagesPerMinute)
printQueue = Queue()
waitingtimes = []
# 通過for循環(huán)來模擬時(shí)間運(yùn)行,每一個(gè)數(shù)字代表一秒
for currentSecond in range(numSeconds):
# 通過隨機(jī)函數(shù),平均3分種產(chǎn)生一個(gè)任務(wù)
if newPrintTask():
task = Task(currentSecond)
printQueue.enqueue(task)
# 只要在打印機(jī)空,且有任務(wù)下,打印機(jī)開始運(yùn)行
if (not labprinter.busy()) and (not printQueue.isEmpty()):
nexttask = printQueue.dequeue()
waitingtimes.append(nexttask.waitTime(currentSecond))
labprinter.startNext(nexttask)
# 消耗的打印時(shí)間,幫助打印機(jī)完成任務(wù)清零
labprinter.tick()
averageWait = sum(waitingtimes) / len(waitingtimes)
print("Average Wait %6.2f secs %3d tasks remaining" % (averageWait, printQueue.size()))
if __name__ == '__main__':
for i in range(10):
simulation(3600, 5)
雙端隊(duì)列
用Python模擬一個(gè)雙端隊(duì)列
class Deque:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
# 頭部放入數(shù)據(jù)
def addFront(self, item):
self.items.append(item)
# 尾部放入數(shù)據(jù)
def addRear(self, item):
self.items.insert(0, item)
# 頭部取出數(shù)據(jù)
def removeFront(self):
self.items.pop()
# 尾部取出數(shù)據(jù)
def removeRear(self):
self.items.pop(0)
def size(self):
return len(self.items)
用雙端隊(duì)列判斷回文
from t3_14 import Deque
def plachecker(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
if __name__ == '__main__':
print(plachecker('121'))
上面的雙端隊(duì)列還是比較好理解的,后面用Python模擬實(shí)現(xiàn)鏈表。
鏈表
為了實(shí)現(xiàn)無序列表,我們要構(gòu)建鏈表。我個(gè)人的感覺有點(diǎn)像生成器。
節(jié)點(diǎn)(Node)是構(gòu)建鏈表的基本數(shù)據(jù)結(jié)構(gòu)。每一個(gè)節(jié)點(diǎn)對(duì)象都必須持有至少兩份信息。
首先,節(jié)點(diǎn)必須包含列表元素,我們稱之為節(jié)點(diǎn)的數(shù)據(jù)變量。其次,節(jié)點(diǎn)必須保存指向下一個(gè)節(jié)點(diǎn)的引用。
首先定義節(jié)點(diǎn):
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 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
# 定義空鏈表的類
# 這個(gè)類本身不包含任何節(jié)點(diǎn)對(duì)象,而只有指向整個(gè)鏈表結(jié)構(gòu)中第一個(gè)節(jié)點(diǎn)的引用。
# 鏈表中,第一個(gè)節(jié)點(diǎn)很重要,后續(xù)的數(shù)據(jù)都要從第一個(gè)節(jié)點(diǎn)開始找
class Unordredlist:
def __init__(self):
self.head = None
# 判斷鏈表是否為空,就看頭部是不是None
def isEmpty(self):
return self.head is None
# 添加節(jié)點(diǎn)
def add(self, item):
# 創(chuàng)建節(jié)點(diǎn)
temp = Node(item)
# 設(shè)置該節(jié)點(diǎn)指向的下一個(gè)節(jié)點(diǎn),實(shí)際為None
temp.setNext(self.head)
# 設(shè)置鏈表頭部更新
self.head = temp
def length(self):
current = self.head
# 定義計(jì)數(shù)器
count = 0
# 只要頭不是None,就循環(huán)讀取,直到都到None尾巴為止
while current != None:
count += 1
current = current.getNext()
return count
def search(self, item):
current = self.head
found = False
# 當(dāng)沒找到None的并且沒有找到的情況下,一直找,找到返回True
while current != None and not found:
if current.getData() == item:
found = True
else:
current = current.getNext()
return found
# 刪除節(jié)點(diǎn),稍微復(fù)雜點(diǎn)
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()
# 第一個(gè)元素就是要被刪除的情況下
if previous is None:
self.head = current.getNext()
# 刪除前面的那個(gè)元素指向,被刪除元素的指向的下一個(gè)元素
else:
previous.setNext(current.getNext())
# 自己寫append,在鏈表的最后添加一個(gè)元素,因?yàn)闆]有保存鏈表的最后元素,還是一樣從頭開始找
def append(self, item):
current = self.head
# 如果是空鏈表
if self.head is None:
self.add(item)
else:
while True:
if current.getNext() is None:
# 設(shè)置假如節(jié)點(diǎn)的最后指向,其實(shí)是None
temp = Node(item)
temp.setNext(current.getNext())
current.setNext(temp)
break
else:
current = current.getNext()
# 格式化輸出
def __repr__(self):
l = []
current = self.head
if current is not None:
while True:
l.append(current.getData())
if current.getNext() is None:
break
current = current.getNext()
return str(l)
if __name__ == '__main__':
u_list = Unordredlist()
# u_list.add(1)
u_list.add(2)
u_list.add(3)
print(u_list)
print(u_list.length())
print(u_list.search(3))
u_list.remove(3)
print(u_list.search(3))
# u_list.append(4)
print(u_list.search(4))
u_list.remove(2)
print(u_list)
有序鏈表數(shù)據(jù)模型
需要重新定義鏈表類的一些方法
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 Unordredlist:
def __init__(self):
self.head = None
#草寫書中查尋方法,書中的while寫法,我真的很部習(xí)慣,我一般喜歡用while True,這種應(yīng)該是C語言過來的人。
def search(self, item):
current = self.head
found = False
stop = False
while current != Node and not found and not stop:
if current.getDate() == item:
found = True
else:
# 后面的數(shù)字不用查了
if current.getDate() > 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:
# 數(shù)據(jù)大于添加數(shù),需要停止了。
if current.getData() > item:
stop = True
else:
previous = current
current = current.getNext()
temp = Node(item)
# 空鏈表的情況下
if previous is None:
temp.setNext(self.head)
self.head = temp
# 并給這個(gè)設(shè)置上節(jié)點(diǎn)與下節(jié)點(diǎn)
else:
temp.setNext(current)
previous.setNext(temp)
Python列表是基于數(shù)組實(shí)現(xiàn)的。
小結(jié):
線性數(shù)據(jù)結(jié)構(gòu)以有序的方式來維護(hù)其數(shù)據(jù)。
棧是簡單的數(shù)據(jù)結(jié)構(gòu),其排序原則是LIFO,既后進(jìn)先出
棧的基本操作有push壓,pop彈,isEmpty是否為空
隊(duì)列是簡單的數(shù)據(jù)結(jié)構(gòu),其排序原理是FIFO,既先進(jìn)先出
隊(duì)列的基本操作有enqueue入隊(duì),dequeue出隊(duì),和isEmpty
表達(dá)式有3種寫法:前序、中序和后序
棧在計(jì)算和轉(zhuǎn)換表達(dá)式的算法中十分有用
棧具有反轉(zhuǎn)特性
隊(duì)列有助于構(gòu)建時(shí)序模擬。
模擬程序使用隨機(jī)數(shù)生成器模擬實(shí)際情況,并且?guī)椭覀兓卮?如果"問題。
雙端隊(duì)列是棧和隊(duì)列的組合。
雙端隊(duì)列基本操作有的操作可以參考from collections import deque
列表是元素的集合,其中每個(gè)元素都有一個(gè)相對(duì)于其他元素的位置(這個(gè)不是Python的列表)
鏈表保證邏輯順序,對(duì)實(shí)際的存儲(chǔ)順序沒有要求。
修改鏈表頭部是一種特殊情況。
課后編程作業(yè):28個(gè)題目,瘋了。
總結(jié)
以上是生活随笔為你收集整理的Python数据结构与算法分析(笔记与部分作业)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分频器的作用是什么(什么是系统时钟)
- 下一篇: CentOs7 修复 引导启动