桶排序详细说明及实现-python

前言:

        说到桶排序,那必定要有桶,那么桶的作用是什么呢?桶的作用就是将序列分为若干份放到桶中,每个桶中能装入的数量范围是一定的,只有最后一个桶可以设置装入很多。这是因为当分的桶一定时,前面的桶装入的数的范围有限,这时还有序列中的数大于最后一个桶的数值范围,就把多余的装到最后一个桶中。

       那么如何把序列一个个放入不同的桶中呢,好比有从1到100这么多个数,我们要怎么分桶,分多少桶,可以根据序列的性质来设置。可以分10个桶,然后每个桶就是装10个数的范围,第一个桶装1-10的数,第二个桶装11-20的数,......,第十个桶装91-100的数,当然若有超过100的数由于没有第十一个桶,我们就把它装入第十个桶中。由此可知,每个桶装一定范围的数。

        把序列装入桶中就完了么,当然不是,还没有排序呢。这里当把一个序列装入桶中时,就用插入排序,从后往前插入,如果该数比前面的数大就插入其后面,不然就向前比较插入。就是说桶中每插入一个数就会把该数放到合适的位置使桶整体有序。

        最后就是按顺序逐个把每个桶中的元素放回原来的序列中。

        

 如下图,我们以序列【5, 24, 78, 65, 54, 94, 15, 36, 68, 35, 3, 78, 89, 56, 47】为例,进行桶排序。可以分配5个桶,每个桶中包含20个数的范围。


 

 实现代码:

def BucketSort(li, n=100, max_num=10000):
    buckets = [[] for _ in range(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
    # 把桶内数据放回原序列
    li.clear()
    for buc in buckets: #每个桶内都是有序的,连接起来
        li.extend(buc)
    return li


import random

li = [random.randint(0,10000) for i in range(100000)]  # 创建一个0-10000的随机数列表

li = BucketSort(li)
print(li)

 排序结果如图:


 桶排序改进:

        从代码中可以看出在每个数插入到桶中时,要对该数在通中的顺序进行排序调整,用的是插入排序进行比较来使桶序列有序的,这种方法还是比较慢的,我们可以使用其他的排序方法来代替插入排序。如快速排序。

 代码实现:

由于我们用到快速排序,我们可以把我们之前写的快速排序进行一下改装,放到QuickSort.py下

        之前的快速排序代码:

def partition(li, left, right):
    tmp = li[left]  # 设置tmp为最左边起始值
    while left < right:
        while left < right and li[right] >= tmp:  # 从右边找比tmp小的数
            right -= 1  # 没找到就往左走一步
        li[left] = li[right]  # 找到了比tmp小的,就把右边的值写到左边空位上,否则就是left=right
        while left < right and li[left] <= tmp:  # 从左边找比tmp大的数
            left += 1  # 没找到就往右走一步
        li[right] = li[left]  # 找到了比tmp大的,就把左边的值写到右边空位上,否则就是left=right
    li[left] = tmp  # 此时,right=left为空,把tmp值放置到此处,
    return left   # 返回下标,对左右两边进行递归排序

def QuickSort(li, left, right):
    if left < right:  # 至少两个元素,一个不需要
        mid = partition(li, left, right)  # 排序并返回mid(相对)
        QuickSort(li, left, mid - 1)  # 对mid左边排序
        QuickSort(li, mid + 1, right)  # 对mid右边排序

        我们为了比较一下改进是否有效,比较其排序时间更直观。因此我们需要一个装饰器。来记录每个函数的排序时间。

        装饰器代码如下:

import time

def cal_time(func):
    def wrapper(*args, **kwargs):
        t1 = time.time()
        result = func(*args, **kwargs)
        t2 = time.time()
        print("%s 运行时间: %s secs." %(func.__name__, t2-t1))
        return result

    return wrapper

        改进版桶排序及比较:

import copy
import random
import sys
from cal_time import *
import QuickSort

# 原版桶排序
@cal_time
def BucketSort(li, n=100, max_num=10000):
    buckets = [[] for _ in range(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
    li.clear()
    for buc in buckets:  # 每个桶内都是有序的,连接起来
        li.extend(buc)
    return li

# 改进版
@cal_time
def BucketSortPro(li, n=100, max_num=10000):
    buckets = [[] for _ in range(n)]  # 创建桶
    for var in li:
        i = min(var // (max_num // n), n-1)  # i 表示var放到几号桶里,最后一个桶是最大的一段数
        buckets[i].append(var)      # 把var加到对应的桶里面

    # 保持桶内的顺序, 使用快排进行每个桶的排序
    for i in buckets:
        QuickSort.QuickSort(i,0,len(i)-1)

    li.clear()
    for buc in buckets:  # 每个桶内都是有序的,连接起来
        li.extend(buc)
    return li

# 快速排序
@cal_time
def quicksort(li):  
    """
    :type li: list
    """
    QuickSort.QuickSort(li,0,len(li)-1)
    return li

li = [random.randint(0,10000) for i in range(100000)]  # 创建范围很大的随机序列

li1 = copy.deepcopy(li)  # 将序列深拷贝3份
li2 = copy.deepcopy(li)
li3 = copy.deepcopy(li)

li1 = BucketSort(li1)  
li2 = BucketSortPro(li2)
li3 = quicksort(li3)  # 排序结果是一样的,我们看排序时间快慢

运行时间比较:

        可以看到改进前桶排序用时达到了9秒多, 而快速排序用时0.3341秒,明显快排较快,而改进后的桶排序内部也采用了快速排序,结果比快排还要快为0.2383秒,所以改进是有效的且显著的。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

onlywishes

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值