国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Python數(shù)據(jù)結(jié)構(gòu)與算法

這篇具有很好參考價(jià)值的文章主要介紹了Python數(shù)據(jù)結(jié)構(gòu)與算法。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

筆記——Python數(shù)據(jù)結(jié)構(gòu)與算法


一、棧和隊(duì)列

1.1 棧的定義

棧、隊(duì)列、雙端隊(duì)列和列表都是有序的數(shù)據(jù)集合, 其元素的順序取決于添加順序或移除順序。一旦某個(gè)元素被添加進(jìn)來(lái),它與前后元素的相對(duì)位置將保持不變。這樣的數(shù)據(jù)集合經(jīng)常被稱為線性數(shù)據(jù)結(jié)構(gòu)。

棧的添加操作和移除操作總發(fā)生在同一端。棧中的元素離底端越近,代表其在棧中的時(shí)間越長(zhǎng),因此棧的底端具有非常重要的意義。最 新添加的元素將被最先移除。這種排序原則被稱作 LIFO(last-in first-out),即后進(jìn)先出。它提供了一種基于在集合中的時(shí)間來(lái)排序的方式。最近添加的元素靠近頂端,舊元素則靠近底端。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

1.2 棧的實(shí)現(xiàn)

代碼實(shí)現(xiàn):(注:定義棧頂位于列表末尾)

class Stack:
    def __init__(self):
        self.items = []
    # 判斷棧是否為空
    def isEmpty(self):
        return self.items == []
    # 入棧,使用列表中的append方法
    def push(self,item):
        self.items.append(item)
    # 出棧,使用列表中的pop方法
    def pop(self):
        return self.items.pop()
    # 返回棧頂元素
    def peek(self):
        return self.items(len(self.items)-1)
    # 返回棧的大小
    def size(self):
        return len(self.items)

## 棧的應(yīng)用
a = Stack()
a.isEmpty()
>> True
a.push(4)
a.size()
>> 2
a.pop()
>> 4

?

1.3 應(yīng)用案例一(括號(hào)匹配)

案例描述:從左到右讀取一個(gè)括號(hào)串,然后判斷其中的括號(hào)是否匹配。匹配括號(hào)是指每一個(gè)左括號(hào)都有與之對(duì)應(yīng)的一個(gè)右括號(hào),并且括號(hào)有正確的嵌套關(guān)系。下面是正確匹配的括號(hào)串:()()();((()))

解決思路:為了解決這個(gè)問(wèn)題,需要注意到一個(gè)重要現(xiàn)象。當(dāng)從左到右處理括號(hào)時(shí),最右邊的無(wú)匹配左括號(hào) 必須與接下來(lái)遇到的第一個(gè)右括號(hào)相匹配,如下圖所示。并且,在第一個(gè)位置的左括號(hào)可能要 等到處理至最后一個(gè)位置的右括號(hào)時(shí)才能完成匹配。相匹配的右括號(hào)與左括號(hào)出現(xiàn)的順序相反。這一規(guī)律暗示著能夠運(yùn)用棧來(lái)解決括號(hào)匹配問(wèn)題。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

# 導(dǎo)入Stack類
from pythonds.basic import Stack
# 定義括號(hào)匹配函數(shù)
def parCherker(symbolString):
    # 實(shí)例化stack類
    s = Stack()
    # 設(shè)置balanced 為T(mén)rue 一個(gè)標(biāo)志位
    balanced = True
    # 初始化序號(hào)0
    index = 0
    # 當(dāng)index符合要求時(shí),遍歷symbolString
    while index < len(symbolString) and balanced:
        # 獲取括號(hào)
        symbol = symbolString[index]
        # 如果是左括號(hào)壓入堆棧
        if symbol == '(':
            s.push(symbol)
        # 若是右括號(hào)則進(jìn)一步判斷
        else:
            # 如果堆棧為空,則設(shè)置標(biāo)志位為False,跳出循環(huán)
            if s.isEmpty():
                balanced = False
            # 否則返回棧頂括號(hào),即遇到")"返回棧頂括號(hào)
            else:
                s.pop()
        index = index + 1
    # 所有括號(hào)匹配并且棧為空,返回TRUE
    if balanced and s.isEmpty():
        return True
    else:
        return False

1.3 應(yīng)用案例二(十進(jìn)制轉(zhuǎn)換)

案例描述:?利用棧將十進(jìn)制轉(zhuǎn)換為二進(jìn)制

解決思路:如下圖所示,每次將整數(shù)除以2然后取余,將所得余數(shù)壓入棧中,隨后出棧逆序輸出。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

def divideBy2(decNumber):
    # 實(shí)例化堆棧
    remstack = Stack()
    # 判斷為整數(shù)
    while decNumber > 0:
        # 除2取余
        rem = decNumber %2
        # 將其壓入堆棧
        remstack.push(rem)
        # 取整數(shù)
        decNumber = decNumber //2
    # 初始化字符串
    binString = ''
    # 將堆棧的值逆序取出
    while not remstack.isEmpty():
        binString = binString + str(remstack.pop())

    return binString

二、隊(duì)列

2.1 隊(duì)列的定義

隊(duì)列是有序集合,添加操作發(fā)生在“尾部”,移除操作則發(fā)生在“頭部”(頭刪尾插)。新元素從尾部進(jìn)入隊(duì)列,然后一直向前移動(dòng)到頭部,直到成為下一個(gè)被移除的元素。最新添加的元素必須在隊(duì)列的尾部等待,在隊(duì)列中時(shí)間最長(zhǎng)的元素則排在最前面。這種排序原則被稱作 FIFO(first-in first-out),即先進(jìn)先出,也稱先到先得。隊(duì)列的存儲(chǔ)結(jié)構(gòu)有順序隊(duì)或鏈隊(duì),主要以循環(huán)隊(duì)列更為常見(jiàn)。根據(jù)其隊(duì)列的存儲(chǔ)方式的區(qū)別其實(shí)現(xiàn)方式也有所差異。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

2.2 隊(duì)列的常見(jiàn)應(yīng)用

  • 脫機(jī)打印輸出:按申請(qǐng)的先后順序依次輸出。
  • 多用戶系統(tǒng)中,多個(gè)用戶排成隊(duì),分時(shí)地循環(huán)使用CPU和主存。
  • 按用戶的優(yōu)先級(jí)排成多個(gè)隊(duì),每個(gè)優(yōu)先級(jí)一個(gè)隊(duì)列
  • 實(shí)時(shí)控制系統(tǒng)中,信號(hào)按接收的先后順序依次處理
  • 網(wǎng)絡(luò)電文傳輸,按到達(dá)的時(shí)間先后順序依次進(jìn)行

2.3 隊(duì)列的實(shí)現(xiàn)

def 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()
    # 計(jì)算隊(duì)列的大小
    def size(self):
        return len(self.items)

## 隊(duì)列的操作
q = Queue()
q.isEmpty()
>> True
q.enqueue('dog')
q.enqueue(4)
q.enqueue(True)
q.size()
>> 3
q.deque()
>> 'dog'

2.4 應(yīng)用案例一(擊鼓傳花)

案例描述:該程序接受一個(gè)名字列表和一個(gè)用于計(jì)數(shù)的常 量 num,并且返回最后一人的名字。 我們使用隊(duì)列來(lái)模擬一個(gè)環(huán)。假設(shè)握著土豆的孩子位于隊(duì)列的頭部。在模擬傳土豆的過(guò)程中,程序?qū)⑦@個(gè)孩子的名字移出隊(duì)列,然后立刻將其插入隊(duì)列的尾部。隨后,這個(gè)孩子會(huì)一直等待,直到再次到達(dá)隊(duì)列的頭部。在出列和入列 num 次之后,此時(shí)位于隊(duì)列頭部的孩子出局,新一輪游戲開(kāi)始。如此反復(fù),直到隊(duì)列中只剩下一個(gè)名字(隊(duì)列的大小為 1)。

解決思路:隊(duì)列結(jié)構(gòu)是一個(gè)循環(huán)隊(duì)列的結(jié)構(gòu),即出列后的元素再進(jìn)列

from pythonds.basic import Queue
# 傳入兩個(gè)參數(shù),namelist和num
def hotPotato(namelist, num):
    # 定義一個(gè)空列表
    simqueue = Queue()
    # 遍歷namelist
    for name in namelist:
        # 將所有name傳入隊(duì)列中
        simqueue.enqueue(name)
    # 當(dāng)隊(duì)列中的元素大于1時(shí),進(jìn)行擊鼓傳花的游戲
    while simqueue.size() >1:
        # 進(jìn)行num次循環(huán)
        for i in range(num):
            # 每次將頭部出隊(duì)列,在尾部加入隊(duì)列
            simqueue.enqueue(simqueue.dequeue())
        # 當(dāng)num次循環(huán)結(jié)束后,返回當(dāng)前頭部的隊(duì)列
        simqueue.dequeue()
    return simqueue.dequeue()

三、鏈表

3.1 鏈表的定義

? 在了解鏈表的定義之前,需要知道什么是鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu),鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)其結(jié)點(diǎn)在存儲(chǔ)器中的位置是任意的,即邏輯上相鄰的數(shù)據(jù)元素在物理上不一定相鄰。鏈表就相當(dāng)于無(wú)序列表。

? 為了實(shí)現(xiàn)無(wú)序列表,我們要構(gòu)建鏈表。無(wú)序列表需要維持元素之間的相對(duì)位置,但是并不需要在連續(xù)的內(nèi)存空間中維護(hù)這些位置信息。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

? 需要注意的是,必須指明列表中第一個(gè)元素的位置。一旦知道第一個(gè)元素的位置,就能根據(jù) 其中的鏈接信息訪問(wèn)第二個(gè)元素,接著訪問(wèn)第三個(gè)元素,依此類推。指向鏈表第一個(gè)元素的引用被稱作頭。最后一個(gè)元素需要知道自己沒(méi)有下一個(gè)元素。

3.2 鏈表的構(gòu)建

??節(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)的引用

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

? 注意,Node 的構(gòu)造方法將 next 的初始值設(shè)為 None。由于這有時(shí)被稱為“將節(jié)點(diǎn)接地”,因此我們使用接地符號(hào)來(lái)代表指向 None 的引用。將 None 作為 next 的初始值是不錯(cuò)的做法。

## 節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)的構(gòu)建
class Node:
    # 初始化
    def __init__(self,initdata):
        self.data = initdata
        self.next = None
    # 查(每次需要設(shè)置兩個(gè)部分)
    def getData(self):
        return self.data
    def getNext(self):
        return self.next
    # 改(每次修改設(shè)置兩個(gè)部分)
    def setData(self,newdata):
        self.data = newdata
    def setNext(self,newnext):
        self.next = newnext

? 鏈表是基于節(jié)點(diǎn)集合來(lái)構(gòu)建的,每個(gè)節(jié)點(diǎn)都通過(guò)顯示的引用指向下一個(gè)節(jié)點(diǎn)。只要知道第一個(gè)節(jié)點(diǎn)的位置(包括第一個(gè)節(jié)點(diǎn)包含第一個(gè)元素),其后的每個(gè)元素都能通過(guò)下一個(gè)引用找到。因此每次查找某個(gè)元素時(shí),都需要從第一個(gè)元素開(kāi)始尋找。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

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) # 將新節(jié)點(diǎn)的末尾與頭節(jié)點(diǎn)進(jìn)行連接
        self.head = temp # 將頭節(jié)點(diǎn)設(shè)置為新加入的節(jié)點(diǎn)
    # 計(jì)算長(zhǎng)度    
    def length(self):
        current = self.head
        count = 0
        while current != None:
            count = 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 # 頭節(jié)點(diǎn)設(shè)置為current
        previous = None # previous設(shè)置為None
        found = False
        while not found:
            if current.getData() == item:
                found = True # 檢查當(dāng)前節(jié)點(diǎn)中的元素是否為要移除的元素
            else:
                previous = current 
                current = current.getNext() # 進(jìn)行蠕動(dòng)
        if previous == None: # 要?jiǎng)h除的節(jié)點(diǎn)正好是頭節(jié)點(diǎn)
            self.head = current.getNext() 
        else: # 要?jiǎng)h除的節(jié)點(diǎn)位于鏈表的中間
            previous.setNext(current.getNext())

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

? 刪除過(guò)程中必須先將previous移動(dòng)到current的位置,然后再移動(dòng)current。這一過(guò)程類似為"蠕動(dòng)",previous必須在current向前移動(dòng)之前指向當(dāng)前位置。

3.3 有序列表的構(gòu)建

? 在有序列表中,元素的相對(duì)位置取決于它們的基本特征。它們通常以升序或者降序排列,并 且我們假設(shè)元素之間能進(jìn)行有意義的比較。

class OrderedList:
    def __init__(self):
        self.head = None
    # 與無(wú)序列表相同
    def isEmpty(self):
        return self.head == None
    def length(self):
        current = self.head
        count = 0
        while current != None:
            count = count +1
            current = current.getNext()
        return count

    # 查
    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 stop:
            if current.getData() > item:
                stop = True
            else:
                previous = current
                current = current.getNext()
        temp = Node(item)
        if previous == None:
            temp.setNext(self.head)
            self.head = temp
        else:
            temp.setNext(current)
            previous.setNext(temp)

通過(guò)增加新的布爾型變量 stop,并將其初始化 為 False,可以將上述條件輕松整合到代碼中。當(dāng) stop 是 False 時(shí),我們可以繼續(xù)搜索鏈表。如果遇到其值大于目標(biāo)元素的節(jié)點(diǎn),則將 stop 設(shè)為 True.從而不需要遍歷完全部的鏈表。

四、搜索與排序

4.1 順序搜索

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

? 從列表中的第一個(gè)元素開(kāi)始,沿著默認(rèn)的順序逐個(gè)查看,直到找到目標(biāo)元素或者查完列表。如果查看列表后仍然沒(méi)有找到目標(biāo)元素,則說(shuō)明目標(biāo)元素不在列表中。

def sequentialSearch(alist,item):
    # 起始指針
    pos = 0
    # 設(shè)置一個(gè)found標(biāo)志位
    found = False
    # 如果指針為超過(guò)列表長(zhǎng)度并且還沒(méi)有找到元素
    while pos < len(alist) and not found :
        if alist[pos] == item:
            found = True
        else:
            pos = pos +1

    return found

時(shí)間復(fù)雜度為O(n)

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

4.2 二分搜索

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

? 二分搜索的首要條件是列表必須是有順序的,即從小到大排列或者從大到小排列。二分搜索相較于順序搜索,它的起始元素不是第一個(gè)元素,而是中間元素。其搜索邏輯如下:如果這個(gè)元素就是目標(biāo)元素,那么就立即停止搜索;如果不是,則利用列表的有序性,排除一半元素,再在剩余一半的元素繼續(xù)應(yīng)用二分搜索,直至找到目標(biāo)元素。

def binarySearch(alist,item):
    # 頭指針
    first = 0
    # 尾指針
    last = len(alist) - 1
    # 設(shè)置found標(biāo)志位
    found = False
    # 循環(huán)操作,條件:如果還有搜索空間,并且沒(méi)有找到
    while first <= last and not found:
        # 尋找中間二分點(diǎn)
        midpoint = (first + last)//2
        # 二分點(diǎn)對(duì)左右兩邊進(jìn)行判斷
        if alist(midpoint) == item:
            found = True
        else:
            # 如果目標(biāo)元素比中間元素小,修改尾指針至中間元素的前一個(gè)元素
            if item < alist[midpoint]:
                last = midpoint -1
            # 如果目標(biāo)元素比中間元素大,修改頭指針至中間元素的后一個(gè)元素
            else:
                first = midpoint +1
    return found

4.3 散列

4.3.1 散列表的定義

? 散列是元素的集合,其中一個(gè)元素以一種便于查找的方式存儲(chǔ)。散列表中的每個(gè)位置通常被稱為槽,其中可以存儲(chǔ)一個(gè)元素。散列表的基本思想就是記錄存儲(chǔ)位置與存儲(chǔ)關(guān)鍵字之間的對(duì)應(yīng)關(guān)系,這個(gè)對(duì)應(yīng)關(guān)系我們稱之為哈希函數(shù)。

? 通過(guò)構(gòu)建散列,我們可以得到一個(gè)時(shí)間復(fù)雜度為O(1)的數(shù)據(jù)結(jié)構(gòu)。槽用一個(gè)從0開(kāi)始的整數(shù)標(biāo)記,例如0號(hào)槽,1號(hào)槽等。初始情形下,散列表中沒(méi)有元素,每個(gè)槽都是空的??梢杂昧斜韥?lái)實(shí)現(xiàn)散列表,并將每個(gè) 元素都初始化為 Python 中的特殊值 None。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

4.3.2 散列函數(shù)的定義

? 散列函數(shù)將散列表中的元素與其所屬位置對(duì)應(yīng)起來(lái)。對(duì)散列表中的任一元素,散列函數(shù)返回 一個(gè)介于 0和m – 1之間的整數(shù)。這個(gè)散列函數(shù)可以通過(guò)自定義編碼的方式,將函數(shù)值和槽值對(duì)應(yīng)起來(lái)。常見(jiàn)的散列函數(shù)是取余函數(shù),具體算法如下:將整數(shù)元素除以表的大小,將得到的余數(shù)作為散列值。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

4.3.3 散列表的沖突

? 顯然,當(dāng)每個(gè)元素的散列值是不同的情況下,這個(gè)散列表是完美的,但是如果散列值是相同的情況下,例如77和44,在散列表大小為11的情況下,散列值均為0,此時(shí)散列函數(shù)會(huì)將兩個(gè)元素都放入同一個(gè)槽中,這種情況稱作沖突。處理沖突是散列計(jì)算的重點(diǎn),因?yàn)槿绻麡?gòu)建想要一個(gè)完美的散列表的話,任何一種散列函數(shù)所耗費(fèi)的內(nèi)存,在預(yù)估元素很多的情況下是相當(dāng)大的。所以,我們需要解決好沖突問(wèn)題,這樣可以進(jìn)一步降低所需內(nèi)存。這里主要有兩種辦法。

? 一種方法是在散列表中找到另一個(gè)空槽,用于放置引起沖突的元素。簡(jiǎn)單的做法是從起初的 散列值開(kāi)始,順序遍歷散列表,直到找到一個(gè)空槽。注意,為了遍歷散列表,可能需要往回檢查第一個(gè)槽。這個(gè)過(guò)程被稱為開(kāi)放定址法,它嘗試在散列表中尋找下一個(gè)空槽或地址。由于是逐個(gè)訪問(wèn)槽,因此這個(gè)做法被稱作線性探測(cè)。

? 另一種處理沖突的方法是讓每個(gè)槽有一個(gè)指向元素集合(或鏈表)的引用。鏈接法允許散列 表中的同一個(gè)位置上存在多個(gè)元素。發(fā)生沖突時(shí),元素仍然被插入其散列值對(duì)應(yīng)的槽中。不過(guò), 隨著同一個(gè)位置上的元素越來(lái)越多,搜索變得越來(lái)越困難。圖 5-12 展示了采用鏈接法解決沖突后的結(jié)果。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

?

4.3.4 利用散列表來(lái)實(shí)現(xiàn)字典

? 字典是最有用的python集合之一,字典能根據(jù)給定的一個(gè)key,快速找到其關(guān)聯(lián)的value,從而達(dá)到其高效搜索的實(shí)現(xiàn)方案。字典是存儲(chǔ)鍵–值對(duì)的數(shù)據(jù)類型。鍵用來(lái)查 找關(guān)聯(lián)的值,這個(gè)概念常常被稱作映射。映射抽象數(shù)據(jù)類型定義如下。它是將鍵和值關(guān)聯(lián)起來(lái)的無(wú)序集合,其中的鍵是不重復(fù)的,鍵和值之間是一一對(duì)應(yīng)的關(guān)系。

# 初始長(zhǎng)度為11的散列表的創(chuàng)建
class HashTable:
    def __init__(self):
        # 設(shè)置散列表的大小
        self.size = 11 
        # 設(shè)置兩個(gè)列表,一個(gè)存放鍵,一個(gè)存放值
        self.slots = [None]*self.size
        self.data = [None]*self.size 
    # 增,參數(shù)鍵值對(duì)
    def put(self,key,data):
        # 定義hashfunction為簡(jiǎn)單的取余函數(shù),hashfunction返回的是散列值
        hashvalue = self.hashfunction(key,len(self.slots))
        # 如果散列表當(dāng)前位置為空,則直接添加
        if self.slots[hashvalue] == None:
            # slots存放鍵,在列表的散列值處存放鍵
            self.slots[hashvalue] = key
            # data存放值,在列表的散列值處存放值,這樣實(shí)現(xiàn)了鍵和值的一一對(duì)應(yīng)(通過(guò)散列值)
            self.data[hashvalue] = data
        # 如果散列表當(dāng)前位置不為空,有沖突
        else:
            # 如果鍵相同,值不同,則直接將原來(lái)的值修改,參考字典修改值的操作
            if self.slots[hashvalue] == key:
                self.data[hashvalue] = data
            # 如果鍵不同,但是散列值相同
            else:
                # 再散列操作,定義一個(gè)nextslot變量
                nextslot = self.rehash(hashvalue,len(self.slots))
                # 如果再散列后的槽中的值不是空,并且鍵不相同,繼續(xù)再散列操作
                while self.slots[nextslot] != None and self.slots[nextslot] != key:
                    nextslot = self.rehash(nextslot,len(self.slots))
                # 設(shè)置鍵值對(duì)
                if self.slots[nextslot] == None:
                    self.slots[nextslot] = key
                    self.data[nextslot] = data 
                else:
                    self.data[nextslot] = data


    # 取余哈希函數(shù)
    def hashfunction(self,key,size):
        return key%size 
    # 再哈希
    def rehash(self,oldhash,size):
        return (ordhash+1)%size

    # 查
    def get(self,key):
        # 獲取散列值
        startslot = self.hashfunction(key,len(self.slots))
        # 設(shè)置三個(gè)標(biāo)志位
        data = None
        stop = False
        found = False
        position = startslot
        # 如果當(dāng)前當(dāng)前散列值的槽不為空且沒(méi)有找到也沒(méi)有停止(因?yàn)橛袥_突的存在,所以有可能再散列)
        while self.slots[position] != None and not found and not stop:
            # 如果找到該鍵則跳出循環(huán),返回data值
            if self.slots[position] == key:
                found = True
                data = self.data[position]
            # 如果鍵發(fā)生沖突,存在再散列操作
            else:
                # 再散列
                position = self.rehash(position,len(self.slots))
                if position == startslot:
                    stop = True
        return data

    def __getitem__(self,key):
        return self.get(key)

    def __setitem__(self,key,data):
        return self.put(key,data)

注:散列表的搜索效率是O(1),它是以一種空間換時(shí)間的數(shù)據(jù)結(jié)構(gòu)。

4.4 冒泡排序

? 冒泡排序多次遍歷列表。它比較相鄰的元素,將不合順序的交換。每一輪遍歷都將下一個(gè)最 大值放到正確的位置上。本質(zhì)上,每個(gè)元素通過(guò)“冒泡”找到自己所屬的位置。

?

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

# 冒泡排序
def bubbleSort(alist):
    # 左閉右開(kāi),逆序遞減
    for passnum in range(len(alist)-1,0,-1):
        # 第一輪比較n-1對(duì),最大的元素一直往前挪,直到遍歷過(guò)程結(jié)束,逐輪遞減
        for i in range(passnum):
            # 如果前一個(gè)元素比后一個(gè)元素大,則要進(jìn)行轉(zhuǎn)移數(shù)值的操作
            if alist[i] > alist[i+1]:
                temp = alist[i]
                alist[i] = alist[i+1]
                alist[i+1] = temp

?

4.5 選擇排序

? 選擇排序在冒泡排序的基礎(chǔ)上做了改進(jìn)遍歷列表時(shí)只進(jìn)行一次交換,選擇排序在每次遍歷時(shí)尋找最大值,并在遍歷完之后將它放到正確位置上。和冒泡排序一樣,每次遍歷完后,當(dāng)前最大的元素就位。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

# 選擇排序
def selectionSort(alist):
    # 類似冒泡排序
    for fillslot in range(len(alist)-1,0,-1):
        # 初始化最大值標(biāo)志位
        positionofMax = 0
        # 左閉右開(kāi),遍歷未排序的整個(gè)序列,尋找最大值
        for location in range(1,fillslot+1):
            if alist[location] > alist[positionofMax]:
                positionofMax = location
        # 將最大值就位
        temp = alist[fillslot]
        alist[fillslot] = alist[positionofMax]
        alist[positionofMax] = temp
        # 時(shí)間復(fù)雜度與冒泡排序一致,均為o(n^2)

4.6 插入排序

插入排序的時(shí)間復(fù)雜度也是 2 On ,但原理稍有不同。它在列表較低的一端維護(hù)一個(gè)有序的子列表,并逐個(gè)將每個(gè)新元素“插入”這個(gè)子列表。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

# 插入排序
def insertionSort(alist):
    for index in range(1,len(alist)):
        # 獲取當(dāng)前指針和當(dāng)前的元素值
        currentvalue = alist[index]
        position = index
        # 如果當(dāng)前指針大于0并且前一個(gè)元素值大于當(dāng)前的元素值,那么需要調(diào)換操作
        while position > 0 and alist[position-1] > currentvalue:
            # 將前一個(gè)元素值與當(dāng)前元素值交換
            alist[position] = alist[position-1]
            # 并且將指針做減一操作,嘗試一直遍歷到列表頭
            position = position-1
        alist[position] = currentvalue

4.7 歸并排序

? 歸并排序是遞歸算法,每次將一個(gè)列表一分為二。如果列表為空或只有一個(gè)元素,那么從定義上來(lái)說(shuō)它就是有序的(基本情況)。如果列表不止一個(gè)元素,就將列表一分為二,并對(duì)兩部分都遞歸調(diào)用歸并。歸并是指將兩個(gè)較小的有序列表歸并為一 個(gè)有序列表的過(guò)程。

?

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

def merge(alist,low,mid,high):
    # 第一個(gè)有序列表的第一個(gè)元素
    i = low
    # 第二個(gè)有序列表的第一個(gè)元素
    j = mid+1
    ltmp = []
    # 只要左右兩個(gè)列表都有數(shù)
    while i<=mid and j<=high:
        # 比較兩個(gè)列表當(dāng)前指針的數(shù)的大小
        if alist[i] < alist[j]:
            # 先放入較小的那個(gè)
            ltmp.append(alist[i])
            # i+1
            i+=1
        else:
            ltmp.append(alist[j])
            j+=1
    # while執(zhí)行完,肯定有一部分是已經(jīng)遍歷完
    # 如果第一個(gè)有序列表有數(shù)
    while i<=mid:
        ltmp.append(alist[i])
        i +=1
    # 如果第二個(gè)有序列表有數(shù)
    while j<=high:
        ltmp.append(alist[j])
        j+=1
    # 將ltmp傳入alist
    alist[low:high+1]=ltmp
# 歸并操作,將列表越分越小,直至分為一個(gè)元素
def merge_sort(alist,low,high):
    if low < high: # 至少有兩個(gè)元素,遞歸;終止條件:只有一個(gè)元素
        # 設(shè)置中間元素
        mid = (low+high)//2
        # 歸并左邊
        merge_sort(alist,low,mid)
        # 歸并右邊
        merge_sort(alist,mid+1,high)
        # 排序
        merge(alist,low,mid,high)

4.8快速排序

? 快速排序也是交換排序的一種,其原理是:將未排序的元素根據(jù)一個(gè)作為基準(zhǔn)的“主元”分為兩個(gè)子序列,其中一個(gè)子序列的記錄均大于主元,而另一個(gè)子序列均小于主元,然后遞歸地對(duì)這兩個(gè)子序列用類似的方法進(jìn)行排序。本質(zhì)上,快速排序使用分治法,將問(wèn)題規(guī)模減小,然后再分別進(jìn)行處理。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

def quickSort(alist):
    quickSoertHelper(alist,0,len(alist)-1)

def quickSoertHelper(alist,first,last):
    if first < last: # 至少兩個(gè)元素
        # 遞歸
        mid = partition(alist,first,last)
        quickSort(alist,first,mid-1)
        quickSort(alist,mid+1,last)


def partition(alist,left,right):
    # tmp作為主元
    tmp = alist[left]
    # 如果之間還有兩個(gè)元素
    while left < right:
         # 從右邊找比temp小的數(shù)
         while left < right and alist[right] >= tmp: 
               # 指針往左走一步
               right -= 1
         # 如果右邊的值比主元小,則要換位置
         alist[left] = alist[right]
         # 在左邊找比temp大的數(shù)
         while left < right and alist[left] <= tmp:
               left +=1
         alist[right]=alist[left]
    alist[left] = tmp
    # 返回此時(shí)的主元位置
    return left

五、樹(shù)

5.1 數(shù)的定義

? 數(shù)據(jù)的邏輯結(jié)構(gòu)分為線性結(jié)構(gòu)和非線性結(jié)構(gòu),上面我們提到的像棧、隊(duì)列、線性表等均為線性結(jié)構(gòu);而這個(gè)樹(shù)和圖屬于非線性結(jié)構(gòu)。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

樹(shù)是n個(gè)節(jié)點(diǎn)的有限集。若n =0,稱為空樹(shù);若n>0,則它滿足如下兩個(gè)條件:1、有且僅有一個(gè)特定的根稱為根節(jié)點(diǎn);2、其余結(jié)點(diǎn)可分為m個(gè)互不相交的有限集$T1,T2,T3...$,其中每個(gè)集合本身又是一棵樹(shù),并稱為根的子樹(shù)。因此可以看出來(lái)樹(shù)的定義本身就是一個(gè)遞歸的過(guò)程,

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

5.2 二叉樹(shù)的定義

二叉樹(shù)不是樹(shù)的特殊情況,而是兩個(gè)概念。

二叉樹(shù)結(jié)點(diǎn)的子樹(shù)要區(qū)分左子樹(shù)和右子樹(shù),即使只有一棵子樹(shù)也要區(qū)分其是左子樹(shù)還是右子樹(shù)。

  • 滿二叉樹(shù)
  • 二叉樹(shù)分類
  • 二叉樹(shù)的遍歷

5.3 使用列表進(jìn)行樹(shù)的創(chuàng)建

在“列表之列表”的樹(shù) 中,我們將根節(jié)點(diǎn)的值作為列表的第一個(gè)元素;第二個(gè)元素是代表左子樹(shù)的列表;第三個(gè)元素是代表右子樹(shù)的列表。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

# 列表的列表進(jìn)行樹(shù)的定義
def BinaryTree(r):
    return [r,[],[]]
# 添加左子樹(shù)
def insertLeft(root,newBranch):
    t = root.pop(1)
    if len(t) > 1:
        root.insert(1,[newBranch,t,[]])
    else:
        root.insert(1,[newBranch,[],[]])
    return root
# 添加右子樹(shù)
def insertRight(root,newBranch):
    t = root.pop(2)
    if len(t) > 1:
        root.insert(2,[newBranch,[],t])
    else:
        root.insert(2,[newBranch,[],[]])
    return root
# 樹(shù)的訪問(wèn)函數(shù)
def getRootVal(root):
    return root[0]

def setRootVal(root,newVal):
    root[0] = newVal

def getLeftChild(root):
    return root[1]

def getRightChild(root):
    return root[2]

5.4 二叉樹(shù)的建立

? 二叉樹(shù)的鏈?zhǔn)酱鎯?chǔ):將二叉樹(shù)的節(jié)點(diǎn)定義為一個(gè)對(duì)象,節(jié)點(diǎn)之間采用類似鏈表的鏈接方式來(lái)鏈接

class BiTreeNode:
    def __init__(self,data):
        # 設(shè)置根節(jié)點(diǎn)
        self.key = data
        # 設(shè)置左右子樹(shù)
        self.leftchild = None
        self.rightchild = None

     # 添加左子樹(shù)
     def insertLeft(self,newNode):
        if self.LeftChild == None:
            self.LeftChild = BinaryTree(newNode)

        else:
            t = BinaryTree(newNode)
            t.left = self.LeftChild
            self.LeftChild = t
    # 添加右子樹(shù)
    def insertRight(self,newNode):
        if self.rightchild == None:
            self.rightchild = BinaryTree(newNode)

        else:
            t = BinaryTree(newNode)
            t.right = self.rightchild
            self.rightchild = t

# 實(shí)例一、構(gòu)建二叉樹(shù),利用初始化類函數(shù)
a = BiTreeNode('A')
b = BiTreeNode('B')
c = BiTreeNode('C')
d = BiTreeNode('D')
e = BiTreeNode('E')
f = BiTreeNode('F')
g = BiTreeNode('G')

e.leftchild = a
e.rightchild = g
a.rightchild = c
c.leftchild = b
c.rightchild = d
g.rightchild = f
# 設(shè)置說(shuō)明根節(jié)點(diǎn)為e
root = e
# 打印根節(jié)點(diǎn)的左子樹(shù)的右子樹(shù)的值
print(root.leftchild.rightchild.key)
# 返回結(jié)果
>> C

上述實(shí)例一構(gòu)建的二叉樹(shù)模型如下圖所示

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

5.5 二叉樹(shù)的遍歷

? 二叉樹(shù)的遍歷是指從根節(jié)點(diǎn)出發(fā),按照某種次序依次訪問(wèn)二叉樹(shù)中所有節(jié)點(diǎn),使得每個(gè)節(jié)點(diǎn)被訪問(wèn)一次且被訪問(wèn)一次。遍歷方式分為以下三種:

1、前序遍歷(根左右)

在前序遍歷中,先訪問(wèn)根節(jié)點(diǎn),然后遞歸地前序遍歷左子樹(shù),最后遞歸地前序遍歷右子樹(shù)。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

2、 中序遍歷(左根右)

先遞歸地中序遍歷左子樹(shù),然后訪問(wèn)根節(jié)點(diǎn),最后遞歸地中序遍歷右子樹(shù)。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

注:可以理解為將樹(shù)從上往下拍扁,然后從左往右進(jìn)行依次遍歷

3、 后序遍歷(左右根)

先遞歸地后序遍歷右子樹(shù),然后遞歸地后序遍歷左子樹(shù),最后訪問(wèn)根節(jié)點(diǎn)。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

# 前序遍歷
def pre_order(root):
    if root:
        print(root.key,end=',')
        pre_order(root.leftchild)
        pre_order(root.rightchild)
# 中序遍歷
def in_order(root):
    if root:
        in_order(root.leftchild)
        print(root.key,end=',')
        in_order(root.rightchild)
# 后序遍歷
def post_order(root):
    if root:
        post_order(root.leftchild)
        post_order(root.rightchild)
        print(root.key,end='')
## 遍歷充分利用了遞歸的思想

5.6 二叉搜索樹(shù)

二叉搜索樹(shù)具有如下特性:

1、若它的左子樹(shù)不空,則左子樹(shù)上所有結(jié)點(diǎn)的值均小于根節(jié)點(diǎn)的值;

2、若它的右子樹(shù)不空,則右子樹(shù)上所有結(jié)點(diǎn)的值均大于根節(jié)點(diǎn)的值;

3、它的左右子樹(shù)也都是二叉排序樹(shù)。

二叉搜索樹(shù)的操作:查詢、插入、刪除

5.6.1 查找

查找的偽代碼如下所示:

若二叉搜索樹(shù)為空,則查找不成功;
否則:
1、若給定值等于根節(jié)點(diǎn),則查找成功;
2、若給定值小于根節(jié)點(diǎn),則繼續(xù)在左子樹(shù)上查找;
3、若給定值大于根節(jié)點(diǎn),則繼續(xù)在右子樹(shù)上進(jìn)行查找

5.6.2 刪除

刪除的偽代碼如下所示:

1、刪除的結(jié)點(diǎn)是葉子節(jié)點(diǎn)
直接刪除,然后修改其父節(jié)點(diǎn)的指針,置空即可。

2、 刪除的結(jié)點(diǎn)有左子樹(shù)和右子樹(shù)
將其右子樹(shù)的最小節(jié)點(diǎn)刪除,并替換當(dāng)前節(jié)點(diǎn)。

3、 刪除的結(jié)點(diǎn)只有左子樹(shù)或只有右子樹(shù)
將此節(jié)點(diǎn)的父親與孩子連接,然后刪除該節(jié)點(diǎn)。

5.6.3 代碼實(shí)現(xiàn)

class BinarySearchTree:

    def __init__(self,li=None):
        self.root = None
        self.size = 0
        self.parent = None
        if li:
            for val in li:
                self.insert_no_rec(val)
# 二叉搜索樹(shù)的插入操作(使用遞歸)
    def insert(self,node,val):
        # 如果節(jié)點(diǎn)為可空
        if not node:
            node = BiTreeNode(val)
        # 如果插入值小于節(jié)點(diǎn),遍歷左子樹(shù)
        elif val < node.key:
            node.leftchild = self.insert(node.leftchild,val)
            node.leftchild.parent = node
        # 如果插入值大于節(jié)點(diǎn),遍歷右子樹(shù)
        elif val > node.key:
            node.rightchild = self.insert(node.rightchild,val)
            node.rightchild.parent = node
        else:
            return node
# 插入操作(不使用遞歸)
    def insert_no_rec(self,key):
        p = self.root
        # 空樹(shù)
        if not p:
            self.root = BiTreeNode(key)
            return
        while True:
            # 如果插入節(jié)點(diǎn)比根節(jié)點(diǎn)小
            if key < p.key:
                # 如果左子樹(shù)存在
                if p.leftchild:
                    # 令p指針指向左子樹(shù)
                    p = p.leftchild
                # 如果左子樹(shù)不存在
                else:
                    # p的左子樹(shù)為該插入節(jié)點(diǎn)
                    p.leftchild =  BiTreeNode(key)
                    # 與父節(jié)點(diǎn)做鏈接
                    p.leftchild.parent = p
                    return

            elif key > p.key:
                if p.rightchild:
                    p = p.rightchild
                else:
                    p.rightchild = BiTreeNode(key)
                    p.rightchild.parent = p
                    return
            # 如果插入節(jié)點(diǎn)和根節(jié)點(diǎn)相同,則返回
            else:
                return

# 查詢操作
    def query(self,node,val):
        if not node:
            return None
        if node.data < val:
            return self.query(node.rchild,val)
        elif node.data > val:
            return self.query(node.leftchild,val)
        else:
            return node
    # 查詢操作非遞歸的方法
    def query_no_rec(self,val):
        p = self.root
        while p:
            if p.data < val:
                p = p.rightchild
            elif p.data > val:
                p = p.leftchild
            else:
                return p
        return None

# 刪除操作
    def delete(self,key):
        p = None
        q = self.root
        # 當(dāng)根節(jié)點(diǎn)不為空時(shí),且添加的值不為根節(jié)點(diǎn)值時(shí)
        while q and q.key != key:
            # 將當(dāng)前根節(jié)點(diǎn)指針賦予p
            p = q
            # 如果刪除的值小于當(dāng)前根節(jié)點(diǎn),則往左子樹(shù)搜索
            if key < q.key:
                # 將q指針指向q的左子樹(shù)
                q = q.leftchild
            # 反之,往右子樹(shù)搜索
            else:
                q = q.rightchild
            if not q:
                return
        # 上面已經(jīng)找到了要?jiǎng)h除的節(jié)點(diǎn),用q引用。而p則是q的父節(jié)點(diǎn)或者None。情況一、刪除的節(jié)點(diǎn)沒(méi)有左子樹(shù)
        if not q.leftchild:
            # 如果刪除節(jié)點(diǎn)的父節(jié)點(diǎn)是空,則要?jiǎng)h除的是根節(jié)點(diǎn),那么直接將其右節(jié)點(diǎn)設(shè)置為根節(jié)點(diǎn)
            if p is None:
                self.root = q.rightchild
            # 如果要?jiǎng)h除的節(jié)點(diǎn)是p的左子樹(shù),那么直接將p的左子樹(shù)指向q的右子樹(shù)
            elif q is p.leftchild:
                p.leftchild = q.rightchild
            # 如果要?jiǎng)h除的節(jié)點(diǎn)是p的右子樹(shù),那么直接將p的右子樹(shù)指向q的右子樹(shù)
            else:
                p.rightchild = q.rightchild
            return
        # 查找節(jié)點(diǎn)q的左子樹(shù)的最右節(jié)點(diǎn),將q的右子樹(shù)鏈接為該節(jié)點(diǎn)的右子樹(shù)
        r = q.leftchild
        # 查找到最小的右子樹(shù)
        while r.rightchild:
            r  = r.rightchild
        r.rightchild = q.rightchild
        if p is None:
            self.root = q.leftchild
        elif p.leftchild is q:
            p.leftchild = q.leftchild
        else:
            p.rightchild = q.leftchild
# 三種遍歷
    def pre_order(self,root):
        if root:
            print(root.key, end=',')
            pre_order(root.leftchild)
            pre_order(root.rightchild)

    def in_order(self,root):
        if root:
            in_order(root.leftchild)
            print(root.key, end=',')
            in_order(root.rightchild)

    def post_order(self,root):
        if root:
            post_order(root.leftchild)
            post_order(root.rightchild)
            print(root.key, end='')

六、圖及其算法

6.1 圖的定義

對(duì)于上面的樹(shù)進(jìn)行比較,圖是更通用的結(jié)構(gòu),事實(shí)上,樹(shù)可以看做一種特殊的圖。圖有多個(gè)前驅(qū)和多個(gè)后繼。圖可以理解為頂點(diǎn)和邊的集合。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

6.2 鄰接矩陣

要實(shí)現(xiàn)圖,最簡(jiǎn)單的方式就是使用二維矩陣。在矩陣實(shí)現(xiàn)中,每一行和每一列都表示圖中的 一個(gè)頂點(diǎn)。第 v行和第 w列交叉的格子中的值表示從頂點(diǎn) v到頂點(diǎn) w的邊的權(quán)重。如果兩個(gè)頂點(diǎn)被一條邊連接起來(lái),就稱它們是相鄰的。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

第v行和第w列交叉的格子中的值表示從頂點(diǎn)v到頂點(diǎn)w的邊的權(quán)重,鄰接矩陣總共大致分為以下三類。

1、無(wú)向圖

對(duì)于n個(gè)頂點(diǎn)的無(wú)向圖,其鄰接矩陣是一個(gè)n×n的方陣。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

2、有向圖

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

3、加權(quán)圖

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

6.3 圖的實(shí)現(xiàn)

class Vertext(): # 包含了頂點(diǎn)信息,以及頂點(diǎn)連接邊

    def __init__(self,key):#key表示是添加的頂點(diǎn)
        self.id = key
        self.connectedTo = {} # 初始化臨接列表

    def addNeighbor(self,nbr,weight=0):# 這個(gè)是賦值權(quán)重的函數(shù)
        self.connectedTo[nbr] = weight

    def __str__(self):
        return str(self.id)+ ' connectedTo: '+str([x.id for x in self.connectedTo])

    def getConnections(self): #得到這個(gè)頂點(diǎn)所連接的其他的所有的頂點(diǎn) (keys類型是class)
        return self.connectedTo.keys()

    def getId(self): # 返回自己的key
        return self.id

    def getWeight(self,nbr):#返回所連接ner頂點(diǎn)的權(quán)重是多少
        return self.connectedTo[nbr]

'''
Graph包含了所有的頂點(diǎn)
包含了一個(gè)主表(臨接列表)
'''
class Graph():# 圖 => 由頂點(diǎn)所構(gòu)成的圖

    '''
    存儲(chǔ)圖的方式是用鄰接表實(shí)現(xiàn)的.

    數(shù)據(jù)結(jié)構(gòu): {
                key:Vertext(){
                    self.id = key
                    self.connectedTo{
                        相鄰節(jié)點(diǎn)類實(shí)例 : 權(quán)重
                        ..
                        ..
                    }
                }
                ..
                ..
        }


    '''
    def __init__(self):
        self.vertList = {} # 鄰接列表
        self.numVertices = 0 # 頂點(diǎn)個(gè)數(shù)初始化

    def addVertex(self,key):# 添加頂點(diǎn)
        self.numVertices = self.numVertices + 1 # 頂點(diǎn)個(gè)數(shù)累加
        newVertex = Vertext(key) # 創(chuàng)建一個(gè)頂點(diǎn)的臨接矩陣
        self.vertList[key] = newVertex
        return newVertex

    def getVertex(self,n):# 通過(guò)key查找定點(diǎn)
        if n in self.vertList:
            return self.vertList[n]
        else:
            return None

    def __contains__(self,n):# transition:包含 => 返回所查詢頂點(diǎn)是否存在于圖中
        #print( 6 in g)
        return n in self.vertList

    def addEdge(self,f,t,cost=0): # 添加一條邊.
        if f not in self.vertList: # 如果沒(méi)有邊,就創(chuàng)建一條邊
            nv = self.addVertex(f)
        if t not in self.vertList:# 如果沒(méi)有邊,就創(chuàng)建一條邊
            nv = self.addVertex(t)

        if cost == 0:# cost == 0 代表是沒(méi)有傳入?yún)?shù),而使用的默認(rèn)參數(shù)0,即是是無(wú)向圖
            self.vertList[f].addNeighbor(self.vertList[t],cost) # cost是權(quán)重.無(wú)向圖為0
            self.vertList[t].addNeighbor(self.vertList[f],cost)
        else:#
            self.vertList[f].addNeighbor(self.vertList[t],cost) # cost是權(quán)重

    def getVertices(self):# 返回圖中所有的定點(diǎn)
        return self.vertList.keys()

    def __iter__(self): #return => 把頂點(diǎn)一個(gè)一個(gè)的迭代取出.
        return iter(self.vertList.values())


#
# -------------------------------------------------
# 以下是測(cè)試數(shù)據(jù).可刪除
# -------------------------------------------------
#
g = Graph()

# for i in range(6):
#     g.addVertex(i)
# print(g.vertList)

'''
# a = g.vertList[0]
# print(a.connectedTo)
'''




g.addEdge(0,5,2)
g.addEdge(1,2,4)
g.addEdge(2,3,9)
g.addEdge(3,4,7)
g.addEdge(3,5,3)
g.addEdge(4,0,1)
g.addEdge(5,4,8)
g.addEdge(5,2,1)

print(g.getVertices())
# vertList = { key :VertextObject}
# VertextObject =  ||key = key, connectedTo = {到達(dá)節(jié)點(diǎn):權(quán)重}||   => |||| 表示的是權(quán)重的意思

# print(g)
for v in g: # 循環(huán)類實(shí)例 => return ->  g = VertextObject的集合  v = VertextObject
    for w in v.getConnections(): # 獲得類實(shí)例的connectedTO
        # print(w)
        print("({},{}:{})".format(v.getId(),w.getId(),v.getWeight(w))) ## 為什么會(huì)是這樣 => 因?yàn)檫@個(gè)時(shí)候v就是class啊

6.4 圖的遍歷

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

圖常用的遍歷方式有兩種,分別是深度優(yōu)先搜索(DFS)和廣度優(yōu)先搜索(BFS)

6.4.1 深度優(yōu)先搜索

? 深度優(yōu)先其思想就是從圖中某一個(gè)頂點(diǎn)出發(fā),然后沿著邊一直搜索,一條道走到黑!直到走投無(wú)路為止,然后再回退(迷途知返),當(dāng)回退到能看到有尚未訪問(wèn)的頂點(diǎn)時(shí),繼續(xù)向前搜索,一條道走到黑。直到迷途知返回到頂點(diǎn),且所有頂點(diǎn)均被訪問(wèn)為止,算法搜索結(jié)束。

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

from pythonds.graphs import Graph
class DFSGraph(Graph):
    def __init__(self):
        super().__init__()
        self.time = 0

    def dfs(self):
        for aVertex in self:
            aVertex.setColor('white')
            aVertex.setPred(-1)
        for aVertex in self:
            if aVertex.getColor() == 'white':
                self.dfsvisit(aVertex)

    def dfsvisit(self,startVertex):
        startVertex.setColor('gray')
        self.time += 1
        startVertex.setDiscovery(self.time)
        for nextVertex in startVertex.getConnections():
            if nextVertex.getColor() == 'white':
                nextVertex.setPred(startVertex)
                self.dfsvisit(nextVertex)
        startVertex.setColor('black')
        self.time += 1
        startVertex.setFinish(self.time)

6.4.2 廣度優(yōu)先搜索

? 廣度優(yōu)先類似于樹(shù)的層次遍歷,從圖中的某個(gè)頂點(diǎn)出發(fā),然后一次訪問(wèn)頂點(diǎn)未曾訪問(wèn)過(guò)的鄰接點(diǎn),然后從這些鄰接點(diǎn)依次訪問(wèn)它們的鄰接點(diǎn)。直至所有點(diǎn)被訪問(wèn)到

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

python 數(shù)據(jù)結(jié)構(gòu)和算法,數(shù)據(jù)結(jié)構(gòu)和算法,python,開(kāi)發(fā)語(yǔ)言

def bfs(g,start):
    start.setDistance(0)
    start.srtPred(None)
    vertQueue = Queue()
    vertQueue.enqueue(start)
    while (vertQueue.size()>0):
        currentVert = vertQueue.dequeue()
        for nbr in currentVert.getConnections():
            if (nbr.getColor() == 'white'):
                nbr.setColor('gray')
                nbr.setDistance(currentVert.getDistance()+1)
                nbr.serPred(currentVert)
                vertQueue.enqueue(nbr)
         currentVert.setColor('black')

七、參考資料

1、《python數(shù)據(jù)結(jié)構(gòu)與算法》

2、清華大學(xué)博士講解Python數(shù)據(jù)結(jié)構(gòu)與算法(完整版)全套100節(jié)

數(shù)據(jù)結(jié)構(gòu)與算法文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-786523.html

到了這里,關(guān)于Python數(shù)據(jù)結(jié)構(gòu)與算法的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Python數(shù)據(jù)結(jié)構(gòu)與算法

    Python數(shù)據(jù)結(jié)構(gòu)與算法

    棧、隊(duì)列、雙端隊(duì)列和列表都是有序的數(shù)據(jù)集合, 其元素的順序取決于添加順序或移除順序。一旦某個(gè)元素被添加進(jìn)來(lái),它與前后元素的相對(duì)位置將保持不變。這樣的數(shù)據(jù)集合經(jīng)常被稱為線性數(shù)據(jù)結(jié)構(gòu)。 棧的添加操作和移除操作總發(fā)生在同一端。棧中的元素離底端越近,代

    2024年02月02日
    瀏覽(23)
  • Python數(shù)據(jù)結(jié)構(gòu)與算法-數(shù)據(jù)結(jié)構(gòu)(列表、棧、隊(duì)列、鏈表)

    Python數(shù)據(jù)結(jié)構(gòu)與算法-數(shù)據(jù)結(jié)構(gòu)(列表、棧、隊(duì)列、鏈表)

    數(shù)據(jù)結(jié)構(gòu)是指相互之間存在這一種或者多種關(guān)系的數(shù)據(jù)元素的集合和該集合中元素之間的關(guān)系組成。 簡(jiǎn)單來(lái)說(shuō),數(shù)據(jù)結(jié)構(gòu)就是設(shè)計(jì)數(shù)據(jù)以何種方式組織并存儲(chǔ)在計(jì)算機(jī)中。 比如:列表、集合與字典等都是一種數(shù)據(jù)結(jié)構(gòu)。 N.Wirth:“程序=數(shù)據(jù)結(jié)構(gòu)+算法” 數(shù)據(jù)結(jié)構(gòu)按照其 邏輯結(jié)

    2024年02月08日
    瀏覽(36)
  • Python數(shù)據(jù)結(jié)構(gòu)與算法-樹(shù)

    Python數(shù)據(jù)結(jié)構(gòu)與算法-樹(shù)

    詳情見(jiàn) https://blog.csdn.net/little_limin/article/details/129845592 Python數(shù)據(jù)結(jié)構(gòu)與算法-堆排序(NB組)—— 一、樹(shù)的基礎(chǔ)知識(shí) 樹(shù)結(jié)構(gòu)也是鏈?zhǔn)酱鎯?chǔ)的,與鏈表的結(jié)構(gòu)相似,只是樹(shù)存在多個(gè)子節(jié)點(diǎn),不是線性的,存在一堆多的情況。與雙鏈表相似,只不過(guò)鏈表節(jié)點(diǎn)對(duì)應(yīng)的下一個(gè)節(jié)點(diǎn)只有一

    2023年04月15日
    瀏覽(26)
  • python算法與數(shù)據(jù)結(jié)構(gòu)---動(dòng)態(tài)規(guī)劃

    python算法與數(shù)據(jù)結(jié)構(gòu)---動(dòng)態(tài)規(guī)劃

    記不住過(guò)去的人,注定要重蹈覆轍。 對(duì)于一個(gè)模型為n的問(wèn)題,將其分解為k個(gè)規(guī)模較小的子問(wèn)題(階段),按順序求解子問(wèn)題,前一子問(wèn)題的解,為后一子問(wèn)題提供有用的信息。在求解任一子問(wèn)題時(shí),通過(guò)決策求得局部最優(yōu)解,依次解決各子問(wèn)題。最后通過(guò)簡(jiǎn)單的判斷,得到

    2024年02月20日
    瀏覽(31)
  • 數(shù)據(jù)結(jié)構(gòu)與算法之Python實(shí)現(xiàn)——隊(duì)列

    數(shù)據(jù)結(jié)構(gòu)與算法之Python實(shí)現(xiàn)——隊(duì)列

    在上一期博客中我們學(xué)習(xí)了棧這種結(jié)構(gòu),本期博客將學(xué)習(xí)一下跟棧很類似的一種結(jié)構(gòu)——隊(duì)列。 本期知識(shí)點(diǎn): 順序隊(duì)列 循環(huán)隊(duì)列 鏈?zhǔn)疥?duì)列 隊(duì)列的應(yīng)用 ?? 什么是隊(duì)列? 隊(duì)列是一種跟棧很相似的結(jié)構(gòu)。我們知道棧是一種 先進(jìn)后出 的結(jié)構(gòu),那么隊(duì)列就像一個(gè)排隊(duì)的隊(duì)伍一樣

    2024年02月06日
    瀏覽(25)
  • 數(shù)據(jù)結(jié)構(gòu)與算法Python版:基數(shù)排序

    簡(jiǎn)介 :基數(shù)排序(radix sort)屬于“分配式排序”(distribution sort),又稱“桶子法”(bucket sort)或bin sort,顧名思義,它是透過(guò)鍵值的部份資訊,將要排序的元素分配至某些“桶”中,藉以達(dá)到排序的作用,基數(shù)排序法是屬于穩(wěn)定性的排序,其時(shí)間復(fù)雜度為O (nlog?m),其中

    2024年01月24日
    瀏覽(21)
  • 【數(shù)據(jù)結(jié)構(gòu)與算法】python實(shí)現(xiàn)二分查找

    【數(shù)據(jù)結(jié)構(gòu)與算法】python實(shí)現(xiàn)二分查找

    二分查找 又稱折半查找,它是一種效率較高的查找方法 原理:首先,假設(shè)表中元素是按升序排列,將表中間位置記錄的與查找比較,如果兩者相等,則查找成功;否則利用中間位置記錄將表分成前、后兩個(gè)子表,如果中間位置記錄的大于查找,則

    2024年02月05日
    瀏覽(32)
  • 【python與數(shù)據(jù)結(jié)構(gòu)】(leetcode算法預(yù)備知識(shí))

    【python與數(shù)據(jù)結(jié)構(gòu)】(leetcode算法預(yù)備知識(shí))

    筆記為自我總結(jié)整理的學(xué)習(xí)筆記,若有錯(cuò)誤歡迎指出喲~ 1.數(shù)字類型: 整數(shù)(int):表示整數(shù)值,例如 1、-5、100。 浮點(diǎn)數(shù)(float):表示帶有小數(shù)部分的數(shù)字,例如 3.14、-0.5、2.0。 復(fù)數(shù)(complex):表示實(shí)部和虛部的復(fù)數(shù),例如 2+3j。 2.布爾類型(bool): 表示真(True)或假(

    2024年02月08日
    瀏覽(23)
  • Python - 深夜數(shù)據(jù)結(jié)構(gòu)與算法之 位運(yùn)算

    Python - 深夜數(shù)據(jù)結(jié)構(gòu)與算法之 位運(yùn)算

    目錄 一.引言 二.位運(yùn)算簡(jiǎn)介 1.二進(jìn)制與十進(jìn)制 2.左/右移 3.位運(yùn)算 4.異或 XOR 5.指定位置的位運(yùn)算 6.實(shí)戰(zhàn)要點(diǎn) 三.經(jīng)典算法實(shí)戰(zhàn) 1.Number-1-of-bits [191] 2.Power-Of-Two [231] 3.Reverse-2-Bits [190] 4.N-Queens [51] 四.總結(jié) 通常情況下我們計(jì)數(shù)采取十進(jìn)制,這是因?yàn)槿粘I钪形覀兞?xí)慣了 0-9 的數(shù)字

    2024年01月18日
    瀏覽(19)
  • python算法與數(shù)據(jù)結(jié)構(gòu)---排序和歸并排序

    python算法與數(shù)據(jù)結(jié)構(gòu)---排序和歸并排序

    掌握歸并排序的基本原理 使用python語(yǔ)言解答歸并排序題目 原理及過(guò)程 將兩個(gè)有序的數(shù)組合并成一個(gè)有序數(shù)組稱為 從上往下分解:把當(dāng)前區(qū)間一分為二,直至分解為若干個(gè)長(zhǎng)度為1的子數(shù)組 從上往下的合并:兩個(gè)有序的子區(qū)域兩兩向上合并; 體現(xiàn)了分治思想,穩(wěn)定排序 復(fù)雜

    2024年01月21日
    瀏覽(44)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包