算法介紹

1.概念

  算法:一個計算過程(函數),或者說是解決問題的方法可以理解成一個算法

  時間複雜度:用來估算算法運行時間的一個式子(單位)。一般來說,時間複雜度高的算法比複雜度低的算法慢

  空間複雜度:用來估算算法佔用內存的一個式子

1.1 常見時間複雜度按照效率排序

  O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n2logn)<O(n3)

  一般來說

      循環減半的過程,時間複雜度通常是O(logn)

      幾次循環,時間複雜度就是n的幾次方

1.2 案例

"""
一段有n個臺階組成的樓梯,小明從樓梯的最底層向最高處前進,它可以選擇一次邁一級臺階或者一次邁兩級臺階。問:他有多少種不同的走法?
"""

思路:

  該題其實也是一個斐波那契數列,f(n) = f(n-1)+f(n-2)

def steps(n):
    if n <= 2:
        return n
    else:
        return steps(n-1) + steps(n-2)

print(steps(10))

1.3 二分排序法(時間複雜度O(logn))

  這裏針對的是一個有序列表,一般是在算法中輸入該列表中某個數,然後輸出這個數在列表中的位置,即索引

示意圖:

代碼:

def bin_search(key, lis):
    low = 0     #
    high = len(lis) - 1     #
    while low < high:
        mid = (low + high) // 2
        if lis[mid] == key:    # 命中
            return mid
        elif lis[mid] > key:  # key在左邊,說明key的範圍在low到mid-1之間
            high = mid - 1      
        elif lis[mid] < key:  # key在右邊,說明key的範圍在high+1到high之間
            low = mid + 1

利用遞歸實現二分查找:

def bin_search_2(key, lis, low, high):
    if low <= high:
        mid = (low + high) // 2
        if lis[mid] == key:
            return mid
        elif lis[mid] > key:    # 在左邊,說明key在low和mid-1之間
            return bin_search_2(key,lis,low, mid-1)
        else:                   # 在右邊,說明key在mid+1和high之間
            return bin_search_2(key,lis,mid+1,high)
    else:
        return

 對於一個二維列表實現的二分查找:

li = [
    [1, 3, 5, 7],
    [9,10,14,17],
    [18,22,25,30]
]

代碼:

def binary_search_2d(li, val):
    left = 0
    m = len(li)
    n = len(li[0])
    right = m * n - 1
    while left <= right:    # 候選區非空
        mid = (left + right) // 2
        i = mid // n       # 取整
        j = mid % n        # 取餘
        if li[i][j] == val:
            return i, j
        elif li[i][j] < val:
            left = mid + 1  # 更新候選區
        else: # >
            right = mid - 1 # 更新候選區
    return None

print(binary_search_2d(li, 22))

2. 排序入門篇

2.1 冒泡排序(時間複雜度O(n**2))

  針對的是列表中的兩個相鄰的數,如果前邊比後面的大,則交換這兩個數,如此持續進行

  代碼關鍵點在於幾趟以及無序區

示例代碼:

import random

def bubble_sort(li):
    for i in range(len(li)-1): # i表示第i趟
        for j in range(len(li)-i-1): # j表示箭頭的下標
            if li[j] > li[j+1]:
                li[j], li[j+1] = li[j+1], li[j]
        print(li)
            
                
li = list(range(10))
random.shuffle(li)
bubble_sort(li)

 以上代碼忽視了極端情況,從運行效率上來講,我們還可以在做優化,排除已經是有序的列表

import random

def bubble_sort_2(li):
    for i in range(len(li)-1): # i表示第i趟
        exchange = False
        for j in range(len(li)-i-1): # j表示箭頭的下標
            if li[j] > li[j+1]:     # 如果前面比後面大
                li[j], li[j+1] = li[j+1], li[j]
                exchange = True
        if not exchange:
            return
        print(li)

li = list(range(10))
random.shuffle(li)
bubble_sort_2(li)

2.2 選擇排序(時間複雜度O(n**2))

  一趟遍歷記錄最小的數,放到第一個位置;

  再一趟遍歷記錄剩餘列表中最小的數,繼續放置;

  關鍵點在於無序區以及最小數的位置

代碼:

import random

def select_sort(li):
    for i in range(len(li)-1):  # i表示趟數
        min_pos = i # 最小值默認爲無序區第一個數 i
        for j in range(i+1, len(li)):
            if li[j] < li[min_pos]:
                min_pos = j
        li[i], li[min_pos] = li[min_pos], li[i]


li = list(range(100))
random.shuffle(li)
select_sort(li)
print(li)

2.3 插入排序(時間複雜度O(n**2))

  列表被分爲有序區和無序區兩個部分。最初有序區只有一個元素。

  每次從無序區選擇一個元素,插入到有序區的位置,直到無序區變空。(類似我們玩紙牌)

  代碼關鍵點在於我們摸到的牌,以及手裏的牌

 代碼:

import random

def insert_sort(li):
    for i in range(1, len(li)): # i表示摸到牌的位置,也表示趟
        j = i - 1       # j是當前用來比較的牌
        tmp = li[i]     # 摸到的牌
        while j >= 0 and li[j] > tmp:   # 摸到的牌和他前面位置(前面位置必須大於等於0)的牌作比較
            li[j+1] = li[j]     # 前面的牌比手裏的大,把它往後移
            j -= 1      # 再去看前面那張牌
        li[j+1] = tmp   # 用來比較的牌比摸到的牌小,直接放到j+1的位置


li = list(range(100, -1, -1))
random.shuffle(li)
insert_sort(li)
print(li)

3. 排序升級篇

3.1 快排(時間複雜度O(n*log(n)))      --->算法裏面簡單,也是必須要掌握的一個

  取一個元素p(第一個元素),使元素p歸位;

  列表被p分成兩部分,左邊都比p小,右邊都比p大;

  遞歸完成排序。

  該算法的關鍵點是先對數組進行整理,然後再做遞歸操作

代碼第一部分

def quick_sort(lis, left, right):
    if left < right:
        mid = partition(lis, left, right)  # 返回中間這個值
        quick_sort(lis, left, mid - 1)      # 對左邊的進行排序
        quick_sort(lis, mid + 1, right)     # 對右邊的進行排序

但是怎麼對它做p歸位處理呢?這裏我們先取出第一個數,即5,5的空位需要從右邊往左找一個比5小的數填充,即2

2放在了5的空位上,現在需要再從左邊取一個比5大的數放在2的空位上,即7

7放在了2的空位上,現在需要再從右邊取一個比5小的數放在7的空位上,即1

---

代碼第二部分(做歸位處理)

def partition(lis, left, right):
    tmp = lis[left]  # 先取出最左邊這個數,存起來
    while left < right:  # 從右邊找比tmp小的數
        while left < right and lis[right] >= tmp:
            right -= 1  # 如果右邊的數總比取出的數大,則往左前進一位繼續找直到找見爲止
        lis[left] = lis[right]  # 找見比tmp小的數,此時插入到左邊空出的那個位置
        while left < right and lis[left] <= tmp:  # 從左邊找比tmp大的數
            left += 1  # 如果左邊的數總比取出的數小,則往右前進一位繼續找直到找見爲止
        lis[right] = lis[left]  # 找見比tmp大的數,此時插入到右邊空出的那個位置
    lis[left] = tmp  # 再把中間這個值tmp寫回來
    return left  # 返回中間這個值,left,right最後重合在一起了


lis = [5, 7, 4, 6, 3, 1, 2, 9, 8] partition(lis, 0, len(lis) - 1) print(lis) # [2, 1, 4, 3, 5, 6, 7, 9, 8] # 可以看出4歸位

以上是我們的基本思想,但是如果不從最左邊取這個tmp,而是從中任意取一個數作爲我們的tmp值呢?

代碼:

import random


def partition(li, left, right):
    i = random.randint(left, right)  # 從中取一個隨機數i
    li[left], li[i] = li[i], li[left]  # 把它調換位置放在最左邊
    tmp = li[left]
    while left < right:
        while left < right and li[right] >= tmp:
            right -= 1
        li[left] = li[right]
        while left < right and li[left] <= tmp:
            left += 1
        li[right] = li[left]
    li[left] = tmp
    return left


def quick_sort(lis, left, right):
    if left < right:
        mid = partition(lis, left, right)  # 返回中間這個值
        quick_sort(lis, left, mid - 1)  # 對左邊的進行排序
        quick_sort(lis, mid + 1, right)  # 對右邊的進行排序


lis = list(range(10000))
random.shuffle(lis)
quick_sort(lis, 0, len(lis) - 1)
print(lis)

3.2 堆排(最複雜的一個,利用二叉樹)

3.2.1 二叉樹

 

3.3 歸併排序

 

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