桶排序是什么?
桶排序(Bucket Sort)的主要思想是:桶排序的思想是将待排序数据分配到有限数量的桶中,每个桶中的数据再单独进行排序。通过合理设置桶的容量和数量,使得数据能够均匀分布,从而提高排序效率。最后,将各个桶中已排序的数据依次合并,得到完整的排序结果。
桶排序的性能如何?
时间复杂度:
在理想情况下,当数据均匀分布在各个桶中,并且每个桶内的排序算法时间复杂度较低时,桶排序的总体时间复杂度可以达到O(n),其中n为待排序元素的数量。这是因为每个桶内的排序可以独立进行,且桶的数量通常远小于n,所以总体时间复杂度接近于线性。
在最坏情况下,如果所有数据都分布在一个桶中,桶排序的时间复杂度可能会达到O(n^2),类似于插入排序或冒泡排序的性能。
空间复杂度:
桶排序通常需要额外的空间来存储桶本身以及桶内元素。因此,空间复杂度至少为O(n+k),其中k为桶的数量。如果桶的数量较大或者桶内元素需要额外的空间进行排序,则空间复杂度会进一步增加。
以下是代码(含有注释):
def selection_sort(arr): #选择排序
for i in range(len(arr)): #遍历原数组
min_index=i #假设最开始的最小值的下标为i,实际上最小值下标后面遍历后会更改
for j in range(i+1,len(arr)): #找出最小值的下标min_index
if arr[j]<arr[min_index]: #如果有比找到的最小值更小的数
min_index=j #就更新最小值的下标
#最重要的是,找到最小值要交换。目的让每一次最小的数在最左边。
arr[i],arr[min_index]=arr[min_index],arr[i]
def Bucket_sort(arr):
min_value=min(arr) #定义原数组最小值
max_value=max(arr) #定义原数组最大值
bucketcount=3 #桶的数量 !!! 注意,我们这里只是简单说明一个桶排序思想,并不一定固定桶的数量,当元素很多时,桶的数量会更多
bucket=[[],[],[]] #记录不同桶的数据
perBucket=(max_value -min_value+bucketcount)//bucketcount #计算每个桶不同数字的元素个数
#这里的加bucket 是因为有不同的桶,要均匀的分配
for num in arr:
bucketindex=(num -min_value)//perBucket #选择一个合适的桶
bucket[bucketindex].append(num) #把元素塞进桶里
for i in range(bucketcount): #遍历每个桶
selection_sort(bucket[i]) #每个桶进行选择排序 减小了数据规模
idx=0 #下标,用于把每个桶里面的数据放回原列表
for i in range(bucketcount): #遍历不同的桶
for v in bucket[i]: # 遍历桶里面的数据
arr[idx]=v #把数据给放原理的列表当中
idx+=1 #遍历完当前元素后 往后走一个位置,继续遍历同一个桶里面的下一个元素
arr=[2,4,1,3,6,9,8,7]
Bucket_sort(arr) #在Bucket_sort里面包含了选择排序,所以只需要调用一个函数
print(arr)
如果你看不懂怎么办?
不要慌,这里我会给出详解。我们先来看看步骤。
桶排序的基本步骤包括:
-
设置桶的容量和数量:根据待排序数据的范围和分布情况,确定桶的容量和桶的数量。桶的容量应该足够小,使得数据能够相对均匀地分配到各个桶中。
-
分配数据到桶中:遍历待排序的数据,根据每个数据的大小,将其分配到对应的桶中。通常,这一步会用到一个映射函数,将数据映射到相应的桶索引上。
-
对每个桶中的数据排序:在分配完数据后,每个桶中都会包含一部分数据。接下来,对每个桶中的数据进行排序。这里可以使用任何其他的排序算法,比如插入排序、快速排序等,根据具体情况选择。
-
合并桶中的数据:当每个桶中的数据都排序完成后,按照桶的顺序,将桶中的数据依次取出,合并成一个有序序列。
每段代码执行的任务:
一:
def selection_sort(arr):
for i in range(len(arr)):
min_index = i
for j in range(i+1, len(arr)):
if arr[j] < arr[min_index]:
min_index = j
arr[i], arr[min_index] = arr[min_index], arr[i]
这段代码(选择排序)的基本思想是:
- 遍历数组,从第一个元素开始,将其假设为当前最小值的下标。
- 继续遍历数组,如果找到更小的元素,则更新最小值的下标。
- 当内部循环结束后,我们已经找到了从当前位置到数组末尾的最小值下标。
- 将当前位置与找到的最小值交换位置。
- 重复上述步骤,直到整个数组排序完成。
二:
def Bucket_sort(arr):
min_value = min(arr)
max_value = max(arr)
bucketcount = 3
bucket = [[], [], []]
perBucket = (max_value - min_value + bucketcount) // bucketcount
- 确定数组中的最小值和最大值。
- 定义桶的数量,数据更大的时候可能桶的数量更大,这里只是给出简单的数据3来说明桶排序的思想。
- 建立二维列表,用于存放每个桶的数据。
- 根据最小值和最大值,以及桶的数量,计算每个桶的范围(
perBucket
)。你是不是以为每个桶只需要(最大值-最小值)//桶的数量,就够了?恰恰相反,我们来详细解释一下哎。
这是为什么(max_value - min_value + bucketcount)的原因,如果你会了可跳过。
在桶排序中,perBucket
用来确定每个桶中应该容纳的数据范围。计算 perBucket
的公式 perBucket = (max_value - min_value + bucketcount) // bucketcount
包含了加上 bucketcount
的部分,这是为了确保当数据分布比较均匀时,最后一个桶能够包含 max_value
。
考虑以下情况:
max_value - min_value
是数据范围的总大小。bucketcount
是桶的数量。
如果不加 bucketcount
,直接进行除法 (max_value - min_value) // bucketcount
,那么计算出的 perBucket
将会是每个桶平均应该包含的数据范围大小。但是,这样可能会导致最后一个桶的数据范围不完整,特别是当 max_value - min_value
不能被 bucketcount
整除时。
加上 bucketcount
是为了确保当 max_value - min_value
不能被 bucketcount
整除时,最后一个桶可以包含到 max_value
。这是通过扩大总的范围(max_value - min_value + bucketcount
),然后再进行除法来实现的。这样,每个桶都会得到一个至少包含 perBucket
个数的范围,并且最后一个桶能够包含 max_value
。
举一个简单的例子(如果你已经会了可跳过):
假设 max_value = 10
,min_value = 1
,bucketcount = 3
。
如果不加 bucketcount
,则 perBucket = (10 - 1) // 3 = 3
。这样,三个桶的范围可能是 [1, 3]
,[4, 6]
,和 [7, 9]
,10
就会被遗漏。
加上 bucketcount
,则 perBucket = (10 - 1 + 3) // 3 = 4
。这样,三个桶的范围可以是 [1, 4]
,[5, 8]
,和 [9, 10]
,所有数值都被包含在内。
这就是为什么要加上bucketcount的原因,一句话概括就是:
为了均匀分布元素,保证最后一个桶接受到最大值(最后一个桶装的元素可能大于前面的桶)。
三:
for num in arr:
bucketindex = (num - min_value) // perBucket
bucket[bucketindex].append(num)
for i in range(bucketcount):
selection_sort(bucket[i])
idx = 0
for i in range(bucketcount):
for v in bucket[i]:
arr[idx] = v
idx += 1
1-3行:这部分代码遍历原始数组 arr
中的每个元素 num
。对于每个元素,它计算一个 bucketindex
,该索引决定了 num
应该被放入哪个桶中。bucketindex
是通过 (num - min_value) // perBucket
计算得出的,这里使用了整数除法来确保索引是一个整数。然后,num
被添加到对应的桶 bucket[bucketindex]
中。
5-6行:在将所有元素分配到桶中之后,这部分代码遍历每个桶,并对桶中的元素进行排序。这里使用了选择排序算法(selection_sort
函数)来对每个桶中的元素进行排序。
最后:
这部分代码负责将排序后的桶元素重新组合回原始数组 `arr`。它初始化一个索引 `idx` 为 0,然后遍历每个桶。
对于每个桶中的元素 `v`,它将其赋值给 `arr[idx]`,并将 `idx` 增加 1,以便下一个元素可以被放置在正确的位置。
这样,当所有的桶都被遍历完时,原始数组 `arr` 就会被更新为排序后的数组。
总结:
整体而言,桶排序是一种分配式排序算法,它通过将数组分配到有限数量的桶中,再对每个桶中的元素进行排序(可能使用别的排序算法或是以递归方式继续使用桶排序进行排序),最后将各个桶中的数据取出放入原始数组得到有序序列。桶排序的性能通常取决于数据的分布和桶的数量。对于均匀分布的数据,桶排序可以是非常高效的。
最后喝一口鸡汤^-^:
假如生活抛来一颗柠檬,你大可以选择把它榨成鲜美的柠檬汁。