...

基于桶思想的排序

1. 桶思想

基数排序 vs 计数排序 vs 桶排序
以上都是利用了桶思想的排序,都属于无需比较进行排序思想里的一部分。
三者的主要区别就是。

  • 计数排序:每个桶只存储单一键值
  • 基数排序:根据键值的每位数字来分配桶;
  • 桶排序:每个桶存储一定范围的数值;

因为基本的都是拿空间来换时间,并且大多适用于数值范围小数据多这种场合。举几个例子。

  • 测量成年人的身高(140~220)差不多都是这个区间范围内的
  • 测试高考成绩(0~800) 差不多又是在这个范围内的。
    不是所有的排序都适用于桶思想,要看具体的数据是什么类型!

2. 计数排序 (Counting Sort)

2-1 特点

非比较的排序方式。多适用于确定范围的整数。
是标准的用空间在换时间的排序算法。
代码实现方便,最难的地方在于理解。起码需要2个数组。

  • 原始数组
  • 用来计数
  • 用来遍历反向输出数组

2-2 代码实现

2-2-1 思路1

"""  
这个是在原来的数组上  
进行重新写入覆盖的实现方式  
"""  

from random import shuffle  

def counting_sort(array, maxval):  
    # 数组长度为最大值+1  
    m = maxval + 1  
    # 新建一个长度为m全部为0的数组  
    count = [0] * m  
    # 遍历所有的文字  
    for a in array:  
        # a此时虽然为数组里的数字,但是作为index加入到了计数数组里面  
        count[a] += 1  
    # 默认i为0,用来计数全新产生的数组                  
    i = 0  
    # 遍历所有的m,这个时候a就是0~m,0.1.2.3.4...  
    for a in range(m):  
        # 遍历第2个数组,这时候数值为count[a] 其实是index为0,1,2,3里面所有值的个数  
        # 比如这样一个数字 0出现了3次。  
        # 0 1 2 3 4 5    
        # 3 0 3 1 1 2  
        # 那么下面这个for c in range(count(0)) = for c in range(3)  
        # 那么c就应该是0,1,2  
        # i = 0 =>array[0] = 0,array[1] = 0,array[3] = 0结束           
        for c in range(count[a]):   
            array[i] = a  
            i += 1  
    return array  


arr = [1, 1, 2, 3, 4, 4, 5, 6, 7, 8, 8, 8, 10, 10, 12, 14, 14, 15]  

print("原始数组\n",arr)  
shuffle(arr)  

print("打乱之后\n",arr)  
maxval = max(arr)  

counting_sort(arr,maxval)  
print("排序之后\n",arr)  

# 原始数组  
#  [1, 1, 2, 3, 4, 4, 5, 6, 7, 8, 8, 8, 10, 10, 12, 14, 14, 15]  
# 打乱之后  
#  [1, 14, 7, 1, 8, 2, 15, 4, 8, 10, 8, 3, 14, 5, 12, 4, 6, 10]  
# 排序之后  
#  [1, 1, 2, 3, 4, 4, 5, 6, 7, 8, 8, 8, 10, 10, 12, 14, 14, 15]  

2-2-2 思路2

网上查找的一个思路,生成的计数数组大小是按照 最大值-最小值+1 这个套路

def counting_sort(arr, maxVal, minVal):  
    n =len(arr)  
    # 计数数组长度  
    length = maxVal + minVal + 1  
    # 新建一个 排序好的数组  
    outArr = [0 for _ in range(n)]  
    # 新建一个 计数数组  
    countArr = [0 for _ in range(length)]  
    # 遍历所有的数字  
    # 然后放入 countArr  
    # 第一个遍历结束之后得到的数组 print(countArr)  
    # [1, 3, 1, 1, 1, 0, 2, 0, 3, 0, 0, 1, 1, 1, 0, 0, 2]  
    for number in arr:  
        # 这里是 根据最小数字为基准,找到合适的位置  
        countArr[number - minVal] += 1  

    # 对计数数组进行遍历  
    # 从第二个index开始遍历,得到一个数组  
    # 每个位置的值=当前值+前一个位置的值 用来统计出小于等于当前下标的元素个数  
    # [1, 4, 5, 6, 7, 7, 9, 9, 12, 12, 12, 13, 14, 15, 15, 15, 17]  
    for j in range(1, length):  
        countArr[j] = countArr[j] + countArr[j - 1]  
    print(countArr)  
    # 第三个反向遍历  
    # arr[i] 数组最后一个数字 - minVal 最小值 - 1 = 就是这个数字的该在的位置  
    for i in range(n-1, -1, -1):  
        # arr[16] = 1  
        # countArr[1-0] =  countArr[1] = 4  
        # outArr[4-1] = outArr[3] 也就是说 1 应该在outArr[3]的上面  
        outArr[countArr[arr[i] - minVal] - 1] = arr[i]  
        # countArr[1-0] =  countArr[1-0] - 1 = countArr[1] -1  = 4-1 = 3  
        # countArr[arr[i] - minVal] 自减 1  
        countArr[arr[i] - minVal] -= 1  

    return outArr  


arr = [12, 4, 11, 1, 3, 6, 8, 6, 1, 8, 2, 0, 13, 16, 16, 8, 1]  
print("排序之前", arr)  

maxVal = max(arr)  
minVal = min(arr)  
print("排序之后", counting_sort(arr, maxVal,minVal))  

# 排序之前 [12, 4, 11, 1, 3, 6, 8, 6, 1, 8, 2, 0, 13, 16, 16, 8, 1]  
# 排序之后 [0, 1, 1, 1, 2, 3, 4, 6, 6, 8, 8, 8, 11, 12, 13, 16, 16]  

2-3 注意点

添加一个函数用法的区别。

ord() ※ 文字 =>ASCII chr()※ ASCII =>文字
普通 ord("a") # 97 chr(97) # 'a'
转码返回10进制(Python2) ord(u"あ") # 12354
16进制(Python2) hex(ord(u"あ")) # 0x3042 unichr(12354) # u"\u3042"

注意点未完待续。。

3. 基数排序(Radix Sort)

3-1 特点

3-2 代码

3-3 注意点

4. 桶排序(Bucket Sort)

4-1 特点

4-2 代码

4-3 注意点

共有评论(0)

登陆即可评论哦