Leetcode刷题记录-多数元素(简单)-面试经典 150 题

文章介绍了如何在给定数组中寻找多数元素(出现次数超过数组长度一半的元素)的三种方法。首先,简单暴力的双重循环法虽然能解决问题但时间复杂度较高。接着,通过排序数组找到中位数的方法降低了时间复杂度至O(nlogn),但空间复杂度仍为O(logn)。最后,使用摩尔投票算法实现了O(n)时间复杂度和O(1)空间复杂度的解决方案。
摘要由CSDN通过智能技术生成

169. 多数元素(简单)

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入:nums = [3,2,3]
输出:3

示例 2:

输入:nums = [2,2,1,1,1,2,2]
输出:2

提示:

  • n == nums.length
  • 1 <= n <= 5 * 10^4
  • -109 <= nums[i] <= 109

进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。

解题思路:

        首先方法需要int类型的返回值,返回数组中的多数元素(指在数组中出现次数大于nums.length/2的元素),可以假设数组非空,并且总是存在多数元素,也可以理解为众数。

方法①:

        第一眼看到后的最简单的想法就是暴力破解,双重for循环,外层枚举数组中每一个元素,内层再遍历一遍数组统计其出现次数。只要超过数组元素个数的一半,那么就可以返回该元素。

具体的Java实现代码如下:

class Solution {
    public int majorityElement(int[] nums) {
       int number=0;
        if(nums==null || nums.length==0){
            return 0;
        }
        else if(nums.length==1){
            number=nums[0];
            return number;
        }
       
        for(int i=0;i<=nums.length-1;i++){
             int count=1;
            for(int j=i+1;j<=nums.length-1;j++){
                if(nums[i]==nums[j]){
                    count++;
                }      
            }
             if(count>nums.length/2){
                   number=nums[i]; 
                 }
        }
        return number;
}
}

        但是这个算法的时间复杂度为 O(n^2),其中 n 是数组的长度。外层循环需要遍历数组中的每一个元素,内层循环需要遍历数组中剩余的元素,因此总共需要进行 n(n-1)/2 次比较。算法的空间复杂度为 O(1),因为只需要常数级别的额外空间用于存储变量,没有使用额外的数组或集合来存储元素。

 

虽然提交成功了,但是花费的时间太长了,所以这种暴力破解的方式不可取,需要优化。

方法②:

        重新审视一下题意,多数元素即数组中的众数,出现次数超过数组元素的一半,但是因为数组内的元素是乱序的,所以没办法判断。但是如果是有序数组的话,那么数组中间位置的元素那么必定就是多数元素。因为出现次数会超过数组元素的一半!可行。因此接收到数组后先进行排序!

具体的Java实现代码如下:

class Solution {
    public static int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length >> 1];
    }
}

        return nums[nums.length >> 1]; 返回排序后的数组 nums 中索引为 nums.length >> 1 的元素,也就是中间位置的元素,即为该数组的众数。

        PS:nums.length >> 1 是一个位运算操作,它等价于将数组 nums 的长度除以二(即右移一位),得到中间位置的索引。

在这段代码中,我们已经将数组 nums 排好序了,所以中间位置的数就是众数。因为众数最多,所以无论是数组长度为偶数还是奇数,中间位置的数都会是众数或其一个相邻的数,因此返回 nums[nums.length >> 1] 可以有效地找到众数。

进行用例测试,看看能否解决需求:

可以看到这次的速度快了不止一点,成功满足需求!

进行题后反思:这段代码的时间复杂度是 $O(n\log n)$,其中 $n$ 是输入数组的长度。这是因为 Arrays.sort(nums) 的时间复杂度是 $O(n\log n)$,所以整个算法的时间复杂度也是 $O(n\log n)$。空间复杂度是 $O(\log n)$,因为 Arrays.sort(nums) 使用了快速排序算法,其空间复杂度是 $O(\log n)$。所以整个算法的空间复杂度与快速排序算法的空间复杂度相同,都是 $O(\log n)$。

方法③: 

         再进阶考虑一下,尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。

思路如下:

       要设计时间复杂度为 $O(n)$、空间复杂度为 $O(1)$ 的算法解决此问题,可以使用摩尔投票算法(Boyer-Moore Voting Algorithm)。算法基于一个简单的事实:出现次数大于 $\lfloor n/2\rfloor$ 的数字最多只能有一个,因此只需要遍历一遍数组,记录当前数字出现的次数,同时记录候选数字,如果遍历到下一个数字和候选数字相同则次数加一,否则次数减一,当次数为零时更换候选数字。最后剩下的候选数字就是出现次数大于$\lfloor n/2\rfloor$ 的数字。

具体的Java实现代码如下:

 

class Solution {
    public int majorityElement(int[] nums) {
        int count = 0, candidate = 0;   // 定义计数器和候选数字
        for (int num : nums) {          // 遍历数组
            if (count == 0) {          // 若计数器为 0
                candidate = num;       // 则当前数字作为候选数字
            }
            count += (num == candidate) ? 1 : -1; //使用三元运算符?  如果当前数字和候选数字相同,则计数器加 1,否则减 1
        }
        return candidate;  // 返回候选数字,即出现次数大于 n/2 的数字
    }
}

进行用例测试,看看能否解决需求:

可以看到成功满足需求!

题后反思:

        方法③的代码时间复杂度是 $O(n)$,因为只遍历了一遍数组,并且没有额外的循环或递归操作。而空间复杂度是 $O(1)$,因为它只使用了常量级别的额外空间来存储计数器和候选数字。满足我们进阶的需求!

时间:2023/05/06-23:13分 以上仅为个人对力扣刷题的学习记录,有错误欢迎各位指出,交流学习!感谢观看

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Stevedash

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值