题目

来源:力扣(LeetCode)

链接:https://leetcode-cn.com/problems/majority-element/

注意,该题在LC中被标注为easy,所以我们更多应该关注的是文章中不断优化的思路和方法。很多时候面试考察的,就是与面试官一起做题并把时间复杂度和空间复杂度压榨到极致的过程中你所表现出来的能力。

1.描述

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

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

2.示例

  • 示例 1:
	输入:[3,2,3]
输出:3
  • 示例 2:
	输入:[2,2,1,1,1,2,2]
输出:2

解法一 排序

解题思路

很快啊,啪的一下我们想到,由于多数元素是指在数组中出现次数 大于 ⌊n/2⌋ 的元素,那么将数组排序后,即便该元素是最小元素或是最大元素,其在数组中的覆盖范围也超过了数组中点(从左到右超过或是从右到左超过)。于是得到算法:

算法

1.将数组排序;

2.返回数组中点元素nums[nums.size()/2].

代码

1 class Solution {
2 public:
3 int majorityElement(vector<int>& nums) {
4 sort(nums.begin(), nums.end());//对数组进行排序
5 return nums[nums.size()/2];//返回数组中点元素
6 }
7 };

复杂度分析

时间复杂度: O(mlogm)。m为数组元素数,主要是排序的时间消耗。

空间复杂度: O(logm)。主要是系统sort函数需要的栈空间消耗。

解法二 哈希

解题思路

这时候,面试官说,你这解法一的时间复杂度很大啊,你能优化一下吗。稍加思索,我们想到,如果考虑使用哈希表空间换时间,则可以降低时间复杂度。我们可以使用哈希表存储数组中元素出现的次数,当某个元素的出现次数超过一半时,返回该元素。于是得到算法:

算法

1.初始化存储数组元素及其出现次数的哈希表map1,键为元素值,值为该元素在数组中的出现次数;

2.遍历数组元素,将被遍历数组元素的哈希值加一;

3.若该元素在数组中的出现次数超过数组元素数的一半,返回该元素。

代码

 1 class Solution {
2 public:
3 int majorityElement(vector<int>& nums) {
4 unordered_map<int, int> map1;//存储数组元素值及出现次数的哈希表
5 for(int i = 0; i < nums.size(); i++)//遍历数组元素
6 {
7 map1[nums[i]]++;//该元素出现次数加一
8 if(map1[nums[i]] > nums.size()/2)//若该元素出现此处超过一半
9 {
10 return nums[i];//返回该元素
11 }
12 }
13 return 0;//为了能通过编译
14 }
15 };

复杂度分析

时间复杂度: O(m)。遍历数组元素的时间开销。

空间复杂度: O(m)。哈希表的空间消耗。

解法三 随机算法

解题思路

这时候面试官又说了,你这解法二空间复杂度连解法一都不如,再想想有没有更好的解法。

手扶下巴,眉头一皱,我们又想到,由于多数元素是指在数组中出现次数大于⌊n/2⌋ 的元素,因此如果我们随机选取数组中的一个元素,那么将有超过1/2的概率选到该多数元素。于是可以使用这种算法:

算法

1.在数组中随机挑选一个元素;

2.遍历数组统计该元素出现次数,若超过数组元素数的一半,返回该元素,否则重新随机挑选一个元素。

代码

 1 class Solution {
2 public:
3 int majorityElement(vector<int>& nums) {
4 int m = nums.size();//计算数组元素数
5 srand(time(NULL));//设置随机数种子
6 int n;//存储随机数
7 int count = 0;//初始化以随机数为下标的元素的出现次数为0
8 while(count<=m/2){//当以随机数为下标的元素出现次数未超过数组元素数的一般
9 n = rand()%m;//获取一个随机数
10 count = 0;//初始化以随机数为下标的元素出现次数为0
11 for(int num : nums) if(num == nums[n]) ++count;//统计以随机数为下标的元素出现次数
12 }
13 return nums[n];//若以该随机数为下标的元素出现次数超过数组元素数的一半,返回以该随机数为下标的元素
14 }
15 };

复杂度分析

时间复杂度: O(m)。理论上最坏情况下复杂度为O(∞),但实际上由于多数元素出现次数超过数组元素数的一半,平均情况下只需要两次就能选出多数元素,主要的时间开销是统计出现次数,其时间复杂度为O(m)。

空间复杂度: O(1)。只需常数个额外空间。

解法四 摩尔投票法

解题思路

面试官觉得你这解法很有意思,然后又问,那要我就是非酋呢,那岂不是无限循环了,有没有更好的办法。

这下可让人犯了难,有什么更好的办法既能时间最优又能空间最优呢?

考虑这样一个场景,一群人打擂台,人群中分为不同的帮派,挨个上台比武。留在台上的帮派为擂主帮派,如果上台的人不是自己帮派的人,则将其干掉,代价是损失一个帮派成员;如果上台的人是自己帮派的人,则将其留下。则当所有人都参与完比武后,留在台上的必然可以是人数超过比武人数一半的帮派。而在本题中,多数元素出现次数超过数组元素数的一半,也即,其他所有元素加起来都没多数元素多。如果我们记多数元素的值为1,其他元素的值为0,将数组中所有元素值相加,结果必然大于0。于是我们可以设计这样一种算法:

算法

1.初始化候选答案ans为nums[0],初始化数值计算结果count为0;

2.遍历数组若该元素与备选答案相等,count加一,否则减一;

3.若count减为零,更换备选答案ans为当前元素,重置count为1,继续遍历后续元素。

遍历完成后返回最终的ans即为多数元素。

代码

 1 class Solution {
2 public:
3 int majorityElement(vector<int>& nums) {
4 int ans = nums[0];//初始化候选答案为nums[0]
5 int count = 0;//初始化数值计算结果为0
6 for(auto num : nums){//遍历数组
7 if(num==ans) ++count;//若该元素与备选答案相同,则count加一
8 else{//否则减一
9 --count;
10 if(count==0){//若count减为0
11 ans = num;//更换备选答案为当前元素
12 ++count;//重置count为1
13 }
14 }
15 }
16 return ans;//返回答案
17 }
18 };

复杂度分析

时间复杂度: O(m)。只需要对数组进行一次遍历即可

空间复杂度: O(1)。只需常数个额外空间。

Tips:

由于题目明确说明给定的数组中总是存在多数元素,因此本题不用考虑数组不存在多数元素的情况。若未做说明,需要像解法三那样,在选出ans以后再进行一次统计,验证该元素是否是多数元素,若果是,返回之;如果不是,返回不存在多数元素。时间复杂度和空间空间复杂度不变。

更多知识内容分享:

力扣个人主页https://leetcode-cn.com/profile/articles/

CSDN个人主页https://blog.csdn.net/qq_39255924

牛客个人主页https://blog.nowcoder.net/newcoderthewarrior

可可爱爱的蕾姆镇

【Warrior刷题笔记】力扣169. 多数元素 【排序 || 哈希 || 随机算法 || 摩尔投票法】详细注释 不断优化 极致压榨的更多相关文章

  1. 【Warrior刷题笔记】剑指offer 6 24 35. 三道题,让你学会链表递归迭代辅助栈

    题目一 从尾到头打印链表 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-l ...

  2. 【Warrior刷题笔记】剑指offer 32. 三道题,让你学会二叉树的深度广度优先遍历与递归迭代技术

    题目一 剑指 Offer 32 - I. 从上到下打印二叉树 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/cong-shang-dao-xi ...

  3. 【Warrior刷题笔记】143.重排链表 【线性化 || 双指针+翻转链表+链表合并】详细注释

    题目一 力扣143.重排链表 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/reorder-list/ 1.描述 给定一个单链表L的头节点he ...

  4. 《Data Structures and Algorithm Analysis in C》学习与刷题笔记

    <Data Structures and Algorithm Analysis in C>学习与刷题笔记 为什么要学习DSAAC? 某个月黑风高的夜晚,下班的我走在黯淡无光.冷清无人的冲之 ...

  5. Python 刷题笔记

    Python 刷题笔记 本文记录了我在使用python刷题的时候遇到的知识点. 目录 Python 刷题笔记 选择.填空题 基本输入输出 sys.stdin 与input 运行脚本时传入参数 Pyth ...

  6. PTA刷题笔记

    PTA刷题记录 仓库地址: https://github.com/Haorical/Code/tree/master/PTA/GPLT 两周之内刷完GPLT L2和L3的题,持续更新,包括AK代码,坑 ...

  7. leecode刷题(27)-- 合并k个排序链表

    leecode刷题(27)-- 合并k个排序链表 合并k个排序链表 合并 k 个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. 示例: 输入: [ 1->4->5, 1-> ...

  8. 《Algorithms算法》笔记:元素排序(3)——洗牌算法

    <Algorithms算法>笔记:元素排序(3)——洗牌算法 Algorithms算法笔记元素排序3洗牌算法 洗牌算法 排序洗牌 Knuth洗牌 Knuth洗牌代码 洗牌算法 洗牌的思想很 ...

  9. 《剑指offer》刷题笔记

    简介 此笔记为我在 leetcode 上的<剑指offer>专题刷题时的笔记整理. 在刷题时我尝试了 leetcode 上热门题解中的多种方法,这些不同方法的实现都列在了笔记中. leet ...

随机推荐

  1. 【编程思想】【设计模式】【其他模式】graph_search

    Python版 https://github.com/faif/python-patterns/blob/master/other/graph_search.py #!/usr/bin/env pyt ...

  2. rust方法集

    随机数.数字对比.控制台输入 use std::io; use std::cmp::Ordering; use rand::Rng; fn main() { println!("please ...

  3. NSURLConnection和Runloop

    - 1.1 涉及知识点(1)两种为NSURLConnection设置代理方式的区别 //第一种设置方式: //通过该方法设置代理,会自动的发送请求 // [[NSURLConnection alloc ...

  4. Redis哨兵日常维护

    目录 一.日常操作 指定一个从做新主 添加一个从节点 添加一个Setinel节点 一.日常操作 指定一个从做新主 有时候需要将当前主节点机器下线,并指定一个高一些性能的从节点接替 将其它从节点的sla ...

  5. PDF补丁丁将发布开放源代码的1.0版本

    近况 一个月前的今天,母亲永远离开了我. 想起四个月前,我送她了去住院.入院后,做了检查.检查结果没出,我的生日就到了.母亲很关心我的生日.在电话里,她祝我身体健康,又问媳妇有没有给我做生日餐桌的菜肴 ...

  6. vue3官网介绍,安装,创建一个vue实例

    前言:这一章主要是vue的介绍.安装.以及如何创建一个vue实例. 一.vue介绍 vue3中文官网:建议先自己看官网. https://v3.cn.vuejs.org/ vue是渐进式框架,渐进式指 ...

  7. Table.Skip删除前面N….Skip/RemoveFirstN(Power Query 之 M 语言)

    数据源: "姓名""基数""个人比例""个人缴纳""公司比例""公司缴纳"&qu ...

  8. PowerDotNet平台化软件架构设计与实现系列(08):缓存平台

    几乎所有后端应用都会或多或少用到缓存,尤其是分布式缓存服务,以及和本地缓存构造的二级缓存.根据我们一贯的节约代码的风格,为了复用的目标,抽象出缓存平台,进行缓存管理. 考虑到很多公司都会自己造或者直接 ...

  9. Linux(centos)使用docker安装pdf2htmlEX

    pdf2htmlEX是一款可以将pdf文档转换成html文件的插件,但是Linux系统安装起来很麻烦,所以我们使用docker进行安装 首先要安装docker 因为国外镜像很慢,所以我们这边修改使用国 ...

  10. SpringBoot整合quartz实现动态启动,停止定时任务功能

    注意:这个方法当程序重启之后会失效,所以必须将定时任务持久化到数据库,然后程序启动的时候重新把数据库的定时任务加载到quartz中 springboot程序启动初始化代码参考:https://www. ...