python算法2

令人头疼的算法2

Posted by QQL on October 5, 2018

其他排序算法

希尔排序

原理:希尔排序(Shell Sort)是一种分组插入排序算法(插入算法升级版)
希尔排序分析: 每次减半分为一组,gap=len(n)//2,每组再进行插入排序
shellsort.png

def insert_sort_gap(li, gap):
    for i in range(gap, len(li)):  # i表示摸到的牌的下标
        tmp = li[i]
        j = i - gap  # j指的是手里的牌的下标
        while j >= 0 and li[j] > tmp:
            li[j + gap] = li[j]  # 摸到的牌和手上的牌的值进行替换
            j -= gap  # 这组的下一个值
        li[j + gap] = tmp  # 不进行交换,保留摸到的牌原本的值


def shell_sort(li):
    d = len(li) // 2  # 进行分组
    while d >= 1:  # 分组结束条件为分组=1,
        insert_sort_gap(li, d)  # 调用插入排序
        d //= 2  # 得到新的分组步长

时间复杂度:Shell复杂度与gap有关系,不同的gap导致不同结果

  • 最优时间复杂度:根据步长序列的不同而不同
  • 最坏时间复杂度:O(n2)
  • 稳定想:不稳定

计数排序

速度快,使用需要有前提

def count_sort(li, max_count=100):
    count = [0 for _ in range(max_count+1)]
    print('func_count',count)
    for val in li:
        count[val] += 1  # 给对应位置计数+1
    li.clear()  # 清空原来列表,防止浪费空间复杂度
    for ind,val in enumerate(count):
        for i in range(val):  # 循环每个元素的个数
            li.append(ind)  # 重新把数据写回原列表

import random

li = [random.randint(0,100) for _ in range(1000)]
print('first',li)
count_sort(li)
print('final',li)

时间复杂度:O(n),虽然有多个for循环,但是和n,即len(li)没有关系,只有第一个循环与n有关系,所以为O(n)
计数排序速度比任何排序都快,但是使用计数排序有前提
必须知道列表的范围,数字大的也不行,因为如果只有五个数,每个数很大,则要开辟很大的空间

桶排序

原理:计数排序中,如果元素范围比较大(例如:1~1亿之间)需要改造算法计数排序升级版,就是桶排序。首相将元素分在不同的桶中,再对每个桶中的元素排序
桶.png

def bucket_sort(li, n=100, max_num=10000):
    """
    桶排序
    :param li: 列表
    :param n: 桶的数目
    :param max_num: 数的范围
    :return:
    """
    buckets = [[] for _ in range(n)]  # 创建n个空桶[],每个桶可以放max_num//n个数的范围
    for var in li:
        i = min(var // (max_num//n),n-1)  # i:var被放到几号桶
        buckets[i].append(var)  # 把var加到桶里
        # 保持桶内的顺序
        for j in range(len(buckets[i])-1,0,-1):  # 倒过来进行桶内排序
            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

import random

li = [random.randint(0, 100) for i in range(10000)]
li = bucket_sort(li)
print(li)

桶排序的表现取决于数据的分布。也就是需要对不同的数据排序时使用不同的分桶策略
时间复杂度

  • 平均情况:O(n+k)
  • 最坏情况:O(n^2k)
  • 空间复杂度:O(nk)

基数排序

本质:多关键字排序
根据数字不同位数,分成n个桶
桶排序的升级版本

def radix_sort(li):
    max_num = max(li)  # 最大值9->1, 99->2, 888->3, 10000->5
    it = 0
    while 10 ** it <=max_num:
        buckets = [[] for _ in range(10)]
        for var in li:
            # 987 it=1 987//10->98 98%10->8;   it=2  987//100->9  9%10=9
            digit = (var//10**it)%10
            buckets[digit].append(var)
        # 分桶完成
        li.clear()
        for buc in buckets:
            li.extend(buc)
        # 把数重新写回li

        it += 1

时间复杂度:O(kn)
空间复杂度:O(k+n)
k表示数字位数