求众数

博客围绕LeetCode 169求众数问题展开,给定非空数组且存在众数(出现次数大于 ⌊ n/2 ⌋ )。介绍了四种解法,包括排序后取中位数、摩尔投票法、基于Partition函数的分治法和基于HashMap的统计,还提及了各方法的时间和空间复杂度。
题目

leetcode 169 求众数
给定一个大小为 n 的数组,找到其中的众数。众数是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在众数。

总结

方法一:排序后取中位数

按照数组的特点,若一个数在数组中出现次数大于一半,则当给该数组排序后,该数字一定会出现在中间位置。

到了JDK7的时候JDK内置的排序算法已经由经典快排变成了Dual-Pivot排序算法。快速排序的时间复杂度为O(nlogn)。经典快排递归的时候把输入数组分两段,而Dual-Pivot则分三段。

class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        // 当为奇数时,返回最中间数字;当为偶数时,返回偏右侧数字。
        return nums[nums.length / 2];
    }
}

方法二:基于数组特点的摩尔投票法

我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字;另一个是次数。当我们遍历到下一个数字的时候,如果count=0,说明从此个数字重新开始算,result需要更新为当前数字。如果count!=0,说明下一个数字如果相同,则count++,如果不同,则抵消一个,count–。

该方法只需要遍历一遍数组,故时间复杂度为O(n)。

class Solution {
    public int majorityElement(int[] nums) {
        int len = nums.length;
        int result;
        int count;

		// 要考虑数组为空的情况
        if (len == 0) {
            return 0;
        } else {
            result = nums[0];
            count = 1;
        }

		// 从第二个元素开始遍历
        for(int i = 1; i<nums.length; i++){
            if(count == 0){
                result = nums[i];
                count = 1;
            }else if(nums[i] == result)
                count++;
            else
                count--;
        }

		// 要考虑数组中不存在众数的情况
        if (count > 0) {
            return result;
        }else {
            return 0;
        }
    }
}

方法三:基于Partition函数的分治法

如果把这个数组排序,那么排序之后位于数组中间的数字一定就是那个出现次数超过数组长度一般的数字。即,我们只用找出长度为n的数组中第n/2大的数字。于是可以用快速排序的思想,找到第n/2大的数字。

该方法时间复杂度为O(n),空间复杂度为O(1),但是需要在原数组上进行改动。注:如果代码中加System.out可能会在leetcode上超出时间限制。

class Solution {
    public int majorityElement(int[] nums) {
        // 我们的目的是找出排序在第mid的数
        int len = nums.length;
        int mid = len / 2;

        if(len == 1) {
            return nums[0];
        }

        int start = 0;
        int end = len - 1;
        int index = partition(nums, start, end);

        while (index != mid) {
            if (index > mid) {
                end = index - 1;
                index = partition(nums, start, end);
            } else {
                start = index + 1;
                index = partition(nums, start, end);
            }
        }

        return nums[mid];
    }

    // partition函数是为了找出nums[0]在排序中的第几个位置
    public int partition(int[] nums, int start, int end) {
        int i = start;
        int j = end;
        int pivot = nums[start];

        if (i >= j) {
            return i;
        }

        while (i < j) {
            while (pivot <= nums[j] && i < j) {
                j--;
            }
            while (pivot >= nums[i] && i < j) {
                i++;
            }
            //如果满足条件则交换
            if (i < j) {
                int temp = nums[j];
                nums[j] = nums[i];
                nums[i] = temp;
            }
        }
        nums[start] = nums[i];
        nums[i] = pivot;

        return i;
    }
}

方法四:基于HashMap的统计

可以利用HashMap保存每对键值出现的次数。

这种方式的时间复杂度是O(n),空间复杂度也是O(n)。

class Solution {
    public int majorityElement(int[] nums) {
        int len = nums.length;
        HashMap<Integer, Integer> map = new HashMap<>();
        if (len == 1) {
            return nums[0];
        }

        for(int i = 0; i < len; i++) {
            if (!map.containsKey(nums[i])) {
                map.put(nums[i], 1);
            } else {
                int count = map.get(nums[i]);
                count++;
                if (count > len/2) {
                    return nums[i];
                } else {
                    map.put(nums[i], count);
                }
            }
        }

        return 0;
    }
}

在 pandas 库中,有多种众数的方法: 1. **按列获取众数**:从 `pandas.DataFrame` 调用 `mode()` 方法,返回一个 `pandas.DataFrame`,其元素默认为每一列的众数值。即使只有一种模式,也会返回一行的 `pandas.DataFrame` [^1]。 ```python import pandas as pd # 示例据框 df = pd.DataFrame({'col1': [1, 2, 2, 3], 'col2': [4, 4, 5, 6]}) print(df.mode()) ``` 2. **对 `dataframe` 进行 `groupby` 后众数**:使用 `pd.Series.mode()` 函,该函返回 `Series` 的众数,当众数有多个时,会返回一个 `list`,里面包含了所有众数 [^2]。 ```python import pandas as pd df = pd.DataFrame({'a': ['A', 'A', 'B', 'B'], 'b': [1, 1, 2, 3]}) print(df.groupby('a').agg(pd.Series.mode).reset_index()) ``` 3. **行列众数及按列去重**:可以通过 `mode()` 方法众数,通过指定 `axis=1` 特定列的行众数 [^3]。 ```python import pandas as pd df2 = pd.DataFrame(data={'A': [1, 2, 3, 3, 4, 4, 4, 4], 'B': ['a', 'b', 'c', 'c', 'd', 'd', 'd', 'd'], 'C': [11, 22, 33, 33, 44, 44, 44, 44], 'D': [11, 22, 33, 33, 44, 44, 44, 44]}) print(df2.mode()) # 众数 print(df2.loc[:, ['A', 'C', 'D']].mode(axis=1)) # 特定列的行众数 ``` 4. **统计值型据的众数出现的频**:先将值型据转换成类别型据,然后用 `describe()` 进行统计,类型转换用 `astype()` 实现 [^4]。 ```python import pandas as pd detail = pd.read_excel('./meal_order_detail.xlsx', sep=',', encoding='gbk') detail['amounts'] = detail['amounts'].astype('category') print(detail['amounts'].describe()) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值