算法圖解筆記

第一章

 

二分查找算法:

 

解釋及原理:二分查找是一種查找算法,需要輸入列表必須是有序的。如果要查找的元素在列表中則返回所在的位置,否則返回None。假設你想一個1-100之間的數字,某人每次猜測後會知道所猜的數字是大了還是小了,如果直接從1開始猜那麼至少要猜n次(n是你想的數字,這實際上是簡單查找)。如果從中間也就是50開始猜,那麼無論大小都已經排除掉一半的數字,接着再從剩下的一半數字的中間開始猜,以此類推知道猜到爲止,這就是二分查找的原理。

例子:假設要在一個包含240000個單次的字典中查找一個單詞,最多需要多少步呢?

如果要查找的單詞在詞典的末尾:

簡單查找需要:240000步

二分查找需要:18步(對於包含n個元素的列表最多需要log2n步)

速度顯而易見,二分查找圖示如下:

Python實現:

 

def binary_search(list, searchvalue):

    low_index = 0

    high_index = len(list)-1

    while low_index <= high_index:

        mid_index = int((low_index + high_index) / 2)  # 取整

        guessvalue = list[int(mid_index)]

        if guessvalue == searchvalue:

            return mid_index

        if guessvalue > searchvalue:

            high_index = mid_index - 1

        if guessvalue < searchvalue:

            low_index = mid_index + 1

    return None

 

mylist = [1,2,3,4,5,6,7,8,9]

print(binary_search(mylist, 9))

 

算法的運行時間應該選擇效率最高的算法以便最大限度的減少運行時間活佔用空間。

線性時間:最多需要猜測的次數與列表長度相同,即列表包含100個元素需要猜測100次。

對數時間:二分查找的運行時間成爲對數時間或log時間。即列表包含100個元素最多需要猜7次(log2100大約是7)。

 

 

大O表示法(時間複雜度):

大O表示法指出了算法的速度有多快。但是僅知道算法需要多長時間才能運行完還不夠,還需要知道運行時間如何隋列表的長度增長而增加。(簡單查找隨着列表的長度增加運行時間也在增加,而二分查找卻不受此影響)

運行時間爲O(n),單位並不是秒。大O表示法指的並不是以秒爲單位的速度,而是操作數(可以理解的步數),它指出了算法運行時間的增速。

大O表示法指出了最糟情況下的運行時間。比如在電話簿中找人,簡單查找的運行時間是O(n),即所找的人在最後一個。如果所找的人在第一個,對應的運行時間是O(n)還是O(1)呢?這就解釋了大O表示法在最糟糕的=情況下運行的時間,是一個保證,簡單查找的時間不可能超過O(n)。

除了最糟糕情況下的運行時間還需要考慮平均情況的運行時間。

常見的大O運行時間:

O(log n) 二分查找

O(n) 簡單查找

O(n*log n) 快速排序算法(一種速度較快的排序算法)

O(n2) 選擇排序算法(一種較慢的排序算法)

O(n!) 旅行商問題的解決方案(一種非常慢的算法)

 

旅行商問題:

有這樣一個問題:有一個旅行商需要前往5個城市,同時要確保旅程最短。因此需要對着5個城市進行排列組合(共計120種),每種方式都要計算路線並選出最短路線。如果有6個城市那個就需要執行720次操作(720中排列方式),7個城市5040次操作......推而廣之,n個城市就需要n!(n的階乘)次操作。關於這個問題目前沒有較好的解決方案,只能去找近似答案。

 

總結:

算法的速度並非指時間,而是操作數的增速

談論算法的速度時,我們說的是隨着輸入的增加,其運行時間將以什麼速度增加

算法的運行時間用大O表示

O(log n) 比O(n)塊,當需要搜索的元素增加的時候前者比後者快的越多,元素越多差距越明顯。

 

 

 

第二章

 

內存的工作原理:

理解:假設你去演出需要存兩件東西,寄存處有一個櫃子(內存),裏面有很多抽屜。你將你的兩件東西分別存在兩個抽屜裏面,並知道是哪個抽屜(即地址),這樣就可以了。計算機就像是很多抽屜的集合,每個抽屜有地址。當需要將數據存在內存中時,請求計算機提供存儲空間,並返回給你一個存儲的地址。

 

數組和鏈表:

 

數組:

將數組中的元素存在內存中時位置的相連的。好比說三個人去看電影肯定會坐在三個連着的位置。那如果這時候第四個人打電話說要過來,而此時左右兩邊有都沒有空位置,那個就需要重新找有四個位置的位置,之前的三個人都需要動(移動內存),很麻煩。一種解決辦法是’預留位置’,即請求內存提供10個位置,以便添加新來的人,但是會出現兩個問題:1當沒有新人來得時候剩下的位置將會浪費(浪費內存),2新來的人可能多於7個,這樣10個位置也是不夠用的,仍然需要移動內存。還有一個問題就是加入新來的一個人想要坐在3個人的中間,那麼後邊的人都需要移動,如果沒有足夠的空間那麼4個人就都需要移動(刪除時也是需要內存移動的)。因此:數組在add和del方便表現不佳。

如果我們想知道某一個元素的位置怎麼做呢?數組中每個元素都是有地址的,每個地址在數組中映射爲數組的下標(從0開始),這樣的話很容易知道每個元素的位置。因此:數組在sel時效率很高。

鏈表:

鏈表中的元素可以存在內存的任何地方,每個元素都存儲了下一個元素的地址,從而將一系列隨機的內存地址串在一起。在添加元素是只需要將元素放入內存同時 將地址存儲在前一個元素中(可以理解成多個人同時看電影坐在不同的位置)。因此只要有足夠多的內存就可以爲鏈表分配空間。因此:鏈表在插入元素方面很方便。如果在鏈表中間插入元素只需要修改前面元素指向的地址(刪除同理),也是很方便的。

鏈表在讀取元素時不能直接讀取最後一個元素,因爲不知道它的地址,所以必須先訪問前一個元素,而前一個元素的地址又在前前一個元素總存儲,知道讀取第一個時。所以鏈表在讀取數據時總是先讀取第一個元素才能知道後面的。因此:如果需要跳躍讀取,鏈表的效率比較低。但是如果讀取整個列表時效率還是挺高的。

兩者的運行時間:O(n)=線性時間  O(1)常量時間

 

選擇排序算法:

解釋及原理:有一個無序的數組,通過此算法可以將其變有序。思路:遍歷整個數組先找出最小的,添加進新數組同時在元素組中刪除,然後重複這個步驟直到最後一個元素。這樣做並非每次都需要檢查n(數組的長度)個元素,而是在第一次檢查完n個元素之後,之後的檢查的元素依次爲n-1,n-2,.......,1。平均每次檢查1/2*n,因此運行時間是O(n*1/2*n),但是大O表示法省略諸如1/2這樣的常數,因此簡單寫作O(n2)。選擇排序速度不是很快,但卻很靈巧。(本辦法是每次循環整個數組找出最小的,第二小的,....,然後添加到新數組,這樣的運行時間就是O(n2))

 

Python實現:

 

def findSmallest(array):

    # 找出最小的元素的下標

    smallest = array[0]

    smallestindex = 0

    for i in range(1, len(array)):

        if array[i] < smallest:

            smallest = array[i]

            smallestindex = i

    return smallestindex

 

def selectSort(array):

    newarray = []

    for a in range(len(array)):

        smallestindex = findSmallest(array)

        newarray.append(array.pop(smallestindex))

    return newarray

 

if __name__ == '__main__':

    print(selectSort([1,3,5,7,6,4,8,2,9]))

 

 

第三章

遞歸:

解釋及原理:假設一個盒子中有你想要找的東西,而盒子中又有盒子,盒子中的盒子又有盒子,那個你是用什麼辦法去找你想要的東西呢?一個辦法是:創業一個盒子堆並在裏面找一個盒子進行查找,如果是盒子就放回盒子堆一遍後邊查找,如果是你想的東西那麼就大功告成(while實現)。另一個辦法是:檢查盒子,如果還是盒子就再檢查盒子,如果是你想要的東西那麼就大功告成(遞歸實現,自己調用自己)。

 

 

這兩種方法的作用其實是一樣的,只不過使用while性能可能更高,使用遞歸可能使程序更容易理解。

Python實現:

 

def digui(number):

    print(number)

    if number <= 1:     # 基線條件:函數不能再調用自己,避免死循環

        return

    else:     # 遞歸條件:函數調用自己

        digui(number -1)

 

if __name__ == '__main__':

    digui(5)

 

棧:

這部分原文比較好理解,直接貼圖:

 

小編寫到這就不想寫了,一方面是因爲這本書確實挺好,之後的章節書裏面講解的非常清楚,無序再挑挑揀揀,另一方面是因爲最近工作上的活比較多,所以暫時放下,有時間再進行更新。我建議大家直接看書,有基礎的同學在前幾張看起來會感覺比較冗餘,但是確實有助於理解消化,想看書的朋友也可聯繫小編要書。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章