日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

Python数据结构与算法分析(笔记与部分作业)

發(fā)布時(shí)間:2023/12/15 52 生活家
生活随笔 收集整理的這篇文章主要介紹了 Python数据结构与算法分析(笔记与部分作业) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

最近為了給寫搬磚腳本增加一些算法知識(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。