日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

算法图解笔记

發(fā)布時(shí)間:2024/8/1 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法图解笔记 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
  • 前言知識(shí)
  • 第一章,算法簡(jiǎn)介
    • 1.2,二分法查找元素
      • 1.2.1,特殊的二分查找
  • 第二章,選擇排序
    • 2.1,內(nèi)存工作原理
      • 2.2.1,鏈表
      • 2.2.2,數(shù)組
      • 2.2.3,術(shù)語(yǔ)
    • 2.3,選擇排序
    • 2.4,小結(jié)
  • 第三章,遞歸
    • 3.2,基線(xiàn)條件和遞歸條件
    • 3.3,棧
      • 3.3.1,調(diào)用棧
      • 3.3.2,遞歸調(diào)用棧
    • 3.4,小結(jié)
  • 第四章,快速排序
    • 4.1,分而治之
    • 4.2 快速排序
    • 4.3 再談大O表示法
    • 4.4,小結(jié)
  • 第五章,散列表
    • 5.3,沖突
    • 5.4,性能
    • 5.5,小結(jié)
  • 第六章,廣度優(yōu)先搜索
    • 6.1,圖是什么
    • 6.2,廣度優(yōu)先搜索
    • 6.3,棧與隊(duì)列
    • 6.4,代碼實(shí)現(xiàn)圖結(jié)構(gòu)
    • 6.4.1 運(yùn)行時(shí)間
  • 第七章,迪克斯特拉算法
    • 7.1,使用迪克斯特拉算法
    • 7.2,術(shù)語(yǔ)
    • 7.3,負(fù)權(quán)邊
    • 7.4,編程實(shí)現(xiàn)狄克斯特拉算法
    • 7.5,小結(jié)
  • 第八章,貪婪(貪心)算法
    • 8.1,教室調(diào)度問(wèn)題
    • 8.2,背包問(wèn)題
    • 8.3,集合覆蓋問(wèn)題
      • 8.3.1,近似算法
    • 8.4,NP完全問(wèn)題
      • 8.4.1,如何識(shí)別NP完全問(wèn)題
    • 8.5,小結(jié)
  • 第九章,動(dòng)態(tài)規(guī)劃
    • 9.1,概念
    • 9.2,背包問(wèn)題
    • 9.1,最長(zhǎng)公共子串
  • 第十章,K最近鄰算法
  • 第十一章 接下來(lái)如何做

前言知識(shí)

十大經(jīng)典排序算法動(dòng)畫(huà)與解析,看我就夠了!(配代碼完全版)

10 大排序算法時(shí)間復(fù)雜度及空間復(fù)雜度如下圖:

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-A010KZLD-1664372500316)(…/data/images/排序算法時(shí)間復(fù)雜度.jpg)]

第一章,算法簡(jiǎn)介

1.2,二分法查找元素

二分查找是一種算法,其輸入是一個(gè)有序的元素列表(必須有序的原因稍后解釋)。如果要查找的元素包含在列表中,二分查找返回其位置;否則返回 null,使用二分查找時(shí),每次猜測(cè)的是中間的數(shù)字,從而將余下的數(shù)字排除一半。(僅僅當(dāng)列表是有序的時(shí)候,二分查找才管用

一般而言,對(duì)于包含n個(gè)元素的列表查找某個(gè)元素,使用二分法最多需要 log2nlog_{2}nlog2?n 步(**時(shí)間復(fù)雜度為 log2nlog_{2}nlog2?n **),簡(jiǎn)單查找最多需要 n 步。大 O 表示法指出了算法最糟糕情況下的運(yùn)行時(shí)間。二分法實(shí)例代碼如下:

def binary_search(list, item):low = 0high = len(list)-1while low <= high:mid = (low + high) // 2if list[mid] == item:return midelif list[mid] > item:high = mid - 1else:low = mid + 1return Noneif __name__ == "__main__":print(binary_search([1,2,3,4,6,7], 3)) # 輸出 2

1.2.1,特殊的二分查找

有序數(shù)組中的目標(biāo)出現(xiàn)多次,利用二分查找返回在最左邊出現(xiàn)的目標(biāo)值或者是最右邊出現(xiàn)的目標(biāo)值,實(shí)例代碼如下:

def binary_search2(arr, target, flag="left"):if not arr:return Noneleft = 0right = len(arr) - 1while left <= right:mid = left + (right - left) // 2 # 防止數(shù)據(jù)過(guò)大溢出?if arr[mid] < target:left = mid + 1elif arr[mid] > target:right = mid -1else:if flag == "left":if mid > 0 and arr[mid-1] == target:right = mid -1 # 不斷向最左邊逼近else:return midelif flag == "right":if mid + 1 < len(arr) and arr[mid + 1] == target:left = mid + 1 # 不斷向最右邊逼近else:return midreturn Noneif __name__ == "__main__":print(binary_search2([1,1,1,3,3,3,4], 3, "left")) # 查找最左邊出現(xiàn)的目標(biāo)值, 輸出3print(binary_search2([1,1,1,3,3,3,4,4,4], 3, "right")) # 查找最右邊出現(xiàn)的目標(biāo)值, 輸出5

第二章,選擇排序

2.1,內(nèi)存工作原理

在計(jì)算機(jī)中,存儲(chǔ)多項(xiàng)數(shù)據(jù)時(shí),有兩種基本方式-數(shù)組和鏈表。但它們并非適用于所有情形。

2.2.1,鏈表

鏈表中的元素可存儲(chǔ)在內(nèi)存的任何地方。
鏈表的每個(gè)元素都存儲(chǔ)了下一個(gè)元素的地址,從而使一系列隨機(jī)的內(nèi)存地址串在一起。鏈表結(jié)構(gòu)直觀顯示如下圖所示:

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-G7hRcnjg-1664372500320)(…/data/images/2.1鏈表.png)]

鏈表的優(yōu)勢(shì)在插入元素方面,那數(shù)組的優(yōu)勢(shì)又是什么呢?

2.2.2,數(shù)組

需要隨機(jī)地讀取元素時(shí),數(shù)組的效率很高,因?yàn)榭裳杆僬业綌?shù)組的任何元素。在鏈表中,元素并非靠在一起的,你無(wú)法迅速計(jì)算出第五個(gè)元素的內(nèi)存 地址,而必須先訪問(wèn)第一個(gè)元素以獲取第二個(gè)元素的地址,再訪問(wèn)第二個(gè)元素以獲取第三個(gè)元素 的地址,以此類(lèi)推,直到訪問(wèn)第五個(gè)元素。

2.2.3,術(shù)語(yǔ)

數(shù)組的元素帶編號(hào),編號(hào)從 0 而不是 1 開(kāi)始,幾乎所有的編程語(yǔ)言都從0開(kāi)始對(duì)數(shù)組元素進(jìn)行編號(hào),比如C/C++的數(shù)組結(jié)構(gòu)和Python的列表結(jié)構(gòu)。
元素的位置稱(chēng)為索引。下面是常見(jiàn)數(shù)組和鏈表操作的運(yùn)行時(shí)間.

2.3,選擇排序

選擇排序時(shí)間復(fù)雜度O(n2)O(n^{2})O(n2)

def findSmallest(arr):smallest = arr[0] # 存儲(chǔ)最小的值smallest_index = 0 # 存儲(chǔ)最小元素的索引for i in range(1, len(arr)):if arr[i] < smallest:smallest_index = ismallest = arr[i]return smallest # 選擇排序法對(duì)數(shù)組進(jìn)行排序 def selectionSort(arr):newArr = []for i in range(len(arr)):smallest = findSmallest(arr)arr.remove(smallest)newArr.append(smallest)return newArr # 實(shí)例應(yīng)用 print(selectionSort([5, 3, 6, 100])) # [3, 5, 6, 100]

2.4,小結(jié)

  • 計(jì)算機(jī)內(nèi)存猶如一大堆抽屜。
  • 需要存儲(chǔ)多個(gè)元素時(shí),可使用數(shù)組或鏈表。
  • 數(shù)組的元素都在一起。
  • 鏈表的元素是分開(kāi)的,其中每個(gè)元素都存儲(chǔ)了下一個(gè)元素的地址。
  • 數(shù)組的讀取速度很快。
  • 鏈表的插入和刪除速度很快.
  • 在同一個(gè)數(shù)組中,所有元素的類(lèi)型都必須相同(都為int、 double等)。

第三章,遞歸

學(xué)習(xí)如何將問(wèn)題分成基線(xiàn)條件和遞歸條件,學(xué)習(xí)如何使用遞歸算法,遞歸算法直觀上更好理解,步驟簡(jiǎn)單。

3.2,基線(xiàn)條件和遞歸條件

編寫(xiě)遞歸函數(shù)時(shí),必須告訴它何時(shí)停止,因此,每個(gè)遞歸函數(shù)有兩個(gè)部分:基線(xiàn)條件(base case)和遞歸條件(recursive case)。遞歸條件指的是函數(shù)調(diào)用自己,而基線(xiàn)條件則 指的是函數(shù)不再調(diào)用自己,從而避免形成無(wú)限循環(huán)。

3.3,棧

棧的定義:棧是一種只能從表的一端存取數(shù)據(jù)且遵循 “先進(jìn)后出” 原則的線(xiàn)性存儲(chǔ)結(jié)構(gòu)。
調(diào)用棧(call stack)

3.3.1,調(diào)用棧

計(jì)算機(jī)在內(nèi)部使用被稱(chēng)為調(diào)用棧的棧。調(diào)用另一個(gè)函數(shù)時(shí),當(dāng)前函數(shù)暫停 并處于未完成狀態(tài)。該函數(shù)的所有變量的值都還在內(nèi)存中。棧頂?shù)姆娇蛑赋隽水?dāng)前執(zhí)行 到了什么地方。

3.3.2,遞歸調(diào)用棧

棧在遞歸中扮演著重要角色。使用棧雖然很方便,但是也要付出代價(jià):存儲(chǔ)詳盡的信息可能占用大量的內(nèi)存。每個(gè)函數(shù)調(diào) 用都要占用一定的內(nèi)存,如果棧很高,就意味著計(jì)算機(jī)存儲(chǔ)了大量函數(shù)調(diào)用的信息。在這種情況 下,你有兩種選擇。

  • 重新編寫(xiě)代碼
  • 使用尾遞歸

3.4,小結(jié)

  • 遞歸值的是調(diào)用自己的函數(shù)
  • 每個(gè)遞歸函數(shù)都有兩個(gè)條件:基線(xiàn)條件和遞歸條件
  • 棧有兩種操作:壓如和彈出
  • 所有函數(shù)調(diào)用都進(jìn)入調(diào)用棧
  • 調(diào)用棧可能很長(zhǎng),這將占用大量?jī)?nèi)存

第四章,快速排序

快速排序使用分而治之的策略,分而治之是我們學(xué)習(xí)的第一種通用的問(wèn)題解決辦法。
分而治之(divide and conquer,D&C)-一種著名的遞歸式問(wèn)題解決辦法。

4.1,分而治之

D&C算法是遞歸的。使用D&C解決問(wèn)題的過(guò)程包括兩個(gè)步驟:

  • 找出基線(xiàn)條件,這種條件必須盡可能簡(jiǎn)單。
  • 不斷將問(wèn)題分解(或者說(shuō)縮小規(guī)模),直到符合基線(xiàn)條件。

D&C并非可直接用于解決問(wèn)題的算法,而是一種解決問(wèn)題的思路。

4.2 快速排序

C語(yǔ)言標(biāo)準(zhǔn)庫(kù)中的函數(shù)qsort實(shí)現(xiàn)的就是快速排序。快速排序也是用了D&C思想。
對(duì)數(shù)組進(jìn)行快速排序,步驟如下:

  • 隨機(jī)選擇一個(gè)基準(zhǔn)值;
  • 將數(shù)組分成兩個(gè)子數(shù)組:小于基準(zhǔn)值的元素和大于基準(zhǔn)值額元素;
  • 對(duì)這兩個(gè)子數(shù)組進(jìn)行排序。
  • 在平均情況下,快速排序時(shí)間復(fù)雜度O(nlogn)O(nlogn)O(nlogn)。快速排序代碼如下:

    def quicksort(array):if len(array) < 2: # 基線(xiàn)條件:為空或只包含一個(gè)元素的數(shù)組是“有序”的return arrayelse:# 遞歸條件pivot = array[0] less = [x for x in array[1:] if x <= pivot]greater = [x for x in array[1:] if x > pivot]return quicksort(less) + [pivot] + quicksort(greater) print(quicksort([4, 90, 0, 2, 17, 79, 12])) # [0, 2, 4, 12, 17, 79, 90]

    上面的代碼空間復(fù)雜度很大,真正的快排是原地排序,空間復(fù)雜度為O(1),代碼如下:

    # _*_ coding:utf-8 _*_def quick_sort(L):return q_sort(L, 0, len(L)-1)def q_sort(L, left, right):if left < right:pivot = Partition(L, left, right)q_sort(L, left, pivot-1)q_sort(L, pivot+1, right)return Ldef Partition(L, left, right):pivotkey = L[left]while left < right:while left < right and L[right] >= pivotkey:right -= 1L[left] = L[right]while left < right and L[left] <= pivotkey:left += 1L[right] = L[left] # 遇到比基準(zhǔn)大的數(shù), 則覆蓋在之前尾部指針的位置L[left] = pivotkeyreturn leftif __name__ == "__main__":L = [5, 9, 1, 1, 11, 6, 7, 2, 4]print(quick_sort(L))

    4.3 再談大O表示法

    快速排序的獨(dú)特之處在于,其速度取決于選擇的基準(zhǔn)值。在討論快速排序的運(yùn)行時(shí)間前,我 們?cè)賮?lái)看看最常見(jiàn)的大O運(yùn)行時(shí)間。

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-JYpoxy7V-1664372500324)(…/data/images/4.3再談大O表示法.png)]

    • 選擇排序,其運(yùn)行時(shí)間為 O(n2)O(n^2)O(n2),速度非常慢。
    • 還有一種名為合并排序(merge sort)的排序算法,其運(yùn)行時(shí)間為 O(nlogn)O(nlogn)O(nlogn),比選擇排序快得多!
    • 快速排序的情況比較棘手,在最糟情況下,其運(yùn)行時(shí)間為 O(n2)O(n^2)O(n2)。與選擇排序一樣慢!但這是最糟情況。在平均情況下,快速排序的運(yùn)行時(shí)間為 O(nlogn)O(nlogn)O(nlogn)

    由對(duì)數(shù)的換底公式,loganlog_a nloga?nlogbnlog_b nlogb?n 只有一個(gè)常數(shù)因子不同,這個(gè)因子在大O記法中被丟棄。因此記作O(logn)O(log n)O(logn)而不論對(duì)數(shù)的底是多少,是對(duì)數(shù)時(shí)間算法的標(biāo)準(zhǔn)記法

    4.4,小結(jié)

    • D&C將問(wèn)題逐步分解。使用D&C處理列表時(shí),基線(xiàn)條件很可能是空數(shù)組或只包含一個(gè)元 素的數(shù)組。
    • 實(shí)現(xiàn)快速排序時(shí),請(qǐng)隨機(jī)地選擇用作基準(zhǔn)值的元素。快速排序的平均運(yùn)行時(shí)間為O(n log n)
    • 大O表示法中的常量有時(shí)候事關(guān)重大,這就是快速排序比合并排序快的原因所在。
    • 比較簡(jiǎn)單查找和二分查找時(shí),常量幾乎無(wú)關(guān)緊要,因?yàn)榱斜砗荛L(zhǎng)時(shí), O(log n)的速度比O(n) 快得多。

    第五章,散列表

    數(shù)組和鏈表結(jié)構(gòu)可以用以查找,棧不行。散列表也叫哈希表(Hash table),散列表有些類(lèi)似 Python 中的字典 dict 結(jié)構(gòu)。散列表可用以:

    • 模擬映射關(guān)系;
    • 防止重復(fù);
    • 緩沖/記住數(shù)據(jù),以免服務(wù)器再通過(guò)處理來(lái)生成它們。

    5.3,沖突

    給兩個(gè)鍵分配的位置相同,這被稱(chēng)為沖突(collision)。處理沖突最簡(jiǎn)單的辦法就是:如果兩個(gè)鍵映射到了同一個(gè)位置,就在這個(gè)位置存儲(chǔ)一個(gè)鏈表。

    5.4,性能

    散列表,數(shù)組,鏈表的查找、插入刪除元素的時(shí)間復(fù)雜度,如下表所示:

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-mGqZtFSo-1664372500326)(…/images/5.4性能.png)]

    在平均情況下,散列表的查找(獲取給定索引處的值)速度與數(shù)組一樣快,而插入和刪除速 度與鏈表一樣快,因此它兼具兩者的優(yōu)點(diǎn)!但在最糟情況下,散列表的各種操作的速度都很慢。 因此,在使用散列表時(shí),避開(kāi)最糟情況至關(guān)重要。為此,需要避免沖突。而要避免沖突,需要有:

    • 較低的填裝因子;
    • 良好的散列函數(shù)。

    5.5,小結(jié)

    散列表是一種功能強(qiáng)大的數(shù)據(jù)結(jié)構(gòu),其操作速度快,還能讓你以不同的方式建立數(shù)據(jù)模型。 你可能很快會(huì)發(fā)現(xiàn)自己經(jīng)常在使用它。

    • 你可以結(jié)合散列函數(shù)和數(shù)組來(lái)創(chuàng)建散列表。
    • 沖突很糟糕,你應(yīng)使用可以最大限度減少?zèng)_突的散列函數(shù)。
    • 散列表的查找、插入和刪除速度都非常快。
    • 散列表適合用于模擬映射關(guān)系。
    • 一旦填裝因子超過(guò)0.7,就該調(diào)整散列表的長(zhǎng)度。
    • 散列表可用于緩存數(shù)據(jù)(例如,在Web服務(wù)器上)。
    • 散列表非常適合用于防止重復(fù)。

    第六章,廣度優(yōu)先搜索

    圖算法:廣度優(yōu)先搜索(breadth-first search, BFS)算法
    廣度優(yōu)先搜索讓你能夠找出兩樣?xùn)|西之間的最短距離,不過(guò)最短距離的含義有很多!使用廣度優(yōu)先搜索可以:

    • 編寫(xiě)國(guó)際跳棋AI,計(jì)算最少走多少步就可獲勝;
    • 編寫(xiě)拼寫(xiě)檢查器,計(jì)算最少編輯多少個(gè)地方就可將錯(cuò)拼的單詞改成正確的單詞,如將 READED改為READER需要編輯一個(gè)地方;
    • 根據(jù)你的人際關(guān)系網(wǎng)絡(luò)找到關(guān)系最近的醫(yī)生。

    解決最短路徑問(wèn)題的算法被稱(chēng)為廣度有限算法,一般步驟為:

  • 使用圖來(lái)建立問(wèn)題模型。
  • 使用廣度優(yōu)先搜索解決問(wèn)題。
  • 6.1,圖是什么

    圖由節(jié)點(diǎn)(node)和邊(edge)組成。

    6.2,廣度優(yōu)先搜索

    在廣度優(yōu)先搜索的執(zhí)行過(guò)程中,搜索范圍從起點(diǎn)開(kāi)始逐漸向外延伸,即先檢查一度關(guān)系,再檢查二度關(guān)系。

    6.3,棧與隊(duì)列

    • :后進(jìn)先出(Last In First Out,LIFO)的數(shù)據(jù)結(jié)構(gòu)
    • 隊(duì)列:先進(jìn)先出(First In First Out,FIFO)的數(shù)據(jù)結(jié)構(gòu),支持入隊(duì)和出對(duì)操作。

    6.4,代碼實(shí)現(xiàn)圖結(jié)構(gòu)

    圖中每個(gè)節(jié)點(diǎn)都與相鄰節(jié)點(diǎn)相連,散列表結(jié)構(gòu)可以表示這種關(guān)系。
    圖分為有向圖(directed graph)和無(wú)向圖(undirected graph),有向圖關(guān)系是單向的,無(wú)向圖沒(méi)有箭頭,直接相連的節(jié)點(diǎn)互為鄰居。對(duì)從自己出發(fā)有指向他人的箭頭,則有鄰居。

    6.4.1 運(yùn)行時(shí)間

    如果你在你的整個(gè)人際關(guān)系網(wǎng)中搜索芒果銷(xiāo)售商,就意味著你將沿每條邊前行(記住,邊是從一個(gè)人到另一個(gè)人的箭頭或連接),因此運(yùn)行時(shí)間至少為 O(邊數(shù))O(邊數(shù))O(邊數(shù))。你還使用了一個(gè)隊(duì)列,其中包含要檢查的每個(gè)人。將一個(gè)人添加到隊(duì)列需要的時(shí)間是固定的,即為 O(1)O(1)O(1),因此對(duì)每個(gè)人都這樣做需要的總時(shí)間為 O(人數(shù))O(人數(shù))O(人數(shù))。所以,廣度優(yōu)先搜索的運(yùn)行時(shí)間為 O(人數(shù)+邊數(shù))O(人數(shù) + 邊數(shù))O(人數(shù)+邊數(shù)),這通常寫(xiě)作 O(V+E)O(V + E)O(V+E),其中 VVV 為頂點(diǎn)( vertice)數(shù), EEE 為邊數(shù)。

    第七章,迪克斯特拉算法

    7.1,使用迪克斯特拉算法

    迪克斯特拉算法能夠找出加權(quán)圖中前往X的最短路徑。對(duì)于尋找耗時(shí)最少的路徑,迪克斯特拉算法包含4個(gè)步驟:

  • 找出“最便宜”的節(jié)點(diǎn),即可在最短時(shí)間內(nèi)到達(dá)的節(jié)點(diǎn)。
  • 更新該節(jié)點(diǎn)的鄰居的開(kāi)銷(xiāo),其含義將稍后介紹。
  • 重復(fù)這個(gè)過(guò)程,直到對(duì)圖中的每個(gè)節(jié)點(diǎn)都這樣做了。
  • 計(jì)算最終路徑。
  • 每個(gè)節(jié)點(diǎn)都有開(kāi)銷(xiāo)。開(kāi)銷(xiāo)指的是從起點(diǎn)前往該節(jié)點(diǎn)需要多長(zhǎng)時(shí)間。

    7.2,術(shù)語(yǔ)

    • 帶權(quán)重的圖稱(chēng)為加權(quán)圖( weighted graph),不帶權(quán)重的圖稱(chēng)為非加權(quán)圖( unweighted graph)。
    • 要計(jì)算非加權(quán)圖中的最短路徑,可使用廣度優(yōu)先搜索。要計(jì)算加權(quán)圖中的最短路徑,可使用狄克斯特拉算法。
    • 在無(wú)向圖中,每條邊都是一個(gè)環(huán)。狄克斯特拉算法只適用于有向無(wú)環(huán)圖( directed acyclic graph, DAG)。

    圖可能有環(huán),所謂環(huán),是指由一個(gè)節(jié)點(diǎn)出發(fā),走一圈后可以又回到原節(jié)點(diǎn),如下圖所示:

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-nvShzYxH-1664372500329)(…/data/images/7.2環(huán)示意圖.png)]

    7.3,負(fù)權(quán)邊

    因此, 不能將狄克斯特拉算法用于包含負(fù)權(quán)邊的圖。在包含負(fù)權(quán)邊的圖中,要找出最短路徑,可使用另一種算法——貝爾曼?福德算法( Bellman-Ford algorithm)。

    7.4,編程實(shí)現(xiàn)狄克斯特拉算法

    以下圖為例,編程實(shí)現(xiàn)耗時(shí)最短的路徑。

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-IROpVHMd-1664372500334)(…/data/images/7.4圖算法問(wèn)題.png)]

    代碼如下:

    # 為了實(shí)現(xiàn)帶權(quán)圖,可使用散列表,散列表用Python字典實(shí)現(xiàn) graph = {} # 存儲(chǔ)起始節(jié)點(diǎn)鄰居和前往鄰居的開(kāi)銷(xiāo) graph['start'] = {} graph["start"]["a"] = 6 graph["start"]["b"] = 2 print(graph["start"].keys()) # 添加其他節(jié)點(diǎn)及其鄰居 graph["a"] = {} graph["a"]["fin"] = 1graph["b"] = {} graph["b"]["a"] = 3 graph["b"]["fin"] = 5# 終點(diǎn)沒(méi)有任何鄰居 graph['fin'] = {}# 創(chuàng)建存儲(chǔ)每個(gè)節(jié)點(diǎn)開(kāi)銷(xiāo)的開(kāi)銷(xiāo)表 infinity = float("inf") costs = {} costs["a"] = 6 costs["b"] = 2 costs["fin"] = infinity# 創(chuàng)建存儲(chǔ)父節(jié)點(diǎn)的散列表 parents = {} parents["a"] = "start" parents["b"] = "start" parents["fin"] = None # 創(chuàng)建一個(gè)數(shù)組,用于記錄處理過(guò)的節(jié)點(diǎn) processed = []# 找出開(kāi)銷(xiāo)最低的節(jié)點(diǎn) def find_lowest_cost_node(costs):lowest_cost = float("inf")lowest_cost_node = Nonefor node in costs:cost = costs[node]if cost < lowest_cost and node not in processed:lowest_cost = costlowest_cost_node = nodereturn lowest_cost_node# 在未處理的節(jié)點(diǎn)中找出開(kāi)銷(xiāo)最小的節(jié)點(diǎn) node = find_lowest_cost_node(costs) while node is not None:cost = costs[node]neighbors = graph[node]# 遍歷當(dāng)前節(jié)點(diǎn)的鄰居for n in neighbors.keys():new_cost = cost + neighbors[n]# 如果當(dāng)前節(jié)點(diǎn)前往該鄰居更近,就更新該鄰居的開(kāi)銷(xiāo), 同時(shí)將該鄰居的父節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)if costs[n] > new_cost:costs[n] = new_costparents[n] = node# 將當(dāng)前節(jié)點(diǎn)標(biāo)記為處理過(guò)processed.append(node) # 找出接下來(lái)要處理的節(jié)點(diǎn),并循環(huán)node = find_lowest_cost_node(costs)print("Cost from the start to each node:") print(costs)

    7.5,小結(jié)

    • 廣度優(yōu)先搜索用于在非加權(quán)圖中查找最短路徑。
    • 狄克斯特拉算法用于在加權(quán)圖中查找最短路徑。
    • 僅當(dāng)權(quán)重為正時(shí)狄克斯特拉算法才管用。
    • 如果圖中包含負(fù)權(quán)邊,請(qǐng)使用貝爾曼?福德算法。

    第八章,貪婪(貪心)算法

    貪婪算法思想很簡(jiǎn)單:每步都采取最優(yōu)的做法,專(zhuān)業(yè)術(shù)語(yǔ)說(shuō),就是每步都選擇局部最優(yōu)解,最終得到的就是全局最優(yōu)解。

    8.1,教室調(diào)度問(wèn)題

    根據(jù)給定課表,盡可能將更多的課程安排在某間教室。解決辦法:貪婪算法可找到最優(yōu)解。

    8.2,背包問(wèn)題

    背包重量有限,根據(jù)策略使得裝入背包的物品價(jià)值最高。
    在這里, 貪婪策略顯然不能獲得最優(yōu)解,但非常接近。在有些情況下,完美是優(yōu)秀的敵人。有時(shí)候,你只需找到一個(gè)能夠大致解決問(wèn)題的算法,此時(shí)貪婪算法正好可派上用場(chǎng),因?yàn)樗鼈儗?shí)現(xiàn)起來(lái)很容易,得到的結(jié)果又與正確結(jié)果相當(dāng)接近。

    8.3,集合覆蓋問(wèn)題

    每個(gè)廣播臺(tái)都覆蓋特定區(qū)域的州,找出覆蓋全美50個(gè)州的最小廣播集合。
    貪婪算法解決這個(gè)問(wèn)題,當(dāng)廣播臺(tái)數(shù)量過(guò)多,算法所耗費(fèi)的時(shí)間將激增。

    8.3.1,近似算法

    集合覆蓋問(wèn)題舉例:每個(gè)廣播臺(tái)都覆蓋特定區(qū)域的州,找出覆蓋全美50個(gè)州的最小廣播集合。貪婪算法可以解決這個(gè)問(wèn)題,當(dāng)廣播臺(tái)數(shù)量過(guò)多,算法所耗費(fèi)的時(shí)間將激增。

  • 選出這樣一個(gè)廣播臺(tái),即它覆蓋了最多的未覆蓋州。即便這個(gè)廣播臺(tái)覆蓋了一些已覆蓋的州,也沒(méi)有關(guān)系。
  • 重復(fù)第一步,直到覆蓋了所有的州。
  • 這是一種近似算法( approximation algorithm) 。在獲得精確解需要的時(shí)間太長(zhǎng)時(shí),可使用近似算法。判斷近似算法優(yōu)劣的標(biāo)準(zhǔn)如下:

    • 速度有多快;
    • 得到的近似解與最優(yōu)解的接近程度。

    代碼實(shí)例:

    """ 準(zhǔn)備工作 """ # 創(chuàng)建一個(gè)列表,包含要覆蓋的州 states_needed = set(["mt", "wa", "or", "id", "nv", "ut", "ca", "az"]) # 廣播臺(tái)清單 stations = {} stations["kone"] = set(["id", "nv", "ut"]) stations["ktwo"] = set(["wa", "id", "mt"]) stations["kthree"] = set(["or", "nv", "ca"]) stations["kfour"] = set(["nv", "ut"]) stations["kfive"] = set(["ca", "az"]) # 定義一個(gè)集合存儲(chǔ)最終選擇的廣播臺(tái) final_stations = set() """ 計(jì)算答案 """ best_station = None while states_needed:best_station = Nonestates_covered = set()for station, states in stations.items():covered = states_needed & statesif len(covered) > len(states_covered):best_station = stationstates_covered = coveredstates_needed -= states_coveredfinal_stations.add(best_station) print(final_stations)

    程序輸出如下:

    {‘kone’, ‘ktwo’, ‘kthree’, ‘kfive’}

    貪心算法的實(shí)質(zhì)是每次選出當(dāng)前的最優(yōu)解,不管整體,是基于一定假設(shè)下的最優(yōu)解。

    8.4,NP完全問(wèn)題

    旅行商問(wèn)題和集合覆蓋問(wèn)題有一些共同之處:你需要計(jì)算所有的解,并從中選出最小/最短的那個(gè)。這兩個(gè)問(wèn)題都屬于NP完全問(wèn)題。NP完全問(wèn)題的簡(jiǎn)單定義是,以難解著稱(chēng)的問(wèn)題,如旅行商問(wèn)題和集合覆蓋問(wèn)題。很多非常聰明的人都認(rèn)為,根本不可能編寫(xiě)出可快速解決這些問(wèn)題的算法。

    8.4.1,如何識(shí)別NP完全問(wèn)題

    NP 完全問(wèn)題無(wú)處不在!如果能夠判斷出要解決的問(wèn)題屬于 NP 完全問(wèn)題就好了,這樣就不用 去尋找完美的解決方案,而是使用近似算法即可。但要判斷問(wèn)題是不是NP完全問(wèn)題很難,易于解決的問(wèn)題和 NP 完全問(wèn)題的差別通常很小。

    但如果要找出經(jīng)由指定幾個(gè)點(diǎn)的的最短路徑,就是旅行商問(wèn)題——NP完全問(wèn)題。簡(jiǎn)言之,沒(méi)辦法判斷問(wèn)題是不是 NP 完全問(wèn)題,但還是有一些蛛絲馬跡可循的。

    • 元素較少時(shí)算法的運(yùn)行速度非常快,但隨著元素?cái)?shù)量的增加,速度會(huì)變得非常慢。
    • 涉及“所有組合”的問(wèn)題通常是NP完全問(wèn)題。
    • 不能將問(wèn)題分成小問(wèn)題,必須考慮各種可能的情況。這可能是NP完全問(wèn)題。
    • 如果問(wèn)題涉及序列(如旅行商問(wèn)題中的城市序列)且難以解決,它可能就是NP完全問(wèn)題。
    • 如果問(wèn)題涉及集合(如廣播臺(tái)集合)且難以解決,它可能就是NP完全問(wèn)題。
    • 如果問(wèn)題可轉(zhuǎn)換為集合覆蓋問(wèn)題或旅行商問(wèn)題,那它肯定是NP完全問(wèn)題。

    8.5,小結(jié)

    • 貪婪算法尋找局部最優(yōu)解,企圖以這種方式獲得全局最優(yōu)解。
    • 對(duì)于NP完全問(wèn)題,還沒(méi)有找到快速解決方案。
    • 面臨NP完全問(wèn)題時(shí),最佳的做法是使用近似算法。
    • 貪婪算法易于實(shí)現(xiàn)、運(yùn)行速度快,是不錯(cuò)的近似算法。

    第九章,動(dòng)態(tài)規(guī)劃

    9.1,概念

    動(dòng)態(tài)規(guī)劃算法是通過(guò)拆分問(wèn)題,定義問(wèn)題狀態(tài)和狀態(tài)之間的關(guān)系,使得問(wèn)題能夠以遞推(或者說(shuō)分治)的方式去解決。在學(xué)習(xí)動(dòng)態(tài)規(guī)劃之前需要明確掌握幾個(gè)重要概念,如下:

    • 階段:對(duì)于一個(gè)完整的問(wèn)題過(guò)程,適當(dāng)?shù)那蟹譃槿舾蓚€(gè)相互聯(lián)系的子問(wèn)題,每次在求解一個(gè)子問(wèn)題,則對(duì)應(yīng)一個(gè)階段,整個(gè)問(wèn)題的求解轉(zhuǎn)化為按照階段次序去求解。
    • 狀態(tài):狀態(tài)表示每個(gè)階段開(kāi)始時(shí)所處的客觀條件,即在求解子問(wèn)題時(shí)的已知條件。狀態(tài)描述了研究的問(wèn)題過(guò)程中的狀況。
    • 決策:決策表示當(dāng)求解過(guò)程處于某一階段的某一狀態(tài)時(shí),可以根據(jù)當(dāng)前條件作出不同的選擇,從而確定下一個(gè)階段的狀態(tài),這種選擇稱(chēng)為決策。
    • 策略:由所有階段的決策組成的決策序列稱(chēng)為全過(guò)程策略,簡(jiǎn)稱(chēng)策略。
    • 最優(yōu)策略:在所有的策略中,找到代價(jià)最小,性能最優(yōu)的策略,此策略稱(chēng)為最優(yōu)策略。
    • 狀態(tài)轉(zhuǎn)移方程:狀態(tài)轉(zhuǎn)移方程是確定兩個(gè)相鄰階段狀態(tài)的演變過(guò)程,描述了狀態(tài)之間是如何演變的。

    9.2,背包問(wèn)題

    學(xué)習(xí)動(dòng)態(tài)規(guī)劃,這是一種解決棘手問(wèn)題的方法,它將問(wèn)題分成小問(wèn)題,并先著手解決這些小問(wèn)題,每個(gè)動(dòng)態(tài)規(guī)劃問(wèn)題都是從一個(gè)網(wǎng)格入手,背包問(wèn)題的網(wǎng)格如下:

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-BtcVv2ps-1664372500337)(…/data/images/9.2-背包問(wèn)題.jpg)]

    工作原理:動(dòng)態(tài)規(guī)劃先解決子問(wèn)題,再逐步解決大問(wèn)題。從背包問(wèn)題的網(wǎng)格計(jì)算入手,可明白為何計(jì)算小背包可裝入的商品的最大價(jià)值。余下了空間時(shí),你可根據(jù)這些子問(wèn)題的答案來(lái)確定余下的空間可裝入哪些商品。計(jì)算每個(gè)單元格的價(jià)值時(shí),使用的公式都相同。 這個(gè)公式如下:

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-bQDVmanL-1664372500338)(…/data/images/9.2-背包問(wèn)題公示圖.png)]

    網(wǎng)格的行順序發(fā)生變化時(shí),最終答案沒(méi)有變化。各行的排列順序?qū)ψ罱K結(jié)果無(wú)關(guān)緊要。

    動(dòng)態(tài)規(guī)劃功能強(qiáng)大,它能夠解決子問(wèn)題并使用這些答案來(lái)解決大問(wèn)題。 但僅當(dāng)每個(gè)子問(wèn)題都是離散的,即不依賴(lài)于其他子問(wèn)題時(shí),動(dòng)態(tài)規(guī)劃才管用。 這意味著使用動(dòng)態(tài)規(guī)劃算 法解決不了去巴黎玩的問(wèn)題。

    9.1,最長(zhǎng)公共子串

    通過(guò)動(dòng)態(tài)規(guī)劃問(wèn)題,得到以下啟示:

    • 動(dòng)態(tài)規(guī)劃可幫助你在給定約束條件下找到最優(yōu)解在背包問(wèn)題中,你必須在背包容量給定的情況下,偷到價(jià)值最高的商品。
    • 在問(wèn)題可分解為彼此獨(dú)立且離散的子問(wèn)題時(shí),就可使用動(dòng)態(tài)規(guī)劃來(lái)解決。
      要設(shè)計(jì)出動(dòng)態(tài)規(guī)劃解決方案可能很難,這正是本節(jié)要介紹的。下面是一些通用的小貼士:
    • 每種動(dòng)態(tài)規(guī)劃解決方案都涉及網(wǎng)格。
    • 單元格中的值通常就是你要優(yōu)化的值。在前面的背包問(wèn)題中,單元格的值為商品的價(jià)值。
    • 每個(gè)單元格都是一個(gè)子問(wèn)題,因此你應(yīng)考慮如何將問(wèn)題分成子問(wèn)題,這有助于你找出網(wǎng)格的坐標(biāo)軸。

    第十章,K最近鄰算法

    第十一章 接下來(lái)如何做

    總結(jié)

    以上是生活随笔為你收集整理的算法图解笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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