常見演算法

来源:https://www.cnblogs.com/guokaifeng/archive/2020/05/05/12832637.html
-Advertisement-
Play Games

基礎演算法 [TOC] 演算法基礎 演算法的定義和特征 演算法設計的要求 演算法的時間複雜度 二分查找 冒泡排序 選擇排序 python 代碼 將亂序中的最大值找出,跟最後一個元素交換位置 def sort(alist): max_index = 0 最大值的下標 for i in range(1,len(a ...


基礎演算法

目錄

演算法基礎

  • 演算法的定義和特征

# 一個計算過程,解決問題的方法
- 解題方案的準確而完整的描述,是一系列解決問題的清晰指令,演算法代表著用系統的方法描述解決問題的策略機制,
能夠對一定規範的輸入,在有限時間內獲得所要求的輸出,如果一個演算法有缺陷,或不適合於某個問題,執行這個演算法
將不會解決這個問題,不同的演算法可能用不同的時間、空間或效率來完成同樣的任務。一個演算法的優劣可以用空間複雜度與時間複雜度來衡量.
# 特征
- 有窮性:演算法的有窮性是指演算法必須能在執行有限個步驟之後終止
- 確切性:演算法的每一步驟必須有確切的定義
- 輸入項:一個演算法有0個或多個輸入,以刻畫運算對象的初始情況,所謂0個輸入是指演算法本身定出了初始條件
- 輸出項:一個演算法有一個或多個輸出,以反映對輸入數據加工後的結果,沒有輸出的演算法是毫無意義的;
- 可行性:演算法中執行的任何計算步驟都是可以被分解為基本的可執行的操作步,即每個計算步都可以在有限時間內完成(也稱之為有效性)。
  • 演算法設計的要求

- 確定性: 指的是演算法至少應該有輸入,輸出和加工處理無歧義性,能正確反映問題的需求,能夠得到問題的正確答案。
- 確定性大體分為四個層次:
    1.演算法程式無語法錯誤;
    2.演算法程式對於合法的輸入產生滿足要求的輸出;
    3.對於非法輸入能夠產生滿足規格的說明;
    4.演算法程式對於故意刁難的測試輸入都有滿足要求的輸出結果。

- 可讀性: 程式便於閱讀,理解交流。
- 健壯性: 當輸入數據不合法時,演算法也能作出相關處理,而不是產生異常,崩潰或者莫名其妙的結果。時間效率高和存儲量低
  • 演算法的時間複雜度

# 定義
- 在進行演算法分析時,語句總的執行次數T(n)是關於問題規模n的函數,進而分析T(n)隨n的變化情況並確定T(n)的數量級.
- 演算法的時間複雜度,也就是演算法的時間量度,記作:T(n)=0(f(n))
- 它表示隨問題規模n的增大,演算法執行時間的埔長率和 f(n)的埔長率相同,稱作演算法的漸近時間複雜度,簡稱為時間複雜度。
其中f( n)是問題規橫n的某個函數.

# 求解演算法的時間複雜度的具體步驟
- 找出演算法中的基本語句 (演算法中執行次數最多的那條語句就是基本語句,通常是最內層迴圈的迴圈體)
- 計算基本語句的執行次數的數量級(只需計算基本語句執行次數的數量級,這就意味著只要保證基本語句執行次數的函數中的最高次冪正確即可,
可以忽略所有低次冪和最高次冪的繫數。這樣能夠簡化演算法分析,並且使註意力集中在最重要的一點上:增長率)
- 用大Ο記號表示演算法的時間性能(將基本語句執行次數的數量級放入大Ο記號中)

# 推導大o階的基本的推導方法:
- 用常數1取代運行時間中的所有加法常數。
- 在修改後的運行次數函數中,只保留最髙階項。
- 如果最高階項存在且不是1,則去除與這個項相乘的常數。簡單的說,就是保留求出次數的最高次冪,並且把繫數去掉。 如T(n)=n2+n+1 =O(n2)

#複雜度O(1) print("this is wd") 
#複雜度O(n) for i in range(n): print(i) 
#複雜度O(n2) for i in range(n): for j in range(n): print(j) 
#複雜度O(n3) for i in range(n): for j in range(n): for k in range(n): print('wd') 
#複雜度O(log2n) while n > 1: print(n) n = n // 2

#  常見的複雜度按效率排序:O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(2nlogn)<O(n2) 
  • 演算法空間複雜度

# 空間複雜度(Space Complexity)是對一個演算法在運行過程中臨時占用存儲空間大小的量度(三個方面)
- 包括存儲演算法本身所占用的存儲空間(存儲演算法本身所占用的存儲空間與演算法書寫的長短成正比,要壓縮這方面的存儲空間,就必須編寫出較短的演算法)
- 演算法的輸入輸出數據所占用的存儲空間(演算法的輸入輸出數據所占用的存儲空間是由要解決的問題決定的,
是通過參數表由調用函數傳遞而來的,它不隨本演算法的不同而改變)
- 演算法在運行過程中臨時占用的存儲空間(演算法在運行過程中臨時占用的存儲空間隨演算法的不同而異,有的演算法只
需要占用少量的臨時工作單元,而且不隨問題規模的大小而改變,這種演算法是節省存儲的演算法;有的演算法需要
占用的臨時工作單元數與解決問題的規模n有關,它隨著n的增大而增大,當n較大時,將占用較多的存儲單元)
- 具體表述
    1.當一個演算法的空間複雜度為一個常量,即不隨被處理數據量n的大小而改變時,可表示為O(1);
    2.當一個演算法的空間複雜度與以2為底的n的對數成正比時,可表示為0(log2n);
    3.當一個演算法的空間複雜度與n成線性比例關係時,可表示為0(n).若形參為數組,則只需要為它分配一個存儲由實
    4.參傳送來的一個地址指針的空間,即一個機器字長空間;若形參為引用方式,則也只需要為其分配存儲一個地
    5.址的空間,用它來存儲對應實參變數的地址,以便由系統自動引用實參變數。

排序二叉樹

# 使用二叉樹 alist = [3,8,5,7,6,2,9,4,1] 進行排序
亂序數據的插入的時候,需要遵從一個準則:
- 插入的第一個數值作為樹的根節點
- 後序插入的數值,如果比根節點小,插入根節點的左側,否則插入到根節點的右側
- 使用深度優先中序遍歷

# 代碼
class Node():
    def __init__(self,item):
        self.item = item
        self.left = None
        self.right = None  
class SortTree():
    def __init__(self):
        self.root = None
    def add(self,item):
        node = Node(item)
        cur = self.root
        if self.root == None:
            self.root = node
            return
        while cur:
            #向右側插入
            if item > cur.item:
                if cur.right == None:
                    cur.right = node
                    break
                else:
                    cur = cur.right

            else:#向左插入
                if cur.left == None:
                    cur.left = node
                    break
                else:
                    cur = cur.left
                    
    def middle(self,root):#中序:左根右
        if root == None:
            return
        self.middle(root.left)
        print(root.item)
        self.middle(root.right)
        
tree = SortTree()
alist = [3,8,5,7,6,2,9,4,1]
for i in alist:
    tree.add(i)
tree.middle(tree.root)  
# 執行結果
1
2
3
4
5
6
7
8
9

二分查找

# 一定是基於有序集合的查找
- 有序列表對於我們的實現搜索是很有用的。在順序查找中,當我們與第一個元素進行比較時,
如果第一個元素不是我們要查找的,則最多還有 n-1 個元素需要進行比較。 二分查找則是從中間元素開始,
而不是按順序查找列表。 如果該元素是我們正在尋找的元素,我們就完成了查找。 如果它不是,我們可以
使用列表的有序性質來消除剩餘元素的一半。如果我們正在查找的元素大於中間元素,就可以消除中間元素
以及比中間元素小的一半元素。如果該元素在列表中,肯定在大的那半部分。然後我們可以用大的半部分重覆該過程,
繼續從中間元素開始,將其與我們正在尋找的內容進行比較。

# 二分查找原理
- 隨便想一個1~100的數字。
你的目標是以最少的次數猜到這個數字。你每次猜測後,我會說小了、大了或對了。
從50 開始,小了,但排除了一半的數字!至此,你知道1~50都小了。接下來,你猜75。大了,那餘下的數字又排除了一半!
使用二分查找時,你猜測的是中間的數字,從而每次都將餘下的數字排除一半。

#  需要搜索的元素越多,二分查找的速度比普通遍歷查找就快得多。
#  二分查找O(log n) 比普通遍歷查找O(n)時間複雜度低。
# 代碼
def find(alist,item):
    find = False
    first = 0
    last = len(alist)-1
    
    while first <= last:
        mid_index = (first + last) // 2
        
        if alist[mid_index] < item:#去中間元素的右側繼續去尋找
            first = mid_index + 1
        elif alist[mid_index] > item:
            last = mid_index - 1
        else:
            find = True
            break
    return find,mid_index

alist = [1,2,3,4,5,6,7,8,9]
print(find(alist,1))
# 執行結果
True

冒泡排序

# 原理
- 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
- 對每一對相鄰元素做同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。 
- 針對所有的元素重覆以上的步驟,除了最後一個。 
- 持續每次對越來越少的元素重覆上面的步驟,直到沒有任何一對數字需要比較。

# 代碼

#列表元素兩兩比較,大的值逐漸向後移動
def sort(alist):
    for i in range(len(alist)-1):
        if alist[i] > alist[i+1]:
            alist[i],alist[i+1] = alist[i+1],alist[i]
    return alist
alist = [3,8,5,7,6]
print(sort(alist))
# 執行結果 [3, 5, 7, 6, 8]

# 逐漸將亂序序列的最大值找出放置在亂序序列的尾部
def sort(alist):
    for j in range(len(alist)-1):
        for i in range(len(alist)-1-j):
            if alist[i] > alist[i+1]:
                alist[i],alist[i+1] = alist[i+1],alist[i]
    return alist

alist = [3,8,5,7,6]
print(sort(alist))   

# 執行結果 [3, 5, 6, 7, 8]

# 什麼時候最快
當輸入的數據已經是正序時(都已經是正序了,我還要你冒泡排序有何用啊)。

# 什麼時候最慢
當輸入的數據是反序時(寫一個 for 迴圈反序輸出數據不就行了,幹嘛要用你冒泡排序呢,我是閑的嗎)。

選擇排序

# 原理
- 選擇排序,可以理解為改進了冒泡排序,每次遍歷列表只做一次交換。為了做到這一點,一個選擇排序在他遍歷時尋找最大的值(最小值),
併在完成遍歷後,將其放置在正確的位置(小的在前,大的在後)。直到所有元素均排序完畢。

# 代碼
# 將亂序中的最大值找出,跟最後一個元素交換位置
def sort(alist):
    max_index = 0 #最大值的下標
    for i in range(1,len(alist)):
        if alist[max_index] < alist[i]:
            max_index = i
    alist[len(alist)-1],alist[max_index] = alist[max_index],alist[len(alist)-1]
    return alist
# 執行結果 [3, 6, 5, 7, 8]

# 依次將亂序中的最大值找出,跟最後一個元素交換位置
def sort(alist):
    for j in range(0,len(alist)-1):
        max_index = 0 # 最大值的下標
        for i in range(1,len(alist)-j):
            if alist[max_index] < alist[i]:
                max_index = i
        alist[len(alist)-1-j],alist[max_index] = alist[max_index],alist[len(alist)-1-j]
    return alist

alist = [3,8,5,7,6]
print(sort(alist))
# 執行結果 [3, 5, 6, 7, 8]

# 選擇排序是一種簡單直觀的排序演算法,無論什麼數據進去都是 O(n²) 的時間複雜度。所以用到它的時候,
數據規模越小越好。唯一的好處可能就是不占用額外的記憶體空間了吧。

插入排序

# 原理
將第一待排序序列第一個元素看做一個有序序列,把第二個元素到最後一個元素當成是未排序序列。從頭到尾依次掃描未排序序列,
將掃描到的每個元素插入有序序列的適當位置。(如果待插入的元素與有序序列中的某個元素相等,則將待插入元素插入到相等元素的後面。)

# i有序子集中元素的個數,還可以表示下標
# alist[i]亂序子集中的第一個元素
# alist[i-1]有序子集中的最後一個元素

# 實現思路
i = 1 
if alist[i] < alist[i-1]:# 亂序子集中的第一個元素值小於有序子集中的最後一個元素的值,交換位置
    alist[i],alist[i-1] = alist[i-1],alist[i]
else:
    pass
i += 1

i = 2
while i > 0: # 有序子集個數大於0時,執行迴圈 
    if alist[i] < alist[i-1]: 
        alist[i],alist[i-1] = alist[i-1],alist[i]
        i -= 1
    else:
        break
        
# 完整代碼
def sort(alist):
    for i in range(1,len(alist)): # 控制遍歷次數,(len(alist)-1)就可以完成排序
        while i > 0:
            if alist[i] < alist[i-1]:
                alist[i],alist[i-1] = alist[i-1],alist[i]
                i -= 1
            else:
                break
    return alist

alist = [3,8,5,7,6]
print(sort(alist))

# 執行結果 [3, 5, 6, 7, 8]

希爾排序

希爾排序(Shell Sort)是插入排序的一種。也稱縮小增量排序,是直接插入排序演算法的一種更高效的改進版本,
該方法的基本思想是:先將整個待排元素序列分割成若幹個子序列(由相隔某個“增量(gap)”的元素組成的)
分別進行直接插入排序,然後依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,
再對全體元素進行一次直接插入排序。因為直接插入排序在元素基本有序的情況下(接近最好情況),效率是很高的,
因此希爾排序在時間效率比直接插入排序有較大提高。

希爾排序,也稱遞減增量排序演算法,是插入排序的一種更高效的改進版本。但希爾排序是非穩定排序演算法。
- 希爾排序是基於插入排序的以下兩點性質而提出改進方法的:
1.插入排序在對幾乎已經排好序的數據操作時,效率高,即可以達到線性排序的效率;
2.但插入排序一般來說是低效的,因為插入排序每次只能將數據移動一位;
  希爾排序的基本思想是:先將整個待排序的記錄序列分割成為若幹子序列分別進行直接插入排序,
待整個序列中的記錄"基本有序"時,再對全體記錄進行依次直接插入排序。    

# 代碼
def sort(alist):
    gap = len(alist)//2 #初始gap
    
    while gap >= 1:
        for i in range(gap,len(alist)):
            while i > 0:
                if alist[i] < alist[i-gap]:
                    alist[i],alist[i-gap] = alist[i-gap],alist[i]
                    i -= gap
                else:
                    break
        gap //= 2
    return alist
alist = [3,8,5,7,6]
print(sort(alist))
# 執行結果 [3, 5, 6, 7, 8]

快速排序

# 原理
1.先從數列中取出一個數作為基準數。
2.分區過程,將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊。
3.再對左右區間重覆第二步,直到各區間只有一個數。

# 實現流程
    - 將列表中第一個元素設定為基準數字,賦值給mid變數,然後將整個列表中比基準小的數值放在基準的左側,
比基準到的數字放在基準右側。然後將基準數字左右兩側的序列在根據此方法進行排放。
    - 定義兩個指針,low指向最左側,high指向最右側
    - 然後對最右側指針進行向左移動,移動法則是,如果指針指向的數值比基準小,則將指針指向的數字移動到基準數字原始的位置,
否則繼續移動指針。
    - 如果最右側指針指向的數值移動到基準位置時,開始移動最左側指針,將其向右移動,如果該指針指向的數值大於基準則將該數值
移動到最右側指針指向的位置,然後停止移動。
    - 如果左右側指針重覆則,將基準放入左右指針重覆的位置,則基準左側為比其小的數值,右側為比其大的數值。

def sort(alist,start,end):
    #基數
    low = start
    high = end 
    
    if low > high:
        return
    
    mid = alist[start]
    while low < high:
        #偏移high
        while low < high:
            if alist[high] >= mid:
                #向左偏移high
                high -= 1
            else:
                alist[low] = alist[high]
                break
        #偏移low
        while low < high:
            if alist[low] <= mid:
                low += 1
            else:
                alist[high] = alist[low]
                break
        if low == high:
            alist[low] = mid
            break
    #作用到左側
    sort(alist,start,high-1)
    #左右到右側
    sort(alist,low+1,end)
    return alist

alist = [6,1,2,7,9,3,4,5,10,6,1,8]
print(sort(alist,0,len(alist)-1))

# 執行結果
[1, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10]
作 者:郭楷豐 出 處:https://www.cnblogs.com/guokaifeng/ 聲援博主:如果您覺得文章對您有幫助,可以點擊文章右下角 推薦一下。您的鼓勵是博主的最大動力! 自 勉:生活,需要追求;夢想,需要堅持;生命,需要珍惜;但人生的路上,更需要堅強。帶著感恩的心啟程,學會愛,愛父母,愛自己,愛朋友,愛他人。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 導入相關依賴: 配置資料庫連接信息: 測試連接: 簡單使用示例: ...
  • 1首先建立Clsss類文件memcached.class.php <?phpclass Memcacheds{ //聲明靜態成員變數 private static $m = null; private static $cache = null; public function __construct ...
  • #include <iostream> #include <ctime> #include <vector> #include <algorithm> using std::cout; using std::endl; /* xx排序,空間複雜度,時間複雜度,是否原地排序,是否穩定排序 */ /* ...
  • 需要準備的環境: (1)python3.8 (2)pycharm (3)截取網路請求信息的工具,有很多,百度一種隨便用即可。 第一:首先通過python的sqlalchemy模塊,來新建一個表。 第二:通過python中的request模塊介面的形式調取數據。 思路:(1)先獲取所有城市信息:需要用 ...
  • WAE : Concrete syntax WAE : Abstract syntax parse : sexp WAE subst : WAE symbol number WAE interp : WAE number ...
  • 一、用於數據分析、科學計算與可視化的擴展模塊主要有:numpy、scipy、pandas、SymPy、matplotlib、Traits、TraitsUI、Chaco、TVTK、Mayavi、VPython、OpenCV。 1.numpy模塊:科學計算包,支持N維數組運算、處理大型矩陣、成熟的廣播函 ...
  • Numpy 定義:NumPy(Numerical Python) 是 Python 語言的一個擴展程式庫,支持大量的維度數組與矩陣運算,此外也針對數組運算提供大量的數學函數庫。它主要用於數組計算,包括: 一個強大的N維數組對象 ndarray 廣播功能函數 整合 C/C++/Fortran 代碼的工 ...
  • 其實單調隊列就是一種隊列內的元素有單調性的隊列,因為其單調性所以經常會被用來維護區間最值或者降低DP的維數已達到降維來減少空間及時間的目的。 每一個答案只與當前下標的前m個有關,所以可以用單調隊列維護前m的個最小值, 考慮如何實現該維護的過程?? 顯然當前下標$X$的$m$個以前的元素(即下標小於$ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...