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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

九大排序算法

發布時間:2023/12/13 综合教程 35 生活家
生活随笔 收集整理的這篇文章主要介紹了 九大排序算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、直接插入排序

每次選擇一個元素,并且將這個元素和已經排好序的數組的所有元素進行比較,然后插入到合適的位置

舉例: 38,65,97,76,13,27,49
[38],65,97,76,13,27,49
[38,65],97,76,13,27,49
[38,65,97],76,13,27,49
[38,65,76,97],13,27,49
[13,38,65,76,97],27,49
...
[13,27,38,49,65,76,97]
最好O(N),最壞/平均時間復雜度O(N^2)
空間復雜度O(1)
代碼如下:
def insertion_sort(arr):
    len_ = len(arr)
    for i in range(1,len_):
        tmp = arr[i]
        j = i - 1
        while j >= 0:
            if arr[j] > tmp:#逆序遍歷已排好序的數,如果當前的數比要插入的數大,就將要插入的數前移
                arr[j+1] = arr[j]
                arr[j] = tmp
            j -= 1
    print(arr)

二、希爾排序

這個是插入排序的修改版,根據步長由長到短分組,進行排序,直到步長為1為止,屬于插入排序的一種。

基本原理:首先將待排序的元素分成多個子序列,使得每個子序列的元素個數相對較少,對各個子序列分別進行直接插入排序,待整個待排序序列“基本有序后”再進行一次直接插入排序

平均時間復雜度O(NlogN),最差O(N^s)

空間復雜度O(1)

代碼如下:

# 希爾排序的實質就是分組插入排序,該方法又稱縮小增量排序,因DL.Shell于1959年提出而得名。
# 希爾排序,也稱遞減增量排序算法,是插入排序的一種更高效的改進版本。希爾排序是非穩定排序算法。
# 希爾排序是基于插入排序的以下兩點性質而提出改進方法的:
# 插入排序在對幾乎已經排好序的數據操作時,效率高,即可以達到線性排序的效率
# 但插入排序一般來說是低效的,因為插入排序每次只能將數據移動一位
def shell_sort(arr):
    step = len(arr) // 2 #設定步長
    while step > 0:
        for i in range(step,len(arr)):
            while i >= step and arr[i-step] > arr[i]: #類似插入排序,當前值與指定步長之前的值比較,符合條件則交換位置
                arr[i],arr[i-step] = arr[i-step],arr[i]
                i -= step
        step = step // 2
    return arr

三、冒泡排序

最好O(N),最壞/平均O(N^2)

空間復雜度O(1)

代碼如下:

def bubble_sort(arr):
    len_ = len(arr)
    for i in range(len_):
        j = i
        while j < len_-1:
            if arr[j] > arr[j+1]: #先冒大的
                arr[j],arr[j+1] = arr[j+1],arr[j]
            j += 1
        print(arr)

def bubble_sort1(arr):
    len_ = len(arr)
    for i in range(0,len_):
        for j in range(i+1,len_):
            if arr[i] > arr[j]: #先冒小的
                arr[i],arr[j] = arr[j],arr[i]
        print(arr)
    return arr


res= bubble_sort1(arr=[36,25,48,12,25,65,43,57])
# print(res)

四、快速排序

原理:對于一組給定的記錄,通過一趟排序后,將原序列分為兩部分,其中前部分的所有記錄均比后部分的所有記錄小,然后再一次對前后兩部分的記錄進行快速排序,遞歸該過程,直到序列中的所有記錄均有序為止。

最好O(NlogN):每次都恰好五五分,一次遞歸共需要比較n次,遞歸深度為lgn;

最壞O(N^2):已排序數組,比較次數為

平均O(NlogN):

快排在所有平均時間復雜度為O(NlogN)的算法中,平均性能最好

空間復雜度O(logN)

當初始序列整體或局部有序時,快排的會退化為冒泡排序。

代碼如下:

#分治法
def quick_sort(arr,left,right):
    if left >= right:
        return arr
    pivot = arr[left] #取第一個元素為哨兵
    low = left #保留初始的left和right的值,后面要用
    high = right
    while left < right:
        while left < right and arr[right] >= pivot: #從右向左,找到第一個比pivot小的元素
            right -= 1
        arr[left] = arr[right] #把該右邊值放到左邊位置上
        while left < right and arr[left] < pivot: #從左向右,找到第一個比pivot大的元素
            left += 1
        arr[right] = arr[left] #把該左邊值放到右邊位置上
    arr[right] = pivot #此時,left和right指向同一個位置
    quick_sort(arr,low,left-1)
    quick_sort(arr,right+1,high)
    return arr

arr = [3,4,2,8,9,5,1]
left,right = 0,len(arr)-1
res = quick_sort(arr,left,right)
print(res)

【快排的優化】三數取中(median-of-three)

引入的原因:雖然隨機選取樞軸時,減少出現不好分割的幾率,但是還是最壞情況下還是O(n2),要緩解這種情況,就引入了三數取中選取樞軸

分析:最佳的劃分是將待排序的序列分成等長的子序列,最佳的狀態我們可以使用序列的中間的值,也就是第N/2個數。可是,這很難算出來,并且會明顯減慢快速排序的速度。這樣的中值的估計可以通過隨機選取三個元素并用它們的中值作為樞紐元而得到。事實上,隨機性并沒有多大的幫助,因此一般的做法是使用左端、右端和中心位置上的三個元素的中值作為樞紐元。顯然使用三數中值分割法消除了預排序輸入的不好情形,并且減少快排大約14%的比較次數

舉例:待排序序列為:8 1 4 9 6 3 5 2 7 0

左邊為:8,右邊為0,中間為6.

我們這里取三個數排序后,中間那個數作為樞軸,則樞軸為6

注意:在選取中軸值時,可以從由左中右三個中選取擴大到五個元素中或者更多元素中選取,一般的,會有(2t+1)平均分區法(median-of-(2t+1),三平均分區法英文為median-of-three)。

具體思想:對待排序序列中low、mid、high三個位置上數據進行排序,取他們中間的那個數據作為樞軸,并用0下標元素存儲樞軸。

即:采用三數取中,并用0下標元素存儲樞軸。

取中樞的代碼如下:

#三數取中
#取待排序序列中low、mid、high三個位置上數據,選取他們中間的那個數據作為樞軸

def findMedian(arr,low,high):
    mid = low + (high-low) >> 1

    if arr[mid] > arr[high]: #目標: arr[mid] <= arr[high]
        arr[mid],arr[high] = arr[high],arr[mid]
    if arr[low] > arr[high]: #目標: arr[low] <= arr[high]
        arr[low],arr[high] = arr[high],arr[low]
    if arr[mid] > arr[low]: #目標: arr[low] >= arr[mid]
        arr[low], arr[mid] = arr[mid], arr[low]
    print(arr)
    return arr[low] #low的位置上保存這三個位置中間的值

arr = [1,3,2]
res = findMedian(arr,0,2)
print(res)

測試數據分析:使用三數取中選擇樞軸優勢還是很明顯的,但是還是處理不了重復數組

五、簡單選擇排序

原理:對于給定的一組記錄,經過第一輪比較后得到最小的記錄,然后將該記錄與第一個記錄進行交換;接著對不包括第一個記錄以外的其他記錄進行第二輪比較,得到最小的記錄并與第二個記錄進行位置交換;重復該過程,直到進行比較的記錄只有1個時為止。

舉例: 38,65,97,76,13,27,49

13,[65,97,76,38,27,49]

13,27,[97,76,38,65,49]

...

13,27,38,49,65,76,97

最好最壞時間復雜度O(N^2)

空間復雜度O(1)

代碼如下:

def select_sort(arr):
    len_ = len(arr)
    for i in range(len_):
        min = i
        for j in range(i+1,len_):
            if arr[j] < arr[min]:
                min = j
        arr[min],arr[i] = arr[i],arr[min] #一輪結束再交換
        # print (arr)
    return arr

res = select_sort(arr=[38,65,97,76,13,27,49])
print(res)

六、堆排序

1.堆排序

大頂堆:父節點比子節點大,堆頂元素必為最大值

小頂堆:子節點比父節點大,堆頂元素必為最小值

原理:

對于給定的n個記錄,初始時把這些記錄看作為一棵順序存儲的二叉樹,然后將其調整為一個大頂堆,然后將堆的最后一個元素與堆頂元素(二叉樹根節點)
進行交換后,堆的最后一個元素即為最大記錄;接著將前(n-1)個元素(不包括最大記錄)重新調整為一個大頂堆,再將堆頂元素與當前堆的最后一個元素進行交
換后得到次大的記錄,重復該過程直到調整的堆中只剩下一個元素時為止,該元素即為最小記錄,此時可得到一個有序序列。
兩過程:(1)建堆,自下(最后一個非葉子節點)而上(第一個非葉子節點),自右向左
(2)交換堆頂元素與最后一個元素的位置



---------->>

堆排序過程:

建堆時間復雜度O(n)

  初始化建堆只需要對二叉樹的非葉子節點調用adjusthead()函數,由下至上,由右至左選取非葉子節點來調用adjusthead()函數。那么倒數第二層的最右邊的非葉子節點就是最后一個非葉子結點。
  假設高度為k,則從倒數第二層右邊的節點開始,這一層的節點都要執行子節點比較然后交換(如果順序是對的就不用交換);倒數第三層呢,則會選擇其子節點進行比較和交換,如果沒交換就可以不用再執行下去了。如果交換了,那么又要選擇一支子樹進行比較和交換;高層也是這樣逐漸遞歸。
  那么總的時間計算為:s=2^(i-1)*(k-i);其中i表示第幾層,2^(i-1)表示該層上有多少個元素,(k-i)表示子樹上要下調比較的次數。
  S=2^(k-2)*1+2^(k-3)2…..+2(k-2)+2^(0)*(k-1)===>因為葉子層不用交換,所以i從k-1開始到1;
  S=2^k-k-1;又因為k為完全二叉樹的深度,而log(n)=k,把此式帶入;
  得到:S=n-log(n)-1,所以時間復雜度為:O(n)
-------------------------------------------------------------------------------------------------------
排序重建堆時間復雜度O(NlogN)

  在取出堆頂點放到對應位置并把原堆的最后一個節點填充到堆頂點之后,需要對堆進行重建,只需要對堆的頂點調用adjustheap()函數。
  每次重建意味著有一個節點出堆,所以需要將堆的容量減一。adjustheap()函數的時間復雜度k=log(n),k為堆的層數。所以在每次重建時,隨著堆的容量的減小,層數會下降,函數時間復雜度會變化。重建堆一共需要n-1次循環,每次循環的比較次數為log(i),則相加為:log2+log3+…+log(n-1)+log(n)≈log(n!)。可以證明log(n!)和nlog(n)是同階函數:

初始化建堆的時間復雜度為O(n)排序重建堆的時間復雜度為nlog(n),所以總的時間復雜度為O(n+nlogn)=O(nlogn)。另外堆排序的比較次數和序列的初始狀態有關,但只是在序列初始狀態為堆的情況下比較次數顯著減少,在序列有序或逆序的情況下比較次數不會發生明顯變化。

代碼如下:

#!user/bin/env python3
# -*- coding: gbk -*-
import time
import heapq

def adjust_heap(arr,i,len_):
    # 在堆中做結構調整使得父節點的值大于子節點
    lchild = 2 * i + 1 #左節點
    rchild = 2 * i + 2 #右節點
    maxs = i
    if i < len_ / 2:
        if lchild < len_ and arr[lchild] > arr[maxs]:
            maxs = lchild
        if rchild < len_ and arr[rchild] > arr[maxs]:
            maxs = rchild
        if maxs != i:
            arr[maxs],arr[i] = arr[i],arr[maxs]
            adjust_heap(arr,maxs,len_) #調整maxs下面的子堆

def build_heap(arr,len_):
    for i in range(len_//2,-1,-1): #從最后一個非葉子節點開始到第一個非葉子節點結束
        adjust_heap(arr,i,len_)

def heap_sort(arr):
    len_ = len(arr)
    #step1 建堆
    build_heap(arr,len_) #將原始數組構建成一個堆

    #step2 對堆進行排序
    for i in range(len_-1,-1,-1):
        arr[i],arr[0] = arr[0],arr[i]#將大頂堆的頂點值與最后一個葉子節點交換,這樣,就能得到一個從小到大的排序
        adjust_heap(arr,0,i)  #繼續對剩下的元素進行堆排序,此時,最大值在數組的末尾
    return arr

import random
arr = [random.randint(1, 2000) for i in range(1000)]

start1_time = time.time()
res1 = heap_sort(arr)
# print(res1)
end1_time = time.time()
print('my method',end1_time-start1_time)

start2_time = time.time()
heapq.heapify(arr)
end2_time = time.time()
print('inner method',end2_time-start2_time)

#可以看到,我們實現的排序算法在時間上不如內置的heapq.heapify()

2.n個數組找topk

有n個數組,每個數組中有m個元素,并且是有序排列好的,現在如何在這n*m個數中找出排名前500的數?

思路1:快排partition,缺點會改變數組順序

思路2:堆排序,速度快,不改變數組順序

  (1)首先建立大頂堆,堆的大小為數組的個數,即n,把每個數組最大的值(數組第一個值)存放到堆中。Python中heapq是小頂堆,通過對輸入和輸出的元素分別取相反數來實現大頂堆的功能

  (2)接著刪除堆頂元素,保存到另外一個大小為500的數組中,然后向大頂堆插入刪除的元素所在數組的下一個元素。

  (3)重復第(1)、(2)個步驟,直到刪除個數為最大的k個數,這里為500。

import heapq
def getTop(data):
    rowSize = len(data)
    columnSize = len(data[0])
    result = [None] * columnSize #保持一個最小堆,這個堆存放來自20個數組的最大數
    heap = []
    i = 0
    while i < rowSize:
        #數值,數值來源的數組,數值在數組中的次序index
        arr = (-data[i][0],i,0)
        heapq.heappush(heap,arr)
        i += 1
    num = 0
    while num < columnSize:
        d = heapq.heappop(heap)
        result[num] = -d[0]
        num += 1
        if num >= columnSize:
            break
        arr = (-data[d[1]][d[2]+1],d[1],d[2]+1)
        heapq.heappush(heap,arr)
    return result

#3個數組,每個數組有5個元素且有序,找出排名前5的值
data = [[29,17,14,2,1],[19,17,16,15,6],[30,25,20,14,5]]
print(getTop(data))

七、歸并排序

關鍵兩步驟:(1)劃分子表(2)合并半子表

原理(歸并排序相比較之前的排序算法而言加入了分治法的思想):

1.如果給的數組只有一個元素的話,直接返回(也就是遞歸到最底層的一個情況)

2.把整個數組分為盡可能相等的兩個部分(分)

3.對于兩個被分開的兩個部分進行整個歸并排序(治)

4.把兩個被分開且排好序的數組拼接在一起

最好/最壞/平均O(NlogN)

空間復雜度O(1)

代碼如下:

def merge(left,right):
    '''將兩個長度之和為n的有序子序列合并為一個有序序列,最多執行n-1次關鍵字值間的比較,時間復雜度為O(n)'''
    i,j = 0,0
    result = []
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1

    #循環結束之后可能有一個列表有剩余元素,直接加到result后面
    result += left[i:]
    result += right[j:]

    return result

def merge_sort(arr):#歸并排序
    if len(arr) <= 1:
        return arr
    m = len(arr) // 2
    #分
    left = merge_sort(arr[:m])
    right = merge_sort(arr[m:])
    #合并
    return merge(left,right)

arr = [3,4,2,8,9,5,1]
res = merge_sort(arr)
print(res)

八、基數排序

原理:將最低位優先法用于單關鍵字的情況,個位-十位-百位,依此類推

時間復雜度O(Nlog(r)m)

代碼如下:

def radix_sort(arr,radix=10):
    #求最大數的位數
    k = len(str(max(arr)))
    bucket = [[] for i in range(radix)]
    tmp = 0
    for i in range(1,k+1):#遍歷k位,從低位到高位
        for j in arr:#遍歷每個數
            tmp = j%(radix**i)//radix**(i-1)
            bucket[tmp].append(j)#計算j在第k位上的數
        del arr[:]#清空原arr
        for z in bucket:
            arr += z#按照桶內元素的順序依次加入arr
            del z[:]#清空該桶內的元素
    return arr

arr = [33,24,2,8,19,5,1]
res = radix_sort(arr)
print(res)

九、Timsort

Timsort是結合了合并排序(merge sort)和插入排序(insertion sort)而得出的排序算法,它在現實中有很好的效率。

參考文獻:

【1】排序算法時間復雜度、空間復雜度、穩定性比較

【2】基數排序與桶排序,計數排序【詳解】

【3】手撕九大經典排序算法

【4】三種快速排序以及快速排序的優化

【5】Timsort原理介紹 - 微信公眾號:猴子聊人物 - CSDN博客

總結

以上是生活随笔為你收集整理的九大排序算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。