排序算法歸檔

算法復(fù)雜度速查:http://www.reibang.com/p/2bae94d4ea9b

快速排序是二叉樹的前序遍歷介褥,歸并排序是二叉樹的后序遍歷

排序算法的穩(wěn)定性指的是在排序過程中捆等,能夠保證相等元素在排序后的相對(duì)位置與排序前一致哨毁。

一酗昼、穩(wěn)定排序算法

  1. 冒泡排序:
    • 重復(fù)地走訪要排序的數(shù)列,一次比較兩個(gè)元素,如果它們的順序錯(cuò)誤就把它們交換過來。走訪數(shù)列的工作是重復(fù)地進(jìn)行直到?jīng)]有再需要交換玉吁,也就是說該數(shù)列已經(jīng)排序完成。
    • 在這個(gè)過程中腻异,相等元素不會(huì)交換位置进副,所以是穩(wěn)定排序。
  2. 插入排序:
    • 對(duì)于未排序數(shù)據(jù)悔常,在已排序序列中從后向前掃描影斑,找到相應(yīng)位置并插入曾沈。
    • 當(dāng)遇到相等元素時(shí),會(huì)插入到相等元素的后面鸥昏,不會(huì)改變相等元素的相對(duì)順序,所以是穩(wěn)定排序姐帚。
  3. 歸并排序:
    • 采用分治的思想吏垮,將數(shù)列分成兩部分,分別排序后再進(jìn)行合并罐旗。
    • 在合并過程中膳汪,如果兩個(gè)元素相等,會(huì)按照它們?cè)谠紨?shù)列中的順序依次放入結(jié)果數(shù)列中九秀,所以是穩(wěn)定排序遗嗽。

二车胡、不穩(wěn)定排序算法

  1. 選擇排序:
    • 首先在未排序序列中找到最泻薮辍(大)元素,存放到排序序列的起始位置精置,然后都弹,再從剩余未排序元素中繼續(xù)尋找最薪吭ァ(大)元素,然后放到已排序序列的末尾畅厢。
    • 每次選擇最小元素時(shí)冯痢,可能會(huì)改變相等元素的相對(duì)位置,所以是不穩(wěn)定排序框杜。
  2. 快速排序:
    • 通過一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨(dú)立的兩部分浦楣,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對(duì)這兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序咪辱。
    • 在分區(qū)過程中振劳,可能會(huì)改變相等元素的相對(duì)位置,所以是不穩(wěn)定排序油狂。
  3. 希爾排序:
    • 也稱遞減增量排序算法澎迎,是插入排序的一種更高效的改進(jìn)版本。
    • 由于多次插入排序选调,不同的步長可能會(huì)導(dǎo)致相等元素的相對(duì)位置發(fā)生變化夹供,所以是不穩(wěn)定排序。

遍歷順序

1仁堪、遍歷的過程中哮洽,所需的狀態(tài)必須是已經(jīng)計(jì)算出來的。
2弦聂、遍歷結(jié)束后鸟辅,存儲(chǔ)結(jié)果的那個(gè)位置必須已經(jīng)被計(jì)算出來氛什。

歸并排序

分治法

  • mergesort(): 分,從中間分離array匪凉,分別排序(遞歸)枪眉,最后調(diào)用merge合并
  • merge():治,將2個(gè)array合并為一個(gè)有序array再层,返回有序array
    • 若2個(gè)array都還有贸铜,依次填入
    • 若有一個(gè)遍歷結(jié)束,按序填入
def mergesort(arr):
    if len(arr)<=1:
        return arr
    mid = int(len(arr)/2)
    left = mergesort(arr[0:mid])
    right = mergesort(arr[mid:])
    return merge(left,right)

def merge(left,right):
    l = 0
    r = 0
    res = []
    while( l<len(left) and r < len(right)):
        if left[l]<= right[r]:
            res.append(left[l])
            l +=1
        else:
            res.append(right[r])
            r +=1
    if ( l<len(left) ):
        res.extend(left[l:])
    if ( r<len(right) ):
        res.extend(right[r:])
    return res

快速排序

快速排序的過程是一個(gè)構(gòu)造二叉搜索樹的過程聂受。 快排是不穩(wěn)定排序蒿秦。
為了避免最壞情況,引入隨機(jī) pivot蛋济。

更快的做法
在 partition 過程中棍鳖,把 left 作為 pivot,左右同時(shí)交換碗旅,最終渡处,把l=r 后,r 和pivot(left)交換祟辟,返回 r骂蓖。

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int: 
        return self.quickselect(nums, 0, len(nums)-1, k-1)
    def partition(self, nums, left, right):
        pivot = nums[left]
        l = left+1
        r = right
        while (l <=r):
            if (nums[l]< pivot and nums[r]> pivot):
                nums[l], nums[r] = nums[r], nums[l]
                l += 1
                r -= 1
            if nums[l]>= pivot:
                l += 1
            if nums[r] <= pivot:
                r -= 1 
        nums[left], nums[r] = nums[r], nums[left]
        return r

    def quickselect(self, nums, left, right, k):
        index = self.partition(nums, left, right)
        if index==k:
            return nums[k]
        elif index<k:
            return self.quickselect(nums, index+1, right, k)
        elif index >k:
            return self.quickselect(nums, left, index-1, k )

做法:此算法較慢,無法通過leetcode
在l和r之間川尖,把較小的數(shù)換在前面登下,然后放置pivot,返回pivot的index叮喳。

  1. 把right定位pivot
  2. 從left到i之間被芳,為小于pivot的數(shù)
  3. j遍歷left~right,如果違背馍悟,將j處的小數(shù)換到前邊i的位置畔濒。
def quicksort(arr,left,right):
    if left<right:
        if len(arr)<=1:
            return 
        idx = partition(arr,left, right)
        quicksort(arr,left,idx-1)
        quicksort(arr,idx+1,right)

def partition(arr,left,right):
    pivot = arr[right]
    index = left
    for j in range(left,right):
        if arr[j]<pivot:
            arr[i], arr[j] = arr[j], arr[i]
            index+=1
    arr[index], arr[right] = arr[right], arr[index]
    return i+1


arr = [12,1,7,3,5,6]
quicksort(arr,0,len(arr)-1)
print(arr)

quickselect算法

class Solution:
    def smallestK(self, arr: List[int], k: int) -> List[int]:
        if len(arr)==0 or k==0:
            return list()
        return self.quicksort(arr, 0, len(arr)-1, k)

    def partition(self, arr, left, right, k):
        pivot_value = arr[right]
        index = left
        for j in range(left, right):
            if arr[j] < pivot_value:
                arr[index], arr[j] = arr[j], arr[index]
                index +=1
        arr[index], arr[right] = arr[right], arr[index]
        return  index

    def quickselect(self,arr, left,right, k):
        if left==right:
            return arr[:k]
        index = self.partition(arr, left, right, k)
        if index ==k:
            return arr[:k]
        elif index<k:
            return self.quicksort(arr, index+1, right, k)
        elif index>k:
            return self.quicksort(arr, left, index-1, k)

堆排序

步驟(以升序排序?yàn)槔?/p>

  • 構(gòu)造一個(gè)大頂堆
  • 再將大頂堆的的頭尾元素互換(此步驟是為了模擬逐個(gè)出隊(duì)的過程)
def max_heapify(heap,heapSize,root):  # 調(diào)整列表中的元素并保證以root為根的堆是一個(gè)大根堆
    '''
    給定某個(gè)節(jié)點(diǎn)的下標(biāo)root,這個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)锣咒、左子節(jié)點(diǎn)侵状、右子節(jié)點(diǎn)的下標(biāo)都可以被計(jì)算出來。
    父節(jié)點(diǎn):(root-1)//2
    左子節(jié)點(diǎn):2*root + 1
    右子節(jié)點(diǎn):2*root + 2  即:左子節(jié)點(diǎn) + 1
    '''
    left = 2*root + 1
    right = left + 1
    larger = root
    if left < heapSize and heap[larger] < heap[left]:
        larger = left
    if right < heapSize and heap[larger] < heap[right]:
        larger = right
    if larger != root:  # 如果做了堆調(diào)整則larger的值等于左節(jié)點(diǎn)或者右節(jié)點(diǎn)的值毅整,這個(gè)時(shí)候做堆調(diào)整操作
        heap[larger], heap[root] = heap[root], heap[larger]
        # 遞歸的對(duì)子樹做調(diào)整
        max_heapify(heap, heapSize, larger)


def build_max_heap(heap):  # 構(gòu)造一個(gè)堆趣兄,將堆中所有數(shù)據(jù)重新排序
    heapSize = len(heap)
    for i in range((heapSize -2)//2,-1,-1):  # 自底向上建堆
        max_heapify(heap, heapSize, i)

import random

def heap_sort(heap):  # 將根節(jié)點(diǎn)取出與最后一位做對(duì)調(diào),對(duì)前面len-1個(gè)節(jié)點(diǎn)繼續(xù)進(jìn)行堆調(diào)整過程悼嫉。
    build_max_heap(heap)
    # 調(diào)整后列表的第一個(gè)元素就是這個(gè)列表中最大的元素艇潭,將其與最后一個(gè)元素交換,然后將剩余的列表再遞歸的調(diào)整為最大堆
    for i in range(len(heap)-1, -1, -1):
        heap[0], heap[i] = heap[i], heap[0]
        max_heapify(heap, i, 0)

# 測(cè)試
if __name__ == '__main__':
    a = [30, 50, 57, 77, 62, 78, 94, 80, 84]
    print(a)
    heap_sort(a)
    print(a)
    b = [random.randint(1,1000) for i in range(1000)]
    print(b)
    heap_sort(b)
    print(b)

python中 heapq 的使用

注意默認(rèn)為小頂堆,如果需要大頂堆蹋凝,需要用負(fù)值鲁纠。

import heapq

# 創(chuàng)建一個(gè)列表
data = [5, 7, 9, 1, 3]

# 將列表轉(zhuǎn)換為堆
heapq.heapify(data)
print("Heapified data:", data)  # 輸出: [1, 3, 9, 7, 5]

# 向堆中添加元素
heapq.heappush(data, 4)
print("After heappush(4):", data)  # 輸出: [1, 3, 4, 7, 5, 9]

# 從堆中刪除并返回最小元素
smallest = heapq.heappop(data)
print("Popped smallest element:", smallest)  # 輸出: 1
print("After heappop():", data)  # 輸出: [3, 5, 4, 7, 9]

# 獲取堆中的最小元素,但不刪除
smallest_peek = data[0]
print("Peek smallest element:", smallest_peek)  # 輸出: 3

# 從堆中刪除并返回最小元素鳍寂,推入新的元素
smallest_replaced = heapq.heapreplace(data, 2)
print("Replaced smallest element:", smallest_replaced)  # 輸出: 3
print("After heapreplace(2):", data)  # 輸出: [2, 5, 4, 7, 9]

# 獲取前 n 個(gè)最小的元素
three_smallest = heapq.nsmallest(3, data)
print("Three smallest elements:", three_smallest)  # 輸出: [2, 4, 5]

# 獲取前 n 個(gè)最大的元素
three_largest = heapq.nlargest(3, data)
print("Three largest elements:", three_largest)  # 輸出: [9, 7, 5]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末改含,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子迄汛,更是在濱河造成了極大的恐慌捍壤,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隔心,死亡現(xiàn)場離奇詭異,居然都是意外死亡尚胞,警方通過查閱死者的電腦和手機(jī)硬霍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笼裳,“玉大人唯卖,你說我怎么就攤上這事」恚” “怎么了拜轨?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長允青。 經(jīng)常有香客問我橄碾,道長,這世上最難降的妖魔是什么颠锉? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任法牲,我火速辦了婚禮,結(jié)果婚禮上琼掠,老公的妹妹穿的比我還像新娘拒垃。我一直安慰自己,他們只是感情好瓷蛙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布悼瓮。 她就那樣靜靜地躺著,像睡著了一般艰猬。 火紅的嫁衣襯著肌膚如雪横堡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天冠桃,我揣著相機(jī)與錄音翅萤,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛套么,可吹牛的內(nèi)容都是我干的培己。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼胚泌,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼省咨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起玷室,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤零蓉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后穷缤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敌蜂,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年津肛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了章喉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡身坐,死狀恐怖秸脱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情部蛇,我是刑警寧澤摊唇,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站涯鲁,受9級(jí)特大地震影響巷查,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜抹腿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一吮便、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幢踏,春花似錦髓需、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至搭幻,卻和暖如春咧擂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背檀蹋。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來泰國打工松申, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓贸桶,卻偏偏與公主長得像舅逸,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子皇筛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355