C++语言程序设计——12 排序算法-桶排序

目录

* for循环去重元素

for 循环去重的思路在于找出首次出现的唯一元素,然后按照顺序查找是否有重复。该方法适合小规模,大规模需要使用桶排序或哈希表。双重 for 循环如下代码:

cpp 复制代码
#include <iostream>
using namespace std;

int main()
{
    int a[] = {64,12,64,11,30,3,95,0,40,0,9};
    int n = sizeof(a) / sizeof(a[0]);      // 计算数组长度
    cout << a[0] << " ";         // 先输出第一个元素
    for(int i = 1; i < n; i++)   // 从第二个元素开始遍历,即 i = 1开始
    {
        int temp = a[i];    // temp 是当前要检查的元素
        for(int j = i-1; j >= 0; j--)    // 向左查找是否已经出现过相同的元素
        {
            if(temp==a[j]){
                break;     // 如果前面有相同的元素,则跳出循环
            }
            if(temp!=a[j] && j==0){       // 如果查找到最前面(j==0)都没找到相同的,则输出当前元素
                cout << temp << " ";
            }
        }
    }
    return 0;
}        

时间复杂度

(1)最坏时间复杂度:无重复元素,每次循环需要比较前 i - 1 个元素,例如数组:a[ ] = {1, 2, 3, 4, 5}:

i = 1,需要比较 a[1] = 2 和 a[0] = 1,即 1 次比较。

i = 2,需要比较 a[2] = 3 和 a[1] = 2、a[0] = 1,即 2 次比较。

......

比较次数 = 1 + 2 + ... + (n - 1) = (1 + (n - 1) )n / 2 = n (n - 1) / 2。

所以,最坏时间复杂度为 O(n2)。

(2)最好时间复杂度:

最坏时间复杂度:所有元素重复,每次循环 1 次比较就 break 退出循环。

比较次数 = n - 1 。

所以,最好时间复杂度为 O(n)。

(3)平均时间复杂度:

关注最高阶项,忽略常数和低阶项,所以平均时间复杂度为 O(n2)。
空间复杂度:O(1)

一、桶排序的效率

桶是一种空间换时间(占用更多内存,但速度快)的数据结构思想,通过下标映射元素值,一次访问即可完成检查。每个桶内的排序可以选择不同的算法,例如,插入排序、快速排序、归并排序等。最后,再将所有桶合并。

要注意,桶排序和堆排序不是一个概念,桶排序是 " 分桶排序合并 ",堆排序是 " 建堆取最值调整 "(完全二叉树)。另外,桶排序也不属于严格意义上的分治法,桶排序是" 分桶排序合并 ",而分治法是 "分治合"。

之前通过双层 for 循环去重元素,平均时间复杂度是 O(n2) ,效率低;而通过桶排序平均时间复杂度可以达到 O(n + k)(k为桶数量),效率高。

桶排序所需空间复杂度为 O(n+k),n 是每个桶内存储元素的平均时间, k 是桶所需空间。

桶排序不适合数据范围很大的情况,以免设置桶的数量过多。此外,如果数据元素分布不均匀,桶排序效率会下降,例如,当所有元素都集中在少数几个桶或集中在一个桶时,时间复杂度是 O(n2) 。

二、分桶过程

桶排序常结合数组、vector、链表等实现,桶数量由值域范围和桶大小决定。

复制代码
桶数量 = (最大值 - 最小值) / 桶大小 + 1

例如,通过桶排序方式:

cpp 复制代码
int a[] = {4,40,44,49,75,40,10,38,1,29};

最小值为 1 ,最大值为 75 ,假设每个桶大小为 10 ,则需要 8 个桶:

复制代码
桶数量 = (75-1)/10 + 1 = 8
cpp 复制代码
vector<vector<int>> buckets(8);

每个桶装的元素如下,进行分配:

然后,进行每个桶内排序,如果桶内元素较少可以使用【插入排序】,元素较多使用【快速排序】或【归并排序】:

最终合并结果:

复制代码
1, 4, 10, 29, 38, 40, 40, 44, 49, 75

示例代码如下,最后用 sortedArr 数组存储排序后的结果,遍历该数组输出:

cpp 复制代码
#include <iostream>
using namespace std;

int main() 
{
    int arr[] = {70,31,88,1,55,42,7,38,13,49,95,16,25,9,29,38,5,35,22,66};
    int n = sizeof(arr) / sizeof(arr[0]);    // 计算原数组元素个数

    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }

    // 手动找出数组中的最大值(确定桶的遍历范围)
    int maxVal = arr[0];
    for (int i = 1; i < n; i++) {
        if (arr[i] > maxVal) {
            maxVal = arr[i];
        }
    }

    // 创建桶数组
    int bucket[1001] = {0};      // 初始化为0

    // 统计每个数字出现的次数
    for (int i = 0; i < n; i++) {
        bucket[arr[i]]++;
    }

    // 按顺序存入排序结果数组
    int sortedArr[100] = {0}; // 存储排序后的结果
    int sortedLen = 0;        // 记录排序后的元素个数
    for (int i = 0; i <= maxVal; i++) {
        // 按出现次数添加到结果数组
        while (bucket[i] > 0) {
            sortedArr[sortedLen] = i;
            sortedLen++;
            bucket[i]--;
        }
    }

    for (int i = 0; i < sortedLen; i++) {
        cout << sortedArr[i] << " ";
    }
    return 0;
}
相关推荐
kgduu2 小时前
Gin源码解析
算法·gin
淀粉肠kk2 小时前
【数据结构】哈希表
数据结构·c++
郝学胜-神的一滴2 小时前
Linux C++会话编程:从基础到实践
linux·运维·服务器·开发语言·c++·程序人生·性能优化
AA陈超2 小时前
LyraStarterGame_5.6 Experience系统分析
开发语言·c++·笔记·学习·ue5·lyra
历程里程碑2 小时前
C++ 8:list容器详解与实战指南
c语言·开发语言·数据库·c++·windows·笔记·list
小尧嵌入式2 小时前
C++11线程库的使用(上)
c语言·开发语言·c++·qt·算法
蓝色汪洋3 小时前
luogu填坑
开发语言·c++·算法
暗然而日章3 小时前
C++基础:Stanford CS106L学习笔记 9 类模板(Class Templates)
c++·笔记·学习
小年糕是糕手3 小时前
【C++同步练习】类和对象(三)
开发语言·jvm·c++·程序人生·考研·算法·改行学it