排序算法整理(python)

本博客來自於牛客網左神初級班的筆記整理,個人將java改寫爲python

1、冒泡排序

每次循環將最大的沉底。時間複雜度爲O(n^2),空間複雜度爲O(1)

def swap(nums, i, j):
    temp = nums[i]
    nums[i] = nums[j]
    nums[j] = temp

#冒泡排序
def bubbleSort(nums):
    for i in range(len(nums)):
        for j in range(len(nums)-i-1):
            if nums[j]>nums[j+1]:
                swap(nums,j,j+1)

if __name__ == "__main__":
    sort_list = list(map(int, input().split()))
    bubbleSort(sort_list)
    print(sort_list)

2、插入排序

每次選擇下一個數插入到已經排好序的列表中。時間複雜度爲O(n^2),空間複雜度爲O(1)

def swap(nums, i, j):
    temp = nums[i]
    nums[i] = nums[j]
    nums[j] = temp

#插入排序
def insertSort(nums):
    for i in range(len(nums)):
        for j in range(i-1,-1,-1):
            if nums[j]>nums[j+1]:
                swap(nums,j,j+1)
                
if __name__ == "__main__":
    sort_list = list(map(int, input().split()))
    insertSort(sort_list)
    print(sort_list)

3、選擇排序

在未排序列中找到最大(最小)元素,然後放到已排序列的末尾。時間複雜度爲O(n^2),空間複雜度爲O(1)

def swap(nums, i, j):
    temp = nums[i]
    nums[i] = nums[j]
    nums[j] = temp

#選擇排序
def selectSort(nums):
    for i in range(len(nums)):
        minIndex = i
        for j in range(i+1,len(nums)):
            minIndex = j if nums[j] < nums[minIndex] else minIndex
        swap(nums,i,minIndex)

if __name__ == "__main__":
    sort_list = list(map(int, input().split()))
    selectSort(sort_list)
    print(sort_list)

4、歸併排序

        先考慮合併兩個有序數組,兩個數組初始位置分別設置兩個指針,先取較小的那一個,然後對應指針向後移,直到一個數組爲空爲止,然後將另一個數組的剩餘部分複製過去。

        再考慮利用二分法,將數組不停拆分,直至拆分爲1,然後利用上述步驟合併成有序數組爲止。好處是對於有序數組內的數字無需再比較,故節約了時間。這是一個分治法的典型應用。

        歸併排序時間複雜度爲O(nlogn),空間複雜度爲O(n)(help數組所需)

def mergeSort(nums):
    if len(nums)<2:
        return nums
    mid = len(nums)//2
    left = mergeSort(nums[:mid])
    right = mergeSort(nums[mid:])
    return merge(left, right)
def merge(left, right):
    help = []
    l,r = 0,0
    while l<len(left) and r<len(right):
        if left[l] < right[r]:
            help.append(left[l])
            l += 1
        else:
            help.append(right[r])
            r += 1
    help += left[l:]
    help += right[r:]
    return help

if __name__ == "__main__":
    sort_list = list(map(int, input().split()))
    print(mergeSort(sort_list))

注:此寫法並未改變sort_list數組本身。

5、快速排序

        選取一個數,大於這個數的放右邊,小於這個數的放左邊,然後從分好的左右區間分別重複此步驟,直至各區間只有一個數爲止。主要思想是兩個指針,一個從後往前,遇到小於key值的停止,一個從前往後,遇到大於Key值的時候停止,然後將兩數交換,最後將key值與指針重合的地方交換。此方法一般的時間複雜度是O(nlogn),但當數組本身有序時,複雜度退化爲O(n^2)。因此當採用隨機選取區間中的數時,變成O(n^2)的概率就變成了一個期望值,比單純選取最後一個數要好。空間複雜度爲O(logn)

def swap(nums, i, j):
    temp = nums[i]
    nums[i] = nums[j]
    nums[j] = temp

#快速排序
def quickSort(nums):
    qSort(nums,0,len(nums)-1)
def qSort(nums, left, right):
    if left > right:
        return nums
    key = nums[left]
    l = left
    r = right
    while l<r:
        while nums[r] >= key and l<r:
            r -= 1
        while nums[l] <= key and l<r:
            l += 1
        swap(nums,l,r)
    swap(nums,left,l)
    qSort(nums,left,l-1)
    qSort(nums,r+1,right)

if __name__ == "__main__":
    sort_list = list(map(int, input().split()))
    quickSort(sort_list)
    print(sort_list)

這裏是普通快排,並不是隨機快排

7、堆排序

堆本質上還是一維數組,只不過是用二叉樹的思想來模擬,形式上表現爲完全二叉樹,具體公式如下:

大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  

小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

堆排序算法在https://www.cnblogs.com/chengxiao/p/6129630.html中講解的非常詳細,此處不再敘述

主要三個步驟爲:構建初始堆、堆排序、最大堆調整

def swap(nums, i, j):
    temp = nums[i]
    nums[i] = nums[j]
    nums[j] = temp

#堆排序
def heapSort(nums) :
    n = len(nums)
    first = int(n/2-1)       #最後一個非葉子節點
    for start in range(first,-1,-1) :     #構造大根堆
        max_heapify(nums,start,n-1)
    for end in range(n-1,0,-1):           #堆排,將大根堆轉換成有序數組
        swap(nums,0,end)
        max_heapify(nums,0,end-1)
    return nums
#最大堆調整:將堆的末端子節點作調整,使得子節點永遠小於父節點
#start爲當前需要調整最大堆的位置,end爲調整邊界
def max_heapify(ary,start,end):
    root = start
    while True :
        child = root*2 +1               #調整節點的子節點
        if child > end:
            break
        if child+1 <= end and ary[child] < ary[child+1]:
            child = child+1             #取較大的子節點
        if ary[root] < ary[child]:     #較大的子節點成爲父節點
            swap(ary,root,child)     #交換
            root = child
        else:
            break

if __name__ == "__main__":
    sort_list = list(map(int, input().split()))
    heapSort(sort_list)
    print(sort_list)

================================================================================================

        以上都是基於比較的排序。還有非基於比較的排序,如桶排序、計數排序、基數排序等。這種排序與被排序的數據樣本很有關係,所以在實際中並不常用。時間複雜度與空間複雜度爲O(N),穩定排序。

        應用題:相鄰最大差值。牛客網題目鏈接:https://www.nowcoder.com/practice/376ede61d9654bc09dd7d9fa9a4b0bcd?tpId=49&tqId=29366&rp=5&ru=/ta/2016test&qru=/ta/2016test/question-ranking

        此題目借鑑了桶排序和鴿籠原理的思想,先找出最小數和最大數之間的範圍,然後在有N個數的情況下,均分成N+1個桶,根據鴿籠原理,必有空桶,因此最大差值爲空桶前後桶之間後桶的最小值減去前桶的最大值。

class MaxDivision:
    def findMaxDivision(self, A, n):
        # write code here
        minn = min(A)
        maxx = max(A)
       	# 生成桶
        res = [0 for i in range(maxx - minn + 1)]
        # 填桶
        for i in range(n):
        	res[A[i] - minn] += 1
        count = 0
        num = -0x3f3f3f3f
        for i in range(len(res)):
        	if res[i] == 0:
        		# 如果說當前的桶爲空,則記錄下來連續的空桶數
        		count += 1
        	else:
        		if num < count:
        			num = count
        		count = 0
        # 爲何加1?舉例如下:最大值爲9,最小值爲3,中間有5個空桶,但差值應爲6
        return num + 1

        工程中的綜合排序算法:當數組很短時(如小於60)直接使用插入排序,當數組很長時,對於基礎類型(int,double,float)等,一般使用快排,因爲基礎類型無需穩定性,對於自定義類型,需要穩定性的,則使用歸併。

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