题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
https://leetcode.cn/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/
题解
排序
排序后,数组中出现次数超过一半的数字一定在中间位置。
1 2 3 4 5 6 7
| class Solution { public: int majorityElement(vector<int>& nums) { sort(nums.begin(), nums.end()); return nums[nums.size() / 2]; } };
|
时间复杂度:O(nlogn)
空间复杂度:O(logn),sort() 一般情况认为是快排(实际更复杂),有 O(logn) 的递归深度;也可以自己在原数组上实现堆排,空间复杂度为 O(1)。
哈希
遍历数组,统计数字出现的次数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Solution { public: int majorityElement(vector<int>& nums) { unordered_map<int, int> num_count; int res = -1; for (int i = 0; i < nums.size(); ++i) { ++num_count[nums[i]]; if (num_count[nums[i]] > nums.size() / 2) { res = nums[i]; break; } } return res; } };
|
时间复杂度:O(n)
空间复杂度:O(n)
随机选数
因为在数组中出现次数超过一半,我们可以随机选中数组中的一个数,再判定这个数是不是我们想要的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public: int majorityElement(vector<int>& nums) { while (true) { int candidate = nums[rand() % nums.size()]; int count = 0; for (int num : nums) { if (candidate == num) count++; } if (count > nums.size() / 2) return candidate; } return -1; } };
|
时间复杂度:O(n),理论上最坏的情况为 O(∞),也就是我们永远选不到答案;但是因为在数组中出现的次数超过一半,所以选中的概率是大于二分之一的,期望的时间复杂度为 O(n)。
空间复杂度:O(1)
分治
如果数 a 是数组 nums 的众数,如果我们将 nums 分成两部分,那么 a 必定是至少一部分的众数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Solution { int count_in_range(vector<int>& nums, int target, int lo, int hi) { int count = 0; for (int i = lo; i <= hi; ++i) if (nums[i] == target) count++; return count; } int majority_element_rec(vector<int>& nums, int lo, int hi) { if (lo >= hi) return nums[lo]; int mid = (lo + hi) / 2; int left_majority = majority_element_rec(nums, lo, mid); int right_majority = majority_element_rec(nums, mid + 1, hi); if (count_in_range(nums, left_majority, lo, hi) > (hi - lo + 1) / 2) return left_majority; if (count_in_range(nums, right_majority, lo, hi) > (hi - lo + 1) / 2) return right_majority; return -1; } public: int majorityElement(vector<int>& nums) { return majority_element_rec(nums, 0, nums.size() - 1); } };
|
时间复杂度:O(nlogn),类比归并排序的二路分治。
空间复杂度:O(logn),递归深度为 O(logn),没有其他额外空间。
Boyer-Moore 投票算法
如果数组中两个元素不相等,我们就同时删除这两个元素,最后剩下的一定是出现次数超过一半的数字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Solution { public: int majorityElement(vector<int>& nums) { int res = nums[0]; int count = 1; for (int i = 1; i < nums.size(); ++i) { if (nums[i] == res) ++count; else count--; if (count == 0) { res = nums[i]; count = 1; } } return res; } };
|
时间复杂度:O(n)
空间复杂度:O(1)