1. 程式人生 > >資料結構與演算法基礎(基於python)

資料結構與演算法基礎(基於python)

用大O法表示執行時間,log都表示log2(以2為底的對數)
所有的演算法都是基於python寫的

一、二分查詢法:

1、輸入:一個有序的元素列表
2、輸出:如果要查詢的元素包含在列表中,二分查詢返回其位置,否則返回NULL.
3、使用二分查詢,每次排除一半的數字
4、演算法過程:檢查中間元素,若小了,就改變low,如果大了就修改high
5、完整程式碼如下:

def binary_search(list,item):
    low=0
    high=len(list)-1

    while low<=high:      #只要範圍沒有縮小到只剩一個元素
mid=(low+high)/2 guess=list[mid] if guess==item: return mid if guess > item: high=mid-1 if guess < item: low=mid+1 return None //測試一下 mylist=[1,3,5,7,9] print binary_search(mylist,3) print binary_search(mylist,-1)

6、執行時間:

大O表示法,指出了最糟情況下的執行時間
①簡單查詢:O(n)
②二分查詢:O(log n)
③快速排序:O(n*log n)
④選擇排序:O(n^2)
⑤旅行商問題:一種非常慢的演算法O(n!)
旅行商問題:找最短路徑

二、選擇排序

1、涉及到資料結構——陣列和連結串列

①陣列:記憶體單位相連,固定好了一定的記憶體空間,如果不夠不可以臨時加,只能重新劃定一個連續記憶體空間
②連結串列:元素可儲存在記憶體的任何地方,每一個元素都儲存了下一個元素的地址,優勢在插入元素方面。但是當你要訪問某一個元素時,你就得從連結串列頭開始一個個訪問過去,對於跳躍真的很不方便,因為你不知道後面的元素的地址,只能一個個往後推著走。

執行時間 陣列 連結串列
讀取 O(1) O(n)
插入 O(n) O(1)
刪除 O(n) O(1)

2、插入(刪除)

①使用連結串列插入,只需要改變前面那個元素指向的地址,通常都記錄了連結串列的第一個元素和最後一個元素
②使用陣列,則必須將後面的元素都向後移,這樣感覺會很麻煩,有時候要是數目大於陣列的已劃分的記憶體的長度,那就得複製到一個新的陣列中了
③連結串列只能順序訪問,陣列支援隨機訪問
所以,插入用連結串列,刪除同理

3、選擇排序思想:

每次從剩下的元素中查詢到最大(或最小的元素),之後將元素挑出來放在新列表中接下去的位置中。

4、程式碼如下:

def findSmallest(arr):
    smallest = arr[0]  #用於儲存最小的值
    smallest_index=0   #用於儲存最小的索引
    for i in range(1,len(arr)):
        if arr[i]<smallest:
            smallest=arr[i]
            smallest_index=i
        return smallest_index

def selectionSort(arr):
    newArr=[]
    for i in range(len(arr)):
        smallest=fomdSmallest(arr)
        newArr.append(arr.pop(smallest))#找出陣列中最小的元素,並將其加入到新陣列中,從原來的陣列中pop出來
    return newArr

print selectionSort([5,3,6,2,10,85])

三、快速排序

1、分而治之D&C

2、遞迴——讓解決方案更清晰

遞迴指的是函式呼叫自己,相關資料結構—棧(壓棧和出棧),每次呼叫函式時,計算機都將函式呼叫所涉及到的所有變數的值儲存在記憶體中,如果陷入沒完沒了的遞迴會導致棧溢位。

3、快速排序思想:

①找一個元素作為基準值
②找出比基準值小的元素還有比基準值大的元素,得到——一個由所有小於基準值的數字組成的子陣列 + 基準值 + 一個由所有大於基準值的數字組成的子陣列
③對子陣列繼續進行排序(遞迴呼叫)
歸納證明:歸納條件和基線條件

4、程式碼如下:

#O(n log n)
def quicksort(array):
    if len(array) <2:
        return array   #基線條件:為空或者只包含一個元素的陣列(有序)
    else:
        plvot=array[0]
        less=[i for i in array[1:] if i<=plvot]
        greater=[i for i in array[1:] if i > plvot]
        return quicksort(less)+[plvot]+quicksort(greater)

print quicksort([10,5,2,6,8,9])

5、關於大O時間

①最佳/平均:O(log n)
②最差:O(n^2)

四、散列表—dict(),由鍵和值組成

1、雜湊函式

①雜湊函式總是將同樣的輸入對映到相同的索引,每次取相同已知數的值的函式值時都能得到一樣的函式值結果
②雜湊函式將不同的輸入對映到不同的索引
③雜湊函式知道陣列多大,只返回有效的索引

2、操作散列表

①建立 number=dict() 或者 number={} ( 一對大括號 )
②新增元素number[“Marry”]=8945968
域名 → IP 就是用散列表
③散列表可以防止重複(鍵)
④將散列表用作快取:網站將資料記住,不再需要重新計算獲取。
⑤當訪問一個網站頁面時,它會先檢查散列表中是否儲存了該頁面。

cache={}  #建立散列表

def get_page(url):
    if cache.get(url):    #散列表的get方法
        return cache[url]
    else:
        data=get_data_from_server(url)
        cache[url]=data
        return data

⑥散列表用於模擬對映關係
⑦衝突:如果兩個鍵對映到了同一個位置,就在這個位置儲存一個連結串列
⑧好的雜湊函式應該做到將鍵均勻地對映到散列表的不同位置
⑨效能:在平均情況下,散列表執行各種操作的時間都是O(1),是常量時間,並不意味著馬上,而是說不管散列表多大,所需的時間都一樣

散列表 平均情況 最糟情況 陣列 連結串列
查詢 O(1) O(n) O(1) O(n)
插入 O(1) O(n) O(n) O(1)
刪除 O(1) O(n) O(n) O(1)

⑩填裝因子=散列表包含的元素數 / 位置總數,用於度量散列表中有多少位置是空的,填裝因子大於1意味著元素數量超過了陣列的位置數。填裝因子越低,散列表效能越高。
均勻分佈:即儘量讓不同的鍵值擁有不同的雜湊值

五、廣度優先遍歷—圖演算法,

1、作用:非加權圖的最短路徑

①從節點A出發,有前往節點B的路徑嗎?
②從節點A出發,前往節點B的哪條路徑最短?

2、廣度優先遍歷思想:

首先在一度關係中搜索,確定一度關係中沒有結論後才在二度關係中進行搜尋,搜尋範圍從起點開始逐漸向外延伸

3、涉及到的資料結構—佇列:入隊、出隊 + 散列表(用於對映)

佇列的操作
queue() 定義一個空佇列,無引數,返回值是空佇列。
enqueue(item) 在佇列尾部加入一個數據項,引數是資料項,無返回值。
dequeue() 刪除佇列頭部的資料項,不需要引數,返回值是被刪除的資料,佇列本身有變化。
isEmpty() 檢測佇列是否為空。無引數,返回布林值。
size() 返回佇列資料項的數量。無引數,返回一個整數。
deque() 雙向佇列

4、程式碼如下:

from collections import deque

def search(name):
    search_queue = deque()
    search_queue += graph[name]
    searched=[]   #用於記錄檢查過的人的陣列
    while search_queue:
        person=search_queue.popleft()  #獲取最左邊一個元素,並在佇列中刪除
        if not person in searched:
            if person_is_seller(person):
                print person + " is a mango seller!"
                return true
            else:
                search_queue += graph[person]  #可能是個陣列
                searched.append(person)
    return False

graph={}  #建立一個散列表
graph["you"]=["alice","bob","cici"]
graph["alice"]=["peggy"]
graph["bob"]=["ann"]
........   #表示圖的鄰居關係,一度關係
search["you"]

5、執行時間:O(V + E)

V:表示頂點數,E表示邊數

六、狄克斯特拉演算法 Dijkstra

1、作用:

找出權重最小的路徑

2、過程:

①找出最便宜的節點,即在最短時間內可以前往的節點
②對於該節點的鄰居,檢查是否有前往它們的更短路徑,如果有,更新開銷
③重複這個過程,直到對圖中的每個節點都這樣做了
④計算最終路徑
負權邊不適合用狄克斯特拉演算法,包含負權邊應該使用貝爾曼-福德演算法

3、涉及資料結構:散列表

4、程式碼如下:

def find_lowest_cost_node(costs):
    lowest_cost=float("lnf")
    lowest_cost_node=None
    for node in costs:
        cost=costs[node]
        if cost < lowest_cost and node not in processed:  #如果當前節點的開銷更低且未經過處理
            lowest_cost=cost  #將其視為開銷最低的節點
            lowest_cost_node=node
    return lowest_cost_node


node = find_lowest_cost_node(costs)#在未處理的節點中找出開銷最小的節點
while node is not None:
    cost =costs[node]
    neighbors=graph[node]
    for n in neighbors.keys():       #遍歷所有的鄰居節點
        new_cost=cost + neighbors[n]
        if costs[n] >new_costs:   #如果經當前節點前往該鄰居更近
            costs[n]=new_cost     #更新該鄰居的開銷
            parents[n]=node      #同時將該鄰居的父節點設定為當前節點
    processed.append(node)         #將當前節點加入到處理佇列
    node=find_lowest_cost_node(costs)         #找出接下來要處理的節點

七、貪婪演算法

八、動態規劃

1、將問題分成小問題,並先著手解決小問題,每個動態規劃解決方案都設計網格。

九、K最近鄰演算法—KNN

1、推薦電影?根據喜好?
2、計算兩點的距離,可以使用畢達哥斯拉公式
3、涉及機器學習:
① OCR:光學字元識別 ——人臉識別、語音識別
② 垃圾郵箱過濾器—樸素貝葉斯分類器
4、KNN用於分類和迴歸,需要考慮最近的鄰居。分類就是編組,迴歸就是預測結果

十、其他

1、數:二叉樹、B樹(平衡樹)
B樹、平衡樹是什麼?效能比較好的樹
2、反向索引:搜尋引擎、搜尋
3、傅立葉變換:分析成分,處理訊號、壓縮音樂;
4、並行演算法
5、歸併函式
6、布隆過濾器
7、SHA演算法
8、Diffie-Hellman演算法:公開金鑰演算法,非對稱
9、線性規劃:Simplex演算法