众数问题(递归与分治)

众数问题

给定一个含有 n 个元素的多重集合 S,每个元素 S 出现的次数称为该元素的重数。多重集 S 中重数最大的元素称为众数。例如,集合S={1,2,2,2,3,5} 的众数为 2,重数为 3。

1. 算法原理

该算法通过递归分治法来寻找多重集合中的众数。具体步骤如下:

选择中位数

  • 使用 median() 函数选择当前数组的中位数。

分割数组

  • 使用 split() 函数将数组分割为两部分:小于等于中位数的部分和大于中位数的部分。

递归查找

  • 计算分割后两部分的大小。
  • 如果某一部分的大小大于当前已知的最大重数,则递归查找该部分。
  • 否则,继续查找另一部分。

更新众数

  • 在递归过程中,更新当前找到的最大重数和对应的元素。
2. 算法步骤

初始化

  • 定义数组 a[] 存储多重集合的元素。
  • 定义变量 largest 用于记录当前找到的最大重数。
  • 定义变量 element 用于记录当前找到的众数。

递归查找

  • 调用 mode(l, r) 函数开始递归查找众数。
  • 在 mode() 函数中,选择中位数并分割数组。
  • 根据分割后两部分的大小,递归查找最大重数的部分。

更新众数

  • 在递归过程中,更新 largest 和 element
3. 图示法表示步骤

假设我们有以下多重集合:

S = {1, 2, 2, 2, 3, 5}
步骤 1:初始化
  • largest = 0
  • element = -1
  • a = [1, 2, 2, 2, 3, 5]
步骤 2:递归查找

第一次递归

  • 选择中位数 med = 2
  • 分割数组:l1 = 0r1 = 3
  • 更新 largest = 3element = 2
  • 递归查找 mode(0, 0) 和 mode(4, 5)

第二次递归

  • mode(0, 0):数组为空,返回。
  • mode(4, 5):选择中位数 med = 3
  • 分割数组:l1 = 4r1 = 5
  • 更新 largest = 3element = 2
  • 递归查找 mode(4, 3) 和 mode(6, 5)

第三次递归

  • mode(4, 3):数组为空,返回。
  • mode(6, 5):数组为空,返回。
最终结果
  • 众数为 2,重数为 3
4. 代码关键行注释
#include <iostream>
#include <algorithm>
using namespace std;

// median()函数用于寻找中位数
int median(int a[], int l, int r) {
    sort(a + l, a + r + 1); // 对数组进行排序
    return a[(l + r) / 2]; // 返回中位数
}

// split()函数用中位数将数组分割为2段
void split(int a[], int med, int l, int r, int& l1, int& r1) {
    int i = l, j = r;
    while (i <= j) {
        while (a[i] <= med) i++;
        while (a[j] > med) j--;
        if (i < j) swap(a[i], a[j]);
    }
    l1 = l;
    r1 = j;
}

// mode()函数递归查找众数
void mode(int a[], int l, int r, int& largest, int& element) {
    if (l > r) return; // 如果数组为空,返回
    int med = median(a, l, r); // 选择中位数
    int l1, r1;
    split(a, med, l, r, l1, r1); // 分割数组
    int size = r1 - l1 + 1; // 计算分割后数组的大小
    if (size > largest) { // 如果当前大小大于最大重数
        largest = size; // 更新最大重数
        element = med; // 更新众数
    }
    if (l1 - l > largest) { // 如果左部分的大小大于最大重数
        mode(a, l, l1 - 1, largest, element); // 递归查找左部分
    }
    if (r - r1 > largest) { // 如果右部分的大小大于最大重数
        mode(a, r1 + 1, r, largest, element); // 递归查找右部分
    }
}

int main() {
    int n;
    cin >> n; // 输入元素个数
    int a[n];
    for (int i = 0; i < n; i++) {
        cin >> a[i]; // 输入元素
    }
    int largest = 0, element = -1;
    mode(a, 0, n - 1, largest, element); // 调用mode()函数查找众数
    cout << element << endl; // 输出众数
    cout << largest << endl; // 输出重数
    return 0;
}
5. 时间复杂度
  • 时间复杂度
    • median() 函数的时间复杂度为 O(nlogn),因为需要对数组进行排序。
    • split() 函数的时间复杂度为 O(n)。
    • mode() 函数的时间复杂度为O(nlogn),因为递归深度为logn,每次递归调用 median() 和 split()
    • 总时间复杂度为 O(nlogn)。
6. 总结

通过递归分治法,可以高效地寻找多重集合中的众数。该算法的时间复杂度为线性级别,适用于各种大小的多重集合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值