python實現常見的排序算法

python實現常見的排序算法

冒泡排序

#冒泡排序
def bubble_sort(li):
    for i in range(len(li) - 1):
        exchange = False
        for j in range(len(li) - i - 1):
            if li[j] > li[j+1]:  # > 號升序    <號降序
                li[j],li[j+1] = li[j+1],li[j]  # 調換位置
                exchange = True
        if not exchange:
            return

選擇排序

#選擇排序
def select_sort(li):
    for i in range(len(li)-1):  #一共需要遍歷的次數,因爲最後一個元素時,不需要排序,n-1次
        min_loc = i   #無序區第一個元素的位置,記做最小值
        for j in range(i+1,len(li)):  #從無序區第二個元素開始遍歷到結尾
            if li[min_loc] > li[j]:#找到最小值得索引
                min_loc = j
        li[i],li[min_loc] = li[min_loc],li[i]  #每次排序結束後,將無序區第一個元素與最小值得元素替換位置
        print(li)

插入排序

#插入排序
def insert_sort(li):
    for i in range(1,len(li)):  # 循環n-1次,而且無序區元素是從索引爲1的元素開始,到結尾,依次插入有序區中
        tmp = li[i]  # 保存要插入的元素
        j = i -1 # j爲有序區最後一個元素,和tmp進行比較
        while j >= 0 and tmp < li[j]: # 如果tmp比j元素小,則j向前移動一位
            li[j+1] = li[j]
            j -= 1
        li[j+1] = tmp # tmp比j元素大時,插入在j元素右邊

快速排序

def partition(li,left,right):
    tmp = li[left]  #獲取最左邊一個值,將其歸位
    while left < right: #通過控制left和right
        #從最右邊開始,向左找,找一個比tmp小的數,放在tmp的位置上
        while left < right and tmp <= li[right]: #當tmp比右邊值大或等於,右邊遊標向左移動一位
            right -= 1
        li[left] = li[right]  #將右邊比5小的數放入左邊
        #從左邊向右邊找,找比tmp大的數,放入右邊這個空裏
        # print("左",li)
        while left < right and tmp >= li[left]:
            left += 1
        li[right] = li[left]
        # print("右",li)
    li[left] = tmp  #當左右遊標重合時,就是中間位置,將該元素放入中間位置
    return left


def re_quick_sort(li,left,right):  #用於遞歸
    if left < right:  #最少兩個元素,一個元素不用排序
        mid = partition(li,left,right)  #將第一個元素歸位到中間,獲取其索引
        re_quick_sort(li,left,mid-1)  #排序左邊
        re_quick_sort(li,mid+1,right) #排序右邊

堆排序

#堆排序
#大根堆
def sift(li,low,high):
    '''

    :param li:  傳入需要調整的堆
    :param low:  堆頂,堆的根節點
    :param high:  堆底,堆中最後一個元素
    :return:
    '''
    i = low  # i指向領導位置,需要放入元素的位置
    j = 2 * i + 1  # j指向i的左孩子結點
    tmp = li[low]  # tmp指向需要調整元素的值
    while j <= high:  # j的元素索引不能超過元素的最後一個索引
        # 找左右孩子中最大的孩子的索引  j + 1爲右孩子
        if j + 1 <= high and li[j+1] > li[j]:
            j += 1  # 如果右孩子比左孩子大,取右孩子結點
        if li[j] > tmp: # 把 j 指向的最大的孩子結點和 tmp需要調整的元素值進行比較,
            li[i] = li[j]
            # j元素進入領導層,將i指向j的空位上,j是i的孩子節點
            i = j
            j = 2 * i + 1
        else:
            li[i] = tmp  # 如果tmp需要調整的元素值更大,則將其放入i這個空位上,結束循環
            break
    else:
        li[i] = tmp  # 如果j比high大,代表i是葉子節點的空位,放入tmp調整元素

#小根堆
def sift_min(li,low,high):
    i = low        # 需要的替換位置
    j = i * 2 + 1  # 左節點
    tmp = li[low]
    while j <= high:
        #找左右子節點中最小的節點 的索引
        if j + 1 <= high and li[j+1] < li[j]:
            j += 1
        # 判斷替換元素和子節點誰大
        if li[j] < tmp:  # 如果j索引坐上領導位,空位索引給i,j爲i的子元素
            li[i] = li[j]
            i = j
            j = i * 2 + 1
        else:  # 位置找到了
            li[i] = tmp
            break
    else:  # j索引超過了i,i爲葉子節點時候
        li[i] = tmp

def heap_sort(li):    # 建堆
    n = len(li) - 1  # 獲取最後一個元素索引
    for i in range((n - 1)//2,-1,-1):  # 最後一個非葉子節點 ( n - 1 )//2  從後向前找  從索引爲(n - 1)//2的元素找到索引爲0的元素
        sift_min(li,i,n)
    #每次從頂部取一個數,和底部最後一個元素交換,然後向下調整。每次底部減少一個數
    for i in range(n,-1,-1):    # 最後一個元素索引一直在變化,所以從後向前
        li[0],li[i] = li[i],li[0]
        sift_min(li,0,i-1)  #最上面的元素始終爲0,最後一個元素爲i-1,因爲i的元素已經被拿出來了
    print(li)

歸併排序

#  歸併排序
def merge(li,low,mid,high):
    '''
    :param li:  兩個有序列表組成的一個列表
    :param low:  列表最左端
    :param mid:  列表中間位置,左邊最後一個元素的位置
    :param high: 列表最右邊
    :return:
    '''
    i = low  # 左邊遊標
    j = mid + 1  # 右邊遊標
    tmpArr = []
    while i <= mid and j <= high:  # 兩個遊標都不能超過他們最後一個元素
        if li[i] < li[j]: # 左邊比右邊小,將左邊元素放入臨時數組中
            tmpArr.append(li[i])
            i += 1  # 將左邊遊標向後移動一位
        else:
            tmpArr.append(li[j])
            j += 1
    #將剩下的元素放入臨時列表中
    while i <= mid:
        tmpArr.append(li[i])
        i += 1
    while j <= high:
        tmpArr.append(li[j])
        j += 1
    #將排好序的列表放入原列表中
    li[low:high+1] = tmpArr

def merge_sort(li,low,high):
    if low < high: # 保證進入遞歸的條件:始終有兩個元素   0 1 2 3
        mid = (low + high) // 2
        merge_sort(li,low,mid)    # 先將左右兩邊排序
        merge_sort(li,mid+1,high)
        merge(li,low,mid,high)   # 將左右兩邊排好序的列表歸併

希爾排序

#分組插入  每個分組間插入
def insert_sort_gap(li,gap):
    for i in range(gap,len(li)): #初始位置都是從gap位置開始
        j = i - gap    # j取這個分組的最後一個元素
        tmp = li[i]    # 將這個值存儲下來
        while j >= 0 and li[j] > tmp:  # j索引有效,且j當前指的元素比要插入的元素大
            li[j+gap] = li[j]   # j位置空下來,把j的值向右移動一個
            j -= gap    # j指向向左一個元素
        li[j+gap] = tmp
#希爾排序
def shell_sort(li):
    d = len(li)//2   #第一次分組,每組的大小爲元素的一半
    while d >= 1:
        insert_sort_gap(li,d)  #分一次組排一次序
        d //= 2  # 每次分組,每組的元素個數都減少一半

計數排序

#計數排序
def count_sort(li,max_count=100):
    count = [0 for i in range(max_count+1)] #生成一個全0的列表,範圍0-max_count
    # print(len(count))
    for val in li:
        count[val] += 1   # 遍歷原列表,將每個數都統計到新列表中
    li.clear()  # 清除原來列表
    for index,val in enumerate(count):  # 獲取新列表的索引(代表原列表中的數)和值(值代表這個數在原列表中的個數)
        for i in range(val):  # 將數寫入原列表中
            li.append(index)

桶排序

#桶排序
def bucket_sort(li,n=100,max_num=10000):
    buckets = [[] for i in range(n)]  # 先創建n個數的桶
    # 遍歷值,將每個數插入到桶中
    for val in li:
        i = min(val // (max_num//n),n - 1 ) # max_num/n代表每個桶中有多少個元素 i代表應該房放入哪個桶中 當超過最大桶的索引時,放入最大桶中
        buckets[i].append(val) # 將元素放入目標桶中
        # 給元素進行排序 用冒泡排序
        for j in range(len(buckets[i])-1,0,-1): # 從最後一個元素,一直遍歷到第二個元素,j代表冒泡索引
            if buckets[i][j] < buckets[i][j-1]: # 沒有找到位置,和前面的元素替換值
                buckets[i][j],buckets[i][j-1] = buckets[i][j-1],buckets[i][j]
            else:  # 該元素找到位置後就退出
                break
    # 將所有排序完的元素合併
    sorted_li = []
    for buc in buckets:
        sorted_li.extend(buc)
    return sorted_li

桶排序如果先將數據放入桶中,再使用快排,速度非常快

def buckets_sort2(li,n=100,max_val=10000): # n代表有n個桶,max_val爲最大數字
    buckets = [[] for _ in range(n)]
    for val in li: # 將每個數字遍歷出來
        i = min(val // (max_val // n),n-1)  #確定數字在哪個桶裏面
        buckets[i].append(val)

    #給每個桶排序
    for bucket in buckets:
        _quick_sort(bucket)   #這是一個快排函數,見上面快速排序

    #將元素整合
    result_list = []
    for bucket in buckets:
        result_list.extend(bucket)
    return result_list

測試函數:

import time
#用於統計函數時間的裝飾器
def calTime(func):
    def inner(*args,**kwargs):
        t1 = time.time()
        result = func(*args,**kwargs)
        t2 = time.time()
        print("%s running time:%.8s secs"%(func.__name__,t2-t1))
        return result
    return inner
#將時間裝飾器,裝飾到函數上,在函數名上一行開頭使用@calTime

#用於產生隨機數
import random
import copy
SORT_NUM = 100000
li = [random.randint(0,10000) for i in range(SORT_NUM)]
list1 = copy.deepcopy(li)
list2 = copy.deepcopy(li)
list3 = copy.deepcopy(li)
quick_sort(list1)
buckets_sort(list2)
buckets_sort2(list3)

SORT_NUM爲1000個時,速度爲
quick_sort running time:0.002979 secs
buckets_sort running time:0.001994 secs
buckets_sort2 running time:0.001007 secs

SORT_NUM爲100000個時(十萬),速度爲
quick_sort running time:0.400920 secs
buckets_sort running time:14.30812 secs
buckets_sort2 running time:0.293269 secs

基數排序

def radix_sort(li):
    max_num = max(li)
    it = 0
    while 10 ** it <= max_num:
        buckets = [[] for _ in range(10)]
        for val in li:
            digit = (val // 10 ** it) % 10 #從個位開始,十位,百位依次排序
            buckets[digit].append(val)
        li.clear()
        # 將每次排序完,將其依次放入數組中
        for buc in buckets:
            li.extend(buc)
        it += 1  #排完個位,排十位
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章