希尔、计数、桶、基数排序python实现

一、希尔排序

1、方法

一种分组插入的排序算法

        首先取一个整数d1=n/2,将元素分成d1个组,每组相邻两元素之间的距离为d1,在各组内进行直接插入排序;

        取第二个数d2=d1/2,重复上述分组排序过程,直到di=1,即所有元素在同一组内进行直接插入排序。

        注:希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序;最后一趟排序使得所有数据有序。

例: 5  7  4  6  3   1  2  9  8

每间隔d = 4分为一组: 5  7  4  6  3  1  2  9  8

分成上述分组,颜色相同的分为一组,组内进行排序后为 3  1  2  6  5  7  4  9  8

再由d = 2进行分组  3  1  2  6  5  7  4  9  8  颜色相同的分为一组,进行组内直接插入排序。

最后相当于d = 1分组,进行直接分组插入排序,最终使得数组有序。

2、代码实现

# 希尔排序,代码实现

def insert_sort_gap(li, gap):  # 实现分组插入排序
    for i in range(gap, len(li)-1):
        tmp = li[i]   # 暂存无序区第一个数
        j = i - gap   # 每组内有序区最后一个数的下标
        while j >= 0 and li[j] > tmp:  # 如果有序区的数大于无序区的数
            li[j+gap] = li[j]  # 将有序区的数的位置往后移动一位
            j -= gap
        li[j+gap] = tmp   # 将tmp插入到有序区合适的位置

def shell_sort(li):
    d = len(li) // 2  # 先分为两组
    while d >= 1:
        insert_sort_gap(li, d)
        d //= 2  # 每次分组的组内间距减少一半
# 希尔排序的时间复杂度与选取的gap序列有关

li = list(range(100))
import random
random.shuffle(li)
print(li)
shell_sort(li)
print(li)

二、计数排序

1、方法

        已知列表中有多少个数n,构造一个新的长度为n的列表,遍历原列表,如果第 i 个数为 j ,则在新列表对应大小的下标处进行加1,遍历完成后清除列表。

        将新列表中的元素“取出”,新列表中的下标对应原列表中的数的大小,新列表中的下标的数为原列表中的数出现了几次。

2、代码实现

def count_sort(li, max_count):
    count = [0 for _ in range(max_count+1)]    # 初始化计数器列表
    for val in li:   # 从0开始记录每个数字出现的次数
        count[val] += 1
    li.clear()
    for ind, val in enumerate(count):
        for i in range(val):
            li.append(ind)

import random
li = [random.randint(0, 20) for _ in range(10)]
print(li)
count_sort(li, 20)
print(li)

计数排序需要知道列表中数的范围,和开辟相应的新内存,使用限制比较大。

三、桶排序

        基于计数排序,首先将元素分在不同的桶中,再对每个桶中的元素尽心排序。

代码实现

def bucket_sort(li, n, max_num):   # n:分成多少个桶   max_num:知道的最大的数
    buckets = [[] for _ in range(n)]   # 创建二维列表,表示列表中存有n个桶
    for var in li:   # 将各元素放到对应的桶里
        # 若n=100, max_num=10000
        # 此时桶号是0-99,max_num//n表示每个桶里能存多少个元素
        # 由于10000//100=100,且没有第100号桶,所以将其存在第99号桶里
        i = min(var // (max_num // n), n-1)   # i表示var放到几号桶里
        buckets[i].append(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
# 桶排序:平均情况时间复杂度O(n+k)
# 最坏情况时间复杂度O(k*n的平方)
# 空间复杂度O(nk)

import random
li = [random.randint(0, 100) for _ in range(1000)]
print(li)
print(bucket_sort(li, 10, 100))

四、基数排序

1、方法

        对于列表中的数,根据个位、十位、百位···,进行分桶。例如,一个最大数为三位数的数组,先根据个位数进行分桶,分别分到十个桶里,然后再次对其进行按照十位数进行分桶(不足位的可以看作零处理),此时个位数会在最前面排序好,十位数会向后排序,最后按照百位数进行分桶,则可以得到一个有序的数列。

2、代码实现

def radix_sort(li):
    max_num = max(li)    # 最大值,根据最大值的位数判断进行几次桶排序
    it = 0
    while 10 ** it <= max_num:  # 判断最大数有几位,如果最大数是4位,此时it=3,进入循环,it+1,10的4次方=10000,推出循环,代表最大数有4位
        buckets = [[] for _ in range(10)]
        # 根据位数分桶排序
        for var in li:
            # 例876  取个位(it=0) (876//10**0)%10,取十位(it=1) (876//10**1)%10,取百位(it=0) (876//10**2)%10,
            digit = (var // 10**it) % 10
            buckets[digit].append(var)
        # 分桶完成
        li.clear()    # 清空原列表
        for buc in buckets:
            li.extend(buc)   # 把数重新写入li
        it += 1

# 时间复杂度 O(kn)     # k=lgn
# 空间复杂度 O(k+n)

import random
li = [i for i in range(100)]
random.shuffle(li)
print(li)
radix_sort(li)
print(li)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值