桶排序的介绍
原理: 将数据分配到多个桶中,对每个桶单独排序,最后合并。
时间复杂度:
最好情况:
O
(
n
+
k
)
O(n + k)
O(n+k) (k 是桶的数量)
最坏情况:
O
(
n
2
)
O(n²)
O(n2) (所有数据集中在同一个桶中,退化成插入排序)
平均情况:
O
(
n
+
k
)
O(n + k)
O(n+k)
空间复杂度:
O
(
n
+
k
)
O(n + k)
O(n+k)
稳定性: 稳定
适用场景: 数据分布均匀的情况。
桶排序原始数据
桶排序排序中
实现桶排序
桶排序的实现是基于数组+链表的数据结构。其中关键点有4个
- 桶的个数=数组的长度
- 桶的下标用hash算出,公式为 element × length max + 1 \frac{\text{element} \times \text{length}}{\text{max} + 1} max+1element×length,其中element表示元素实际的值,length表示数组的长度,max表示数组中最大的元素值
- 当发生哈希冲突时,发生冲突的数据会进行进行局部并组成一个链表。这里会涉及到单链表的插入
- 当元素完成在桶中的插入及排序之后,需要遍历桶对应的链表,依次出桶,放入原数组。
// 根据桶的个数来确定hash函数,这份代码适合桶的个数等于数组长度
private static int hash(int element, int max, int length) {
return (element * length) / (max + 1);
}
public static void main(String[] args) {
int[] arr = Util.getRandomArr( 10, 1, 100 );
System.out.println( "begin..." + Arrays.toString( arr ) );
new _9BucketSort().sort( arr );
System.out.println( "final..." + Arrays.toString( arr ) );
Assertions.assertThat( Util.checkOrdered( arr, true ) ).isTrue();
}
private void sort(int[] arr) {
int length = arr.length;
LinkedNode[] bucket = new LinkedNode[length]; // 桶的个数=length
int max = Util.maxOf(arr);//求max
// 入桶
for (int i = 0; i < length; i++) {
int value = arr[i];//扫描每个元素
int hash = hash( arr[i], max, length ); // 桶的下标
if (bucket[hash] == null) {
bucket[hash] = new LinkedNode( value ); // 初始化链表表头
} else {
insertInto( value, bucket[hash], bucket, hash ); // 插入链表
}
}
int k = 0; // 记录数组下标
//出桶,回填arr
for (LinkedNode node : bucket) {
if (node != null) {
while (node != null) {
arr[k++] = node.value;
node = node.next;
}
}
}
}
private void insertInto(int value, LinkedNode head, LinkedNode[] bucket, int hash) {
LinkedNode newNode = new LinkedNode( value );
//小于头节点,放在头上
if (value <= head.value) {
newNode.next = head;
// 替换头节点
bucket[hash] = newNode;
return;
}
/*往后找第一个比当前值大的节点,放在这个节点的前面*/
LinkedNode p = head;
LinkedNode pre = p;
while (p != null && value > p.value) {
pre = p;
p = p.next;
}
if (p == null) { // 跑到末尾了
pre.next = newNode;
} else { // 插入pre和p之间
pre.next = newNode;
newNode.next = p;
}
}