1、计数排序
原理
创建一个长度为 k+1 的数组,每个数组的值对应输入数组中数字出现的次数,通过遍历一次输入数组并统计每个元素出现次数,最后遍历输出
图解
代码
void CountSort(int *a, int n) {
int i;
int k = 0;
int minValue = a[0];
int maxValue = a[0];
for (i = 0; i < n; i++) {
if (a[i] < minValue) {
minValue = a[i];
}
if (a[i] > maxValue) {
maxValue = a[i];
}
}
int count = maxValue - minValue + 1;
int *b = new int[count];
memset(b, 0, sizeof(b));
for (i = 0; i < n; i++) {
b[a[i] - minValue]++;
}
for (i = 0; i < count; i++) {
while (b[i]--) {
a[k++] = i + minValue;
}
}
delete[] b;
}
复杂度
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n + k ) O(n+k) O(n+k)
稳定性
计数排序是稳定的
2、桶排序
思想
桶排序就是划分多个范围相同的区间,每个区间各自排序,最后合并。
桶排序相当于是计数排序的扩展版,只不过计数排序是每个区间是长度为1的桶,并且每个桶的只有相同的元素,而桶排序是按区间存储一定范围的元素,元素种类也可以不相同
注:
1、桶排序只适用于非负数数的情况2、桶排序尽量保证元素均匀分布,若全部集中在一个桶,则排序失效
图解
代码
来源于 菜鸟教程——桶排序
struct ListNode {
int val;
ListNode *next;
};
//链表的有序插入
ListNode *Insert(ListNode *head, int val) {
ListNode *dum = new ListNode;
ListNode *newNode = new ListNode;
ListNode *pre, *cur;
newNode->val = val;
dum->next = head;
pre = dum;
cur = head;
while (cur && cur->val <= val) {
pre = cur;
cur = cur->next;
}
newNode->next = cur;
pre->next = newNode;
return dum->next;
}
//有序链表的合并
ListNode *Merge(ListNode *l1, ListNode *l2) {
ListNode *dumNode = new ListNode;
ListNode *dum = dumNode;
while (l1 && l2) {
if (l1->val >= l2->val) {
dum->next = l2;
l2 = l2->next;
} else {
dum->next = l1;
l1 = l1->next;
}
dum = dum->next;
}
if (l1) {
dum->next = l1;
}
if (l2) {
dum->next = l2;
}
return dumNode->next;
}
//获取桶的长度
int GetBucketLength(int *a, int n) {
int max = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > max) {
max = a[i];
}
}
return (int) sqrt(max) + 1;
}
//桶排序
void BucketSort(int *a, int n) {
int BUCKET_NUM = GetBucketLength(a, n);
vector<ListNode *> bucket(BUCKET_NUM, (ListNode *) (0));
for (int i = 0; i < n; i++) {
int index = a[i] / BUCKET_NUM;
ListNode *head = bucket.at(index);
bucket.at(index) = Insert(head, a[i]);
}
ListNode *head = bucket.at(0);
for (int i = 1; i < BUCKET_NUM; i++) {
head = Merge(head, bucket.at(i));
}
for (int i = 0; i < n; i++) {
a[i] = head->val;
head = head->next;
}
}
复杂度
时间复杂度: O ( n + k ) O(n+k) O(n+k)
空间复杂度: O ( n + k ) O(n+k) O(n+k)
稳定性
桶排序是稳定的
3、基数排序
思想
基数排序使用的是桶排序的思想,不过基数排序比桶排序更加灵活,他只需要10个桶,每次只对一位进行排序
首先对个位进行排序,然后对十位、百位…每一步使序列趋近于有序状态,排到最后一位的时候排序完成
图解
代码
来源于 菜鸟教程——基数排序
//获取最大元素的位数
int MaxBit(int *a, int n) {
int max = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > max) {
max = a[i];
}
}
int cnt = 1;
while (max >= 10) {
cnt++;
max /= 10;
}
return cnt;
}
//基数排序
void RadixSort(int *a, int n) {
int NumBit = MaxBit(a, n);
int tmp[n];
int count[10];
int i, j, k;
int radix = 1;
for (i = 1; i <= NumBit; i++) {
memset(count, 0, sizeof(count));
for (j = 0; j < n; j++) {
k = (a[j] / radix) % 10;
count[k]++;
}
for (j = 1; j < 10; j++) {
count[j] += count[j - 1];
}
for (j = n - 1; j >= 0; j--) {
k = (a[j] / radix) % 10;
tmp[count[k] - 1] = a[j];
count[k]--;
}
for (j = 0; j < n; j++) {
a[j] = tmp[j];
}
radix *= 10;
}
}
复杂度
时间复杂度: O ( n ∗ k ) O(n*k) O(n∗k) k为最大元素的位数
空间复杂度: O ( n + k ) O(n+k) O(n+k)
稳定性
基数排序是稳定的