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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

快排堆排归排三种排序的比较

發布時間:2025/3/21 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 快排堆排归排三种排序的比较 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 快排
  • 堆排序
  • 歸并排序
  • 三種排序的比較

快排

快速排序中最簡單的(遞歸調用)

注:倒序,和 列表中有大量重復元素時,時間復雜度很大

快排例子

注:快排代碼實現(類似于二叉樹 遞歸調用)
            
時間復雜度:O(nlog?n)
空間復雜度:O(nlog?n)
穩定性:不穩定

      
快排代碼

#!/usr/bin/env python # -*- coding:utf-8 -*- import random import sys sys.setrecursionlimit(10000000) #設置系統最大遞歸深度def quick_sort(data, left, right):if left < right:mid = partition(data, left, right) # mid返回的是上一個用來排序那個數的下標quick_sort(data, left, mid - 1)quick_sort(data, mid + 1,right)# 每執行一次partition函數都可以實現將某個數左邊都比這個數小右邊都比這個數大 def partition(data, left, right):tmp = data[left]while left < right:while left < right and data[right] >= tmp: # 從右向左找小于tmp的數放到左邊空位置right -= 1data[left] = data[right] # 將右邊小于tmp值得數放到左邊空位置while left < right and data[left] <= tmp: # 從左向右找到大于tmp的值放到右邊空位置left += 1data[right] = data[left] # 將右邊大于tmp值得數放到右邊空位置data[left] = tmpreturn leftdata = list(range(100)) random.shuffle(data) #將有序列表打亂 quick_sort(data, 0, len(data) - 1) print(data)

不使用遞歸實現快排

#! /usr/bin/env python # -*- coding: utf-8 -*- def quick_sort(arr):'''''模擬棧操作實現非遞歸的快速排序'''if len(arr) < 2:return arrstack = []stack.append(len(arr)-1)stack.append(0)while stack:l = stack.pop()r = stack.pop()index = partition(arr, l, r)if l < index - 1:stack.append(index - 1)stack.append(l)if r > index + 1:stack.append(r)stack.append(index + 1)def partition(arr, start, end):# 分區操作,返回基準線下標pivot = arr[start]while start < end:while start < end and arr[end] >= pivot:end -= 1arr[start] = arr[end]while start < end and arr[start] <= pivot:start += 1arr[end] = arr[start]# 此時start = endarr[start] = pivotreturn startlst = [1,3,5,7,9,2,4,6,8,10] quick_sort(lst) print lst # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

快排簡易實現

#! /usr/bin/env python # -*- coding: utf-8 -*- def quick(list):if len(list) < 2:return listtmp = list[0] # 臨時變量 可以取隨機值left = [x for x in list[1:] if x <= tmp] # 左列表right = [x for x in list[1:] if x > tmp] # 右列表return quick(left) + [tmp] + quick(right)li = [4,3,7,5,8,2] print quick(li) # [2, 3, 4, 5, 7, 8]#### 對[4,3,7,5,8,2]排序 ''' [3, 2] + [4] + [7, 5, 8] # tmp = [4] [2] + [3] + [4] + [7, 5, 8] # tmp = [3] 此時對[3, 2]這個列表進行排序 [2] + [3] + [4] + [5] + [7] + [8] # tmp = [7] 此時對[7, 5, 8]這個列表進行排序 '''

快排的原理

快排思路詳解

# 從排序前--------> 到P歸位 經歷過程(前面都比5小后面都比5大) # 1、 首先從右向左比較,取出列表第一個元素5(第一個位置就空出來)與列表最后一個元素8比較,8>5不換位置 # 2、 用5與-2位置的9比,5<9不換位置 # 3、 5與-3位置的2比較,2<5,將-3位置的5放到1號位置,那么-3號位置空出來了,然后從左往右比較 # 4、 5與2號位置的7比,5<7,將7放到-3號位置,2號位置空出來了,在從右往左比 # 5、 -4號位置的1小于5將1放到空出的2號位置,-4位置空出來了,再從右向左比 # 6、 這樣第一次循環就實現了5放到列表中間,前面的都比5大,后面的都比5小

快排與冒泡時間復雜度對比

快排最壞時間復雜度為何為O(n2)

  • 每次劃分只能將序列分為一個元素與其他元素兩部分,這時的快速排序退化為冒泡排序
    2.如果用數畫出來,得到的將會是一棵單斜樹,也就是說所有所有的節點只有左(右)節點的樹;平均時間復雜度O(n*logn)
  • 堆排序

    堆的定義

  • 堆中某個節點的值總是不大于或不小于其父節點的值
  • 堆總是一棵完全二叉樹
  • 完全二叉樹定義:
  • 若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數
    第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹

  • 完全二叉樹特性
  • 一個高度為h的完全二叉樹最多有 2n -1 個節點
    根為 i 號節點,左孩子 為 2i、 右孩子為 2i+1,父親節點 (i – 1) / 2
    一個滿二叉樹 第 m層節點個數 等于 2m-1 個

    推導一個h層的滿二叉樹為何 有 2h -1 個節點
     

    調長定義(節點的左右子樹都是堆但自己不是堆)

    • 調長圖解
    • 調長原理
  • 首先將2拿出來與9和7比,這里面9最大,就用9作為根
  • 2放到9以前的位置,與8和5比,8最大放到開始9的位置
  • 2放到起始8的位置與6和4比,6最大,就出現了右邊那張圖了
  • 構造堆:從最后一個有孩子的父親開始

    構造堆

    #! /usr/bin/env python # -*- coding: utf-8 -*- def sift(data, low, high):''' 構造堆 堆定義:堆中某節點的值總是不大于或不小于父節點的值:param data: 傳入的待排序的列表:param low: 需要進行排序的那個小堆的根對應的號:param high: 需要進行排序那個小堆最大的那個號:return:'''i = low #i最開始創建堆時是最后一個有孩子的父親對應根的號j = 2 * i+ 1 #j子堆左孩子對應的號tmp = data[i] #tmp是子堆中原本根的值(拿出最高領導)while j <= high: #只要沒到子堆的最后(每次向下找一層) #孩子在堆里# if j < high and data[j] < data[j + 1]:if j + 1 <= high and data[j] < data[j + 1]: #如果有右孩紙,且比左孩子大j += 1if tmp < data[j]: #如果孩子還比子堆原有根的值tmp大,就將孩子放到子堆的根data[i] = data[j] #孩子成為子堆的根i = j #孩子成為新父親(向下再找一層)j = 2 * i + 1 #新孩子 (此時如果j<=high證明還有孩,繼續找)else:break #如果能干就跳出循環就會流出一個空位data[i] = tmp #最高領導放到父親位置def heap_sort(data):'''調整堆'''n = len(data)# n//2-1 就是最后一個有孩子的父親那個子堆根的位置for i in range(n // 2 - 1, -1, -1): #開始位置,結束位置, 步長 這個for循環構建堆# for循環輸出的是: (n // 2 - 1 ) ~ 0 之間的數sift(data, i , n-1) # i是子堆的根,n-1是堆中最后一個元素data = [20,50,20,60,70,10,80,30,40] heap_sort(data) print data # [80, 70, 20, 60, 50, 10, 20, 30, 40]
    • 在構造有序堆時,我們開始只需要掃描一半的元素(n/2-1 ~ 0)即可,為什么?
    • 因為(n/2-1)~0的節點才有子節點,如圖1,n=8,(n/2-1) = 3 即3 2 1 0這個四個節點才有子節點
    • 所以代碼4~6行for循環的作用就是將3 2 1 0這四個節點從下到上,從右到左的與它自己的子節點比較并調整最終形成大頂堆,過程如下:
    • 第一次for循環將節點3和它的子節點7 8的元素進行比較,最大者作為父節點(即元素60作為父節點)
    • 第二次for循環將節點2和它的子節點5 6的元素進行比較,最大者為父節點(元素80作為父節點)
    • 第三次for循環將節點1和它的子節點3 4的元素進行比較,最大者為父節點(元素70作為父節點)
    • 第四次for循環將節點0和它的子節點1 2的元素進行比較,最大者為父節點(元素80作為父節點)

    注:元素20和元素80交換后,20所在的節點還有子節點,所以還要再和它的子節點5 6的元素進行比較,這就是28行代碼 i = j 的原因

    • 至此有序堆已經構造好了!如上面右圖

    調整堆

  • 調整堆過程
  • 1、建立堆 2、通過調長,得到堆頂元素,為最大元素 3、去掉堆頂,將最后一個元素放到堆頂,此時可通過一次調整重新使堆有序 4、堆頂元素為第二大元素 5、重復步驟3,直到堆變空

  • 調整堆具體步驟
  • 堆頂元素80和尾40交換后–>調整堆

        
    堆頂元素70和尾30交換后–>調整堆

    堆頂元素60尾元素20交換后–>調整堆

    其他依次類推,最終已排好序的元素如下

    堆排序代碼實現

    # !/usr/bin/env python # -*- coding:utf-8 -*- import randomdef sift(data, low, high):''' 構造堆 堆定義:堆中某節點的值總是不大于或不小于父節點的值:param data: 傳入的待排序的列表:param low: 需要進行排序的那個小堆的根對應的號:param high: 需要進行排序那個小堆最大的那個號:return:'''root = low # root最開始創建堆時是最后一個有孩子的父親對應根的號child = 2 * root + 1 # child子堆左孩子對應的號tmp = data[root] # tmp是子堆中原本根的值(拿出最高領導)while child <= high: # 只要沒到子堆的最后(每次向下找一層) #孩子在堆里if child + 1 <= high and data[child] < data[child + 1]: # 如果有右孩紙,且比左孩子大child += 1if tmp < data[child]: # 如果孩子還比子堆原有根的值tmp大,就將孩子放到子堆的根data[root] = data[child] # 孩子成為子堆的根root = child # 孩子成為新父親(向下再找一層)child = 2 * root + 1 # 新孩子 (此時如果child<=high證明還有孩,繼續找)else:break # 如果能干就跳出循環就會流出一個空位data[root] = tmp # 最高領導放到父親位置def heap_sort(data):'''調整堆'''n = len(data)''' n//2-1 就是最后一個有孩子的父親那個子堆根的位置 '''for i in range(n // 2 - 1, -1, -1): # 開始位置,結束位置, 步長 這個for循環構建堆# for循環輸出的是: (n // 2 - 1 ) ~ 0 之間的數sift(data, i, n - 1) # i是子堆的根,n-1是堆中最后一個元素# 堆建好了,后下面就是挨個出數for i in range(n - 1, -1, -1): # i指向堆的最后 這個for循環出數然后,調長調整堆# for循環輸出的是 : n-1 ~ 0之間所有的數,n-1就是這個堆最后那個數的位置data[0], data[i] = data[i], data[0] # 將堆的第一個和最后一個值調換位置(將最大數放到最后)sift(data, 0, i - 1) # 將出數后的部分重新構建堆(調長)data = list(range(100)) random.shuffle(data) # 將有序列表打亂 heap_sort(data) print(data)

    初始化建堆過程時間:O(n) 公式推導

    參考博客:https://www.cnblogs.com/GHzz/p/9635161.html
       
    說明:建堆時間復雜度指初始化堆需要調整父節點和子節點順序次數

    推導初始化對時間復雜度:O(n)

    ''' 假設高度為:k ''' #### 1、推倒第i層的總時間:s = 2^( i - 1 ) * ( k - i ) # 說明:如果在最差的條件下,就是比較次數后還要交換;因為這個是常數,所以提出來后可以忽略; ''' 1. 2^( i - 1):表示該層上有多少個元素 2. ( k - i):表示子樹上要下調比較的次數:第一層節點需要調整(h-1)次,最下層非葉子節點需要調整1次。 3. 推倒倒數第1層下調次數:s = 2^( i - 1 ) * 0 倒數第2層下調次數:s = 2^( i - 1 ) * 1倒數第3層下調次數:s = 2^( i - 1 ) * 2倒數第i層下調次數:s = 2^( i - 1 ) * ( k - i ) '''#### 2、一次新建堆總時間:S = n - longn -1 # 根據1中公式帶人推倒 # S = 2^(k-2) * 1 + 2^(k-3)*2.....+2*(k-2)+2^(0)*(k-1) ===> 因為葉子層不用交換,所以i從 k-1 開始到 1; ''' S = 2^(k-2) * 1 + 2^(k-3)*2.....+2*(k-2)+2^(0)*(k-1) # 等式左右乘上2,然后和原來的等式相減,就變成了: S = 2^(k - 1) + 2^(k - 2) + 2^(k - 3) ..... + 2 - (k-1) S = 2^k -k -1 # 又因為k為完全二叉樹的深度,所以 (2^(k-1)) <= n < (2^k - 1 ) # 兩邊同時對2取對數,簡單可得 k = logn # 實際計算得到應該是 log(n+1) < k <= logn 綜上所述得到:S = n - longn -1,所以時間復雜度為:O(n) '''

    堆排序時間:O(nlogn) 公式推導

    • 推導方法1

    循環 n -1 次,每次都是從根節點往下循環查找,所以每一次時間是 logn,總時間:logn(n-1) = nlogn - logn

    • 推導方法2
  • 在一個堆中一次調長(調整堆)時間復雜度: log(n)
  • 排序時一次出棧頂元素需要循環 n次,每次時間復雜度為:log(n)
  • 所以總時間復雜度:nlog(n)
  • 歸并排序

    歸并原理圖(遞歸調用)


    時間復雜度:O(nlog?n)
    空間復雜度:O(1)
    穩定性:穩定

    歸并排序代碼

    #! /usr/bin/env python # -*- coding: utf-8 -*- def merge(li, low, mid, high):''':param li: 帶排序列表:param low: 列表中第一個元素下標,一般是:0:param mid: 列表中間位置下標:param high: 列表最后位置下標:return:'''i = lowj = mid + 1ltmp = []while i <= mid and j <= high:if li[i] < li[j]:ltmp.append(li[i])i += 1else:ltmp.append(li[j])j += 1while i <= mid:ltmp.append(li[i])i += 1while j <= high:ltmp.append(li[j])j += 1li[low:high+1] = ltmpdef mergesort(li, low, high):if low < high:mid = (low + high) // 2 #獲取列表中間的索引下標mergesort(li, low, mid) #先分解mergesort(li, mid+1, high)merge(li, low, mid, high) #然后合并data = [10,4,6,3,8,2,5,7] mergesort(data, 0 , len(data) -1) print(data) # [2, 4, 6, 8, 10, 12, 14, 16, 18]

    三種排序的比較

    • 三種排序算法時間復雜度都是( O(nlogn) )
    • 一般情況下,就運行時間而言
      快速排序 < 歸并排序 < 堆排序
    • 三種排序算法的缺點

    快速排序: 極端情況下排序效率低( O(n2) )
    歸并排序: 需要額外內存開銷(需要新建一個列表放排序的元素)
    堆排序: 在快的排序算法中相對較慢,堆排序最穩定

    詳細鏈接參考這個

    總結

    以上是生活随笔為你收集整理的快排堆排归排三种排序的比较的全部內容,希望文章能夠幫你解決所遇到的問題。

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