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,需要比较 a1 = 2 和 a0 = 1,即 1 次比较。

i = 2,需要比较 a2 = 3 和 a1 = 2、a0 = 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;
}
相关推荐
我是慎独9 分钟前
VulkanSceneGraph学习教程(十一)
c++·学习
imaol118 分钟前
文件编程--标准IO
linux·运维·算法
青 春 记 忆22 分钟前
LeetCode 121. 买卖股票的最佳时机|Python 解法详解
python·算法·leetcode
stolentime34 分钟前
AT_agc066_a [AGC066A] Adjacent Difference题解
c++·算法·贪心算法·构造
程序猿编码38 分钟前
不用PyTorch,我用C++手写了一个能认手写数字的神经网络
c++·pytorch·神经网络·大模型
不会代码的小猴43 分钟前
4. 控件学习2
开发语言·c++·笔记·qt
渣波1 小时前
深度解析:LeetCode 51. N 皇后 —— 回溯算法的巅峰之作
算法
咪饭只吃一小碗1 小时前
# C++ STL 常用容器方法全面总结vector / map / set / unordered_map / unordered_set
算法
daad7771 小时前
802.11 前导码与 STF 深度解析(含检测算法与 5G 对比
人工智能·算法·5g·wifi·802.11·前导码
闻道且行之2 小时前
图片处理助手|C++ 手搓离线 AI 抠图工具,U2Net 原理到落地一次讲透
开发语言·c++·人工智能·神经网络·opencv·计算机视觉