在 leet code oj 上, 刷到 Third Maximum Number
这类问题,总结记录下解决此类问题的方法。
题目描述
Given a non-empty array of integers, return the third maximum number in this array. If it does not exist, return the maximum number. The time complexity must be in O(n).
Example 1:
Input: [3, 2, 1]
Output: 1
Explanation: The third maximum is 1.Example 2:
Input: [1, 2]
Output: 2
Explanation: The third maximum does not exist, so the maximum (2) is returned instead.Example 3:
Input: [2, 2, 3, 1]
Output: 1
Explanation: Note that the third maximum here means the third maximum distinct number.
Both numbers with value 2 are both considered as second maximum.
简而言之, 就是在一个包含N个数的数组中,找出第3个最大值,时间复杂度请控制在O(n) 范围内。如果不存在第3个最大值,请返回数组的最大值。
该题不具备通用性,指定只需要第3大的数,可以通过只需维护三个最大数,遍历数组与之比较,从而就可以得到结果,时间复杂度为 O(3n),但是如果是需要第k大的数,就需要维护k个最大数,最坏情况下,k=n,复杂度为 O(n^2).
如下解法,即为维护三个最大数的解法:
1 | class Solution { |
解题思路
- 使用排序算法,排序后,取出第k个数,最优复杂度 O(nlogn+k)
- 使用选择排序,或者冒泡排序, K次选择后,时间复杂度为 O(KN)
- 利用快速排序的思想,从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。这时有两种情况:
- Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;
- Sa中元素的个数大于等于k,则返回Sa中的第k大数。时间复杂度近似为O(n)
- 二分[Smin,Smax]查找结果X,统计X在数组中出现,且整个数组中比X大的数目为k-1的数即为第k大数。时间复杂度平均情况为O(nlogn)
- 用O(4n)的方法对原数组建最大堆,然后pop出k次即可。时间复杂度为O(4n + klogn)
- 维护一个k大小的最小堆,对于数组中的每一个元素判断与堆顶的大小,若堆顶较大,则不管,否则,弹出堆顶,将当前值插入到堆中。时间复杂度O(nlogk)
- 利用hash保存数组中元素Si出现的次数,利用计数排序的思想,线性从大到小扫描过程中,前面有k-1个数则为第k大数,平均情况下时间复杂度O(n)
- 利用STL的SET有序属性,对数组建立set,复杂度 O(nlogn)
解决代码
解法2对应的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39class Solution {
public:
int thirdMax(vector<int>& nums) {
int n = nums.size();
int maxIndex = 0;
int maxValue = nums[0];
if(n==1)
return nums[0];
else if(n==2)
return nums[0]>nums[1]?nums[0]:nums[1];
//选择排序,但是无法确定重复数有多少,所以不能确定排多少次,故全排
for(int i=0; i<n-1;i++){
maxIndex = i;
maxValue = nums[i];
for(int j=i+1; j<n ;j++){
if(nums[j] > maxValue){
maxValue = nums[j];
maxIndex = j;
}
}
int temp = nums[i];
nums[i] = maxValue;
nums[maxIndex] = temp;
}
// 返回第K个数,跳过重复值
int count = 1;
for(int i=0; i<n-1; i++){
if(nums[i] == nums[i+1])
continue;
else{
count++;
if(count == 3)
return nums[i+1];
}
}
return nums[0];
}
};解法3对应的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48#include <iostream>
using namespace std;
const int N = 8;
const int K = 4;
int partition(int a[] ,int low , int high)
{
int i = low - 1;
int j = low;
while(j < high){
if(a[j] >= a[high]){
swap( a[i+1] , a[j]);
i++;
}
j++;
}
//最后处理a[high]
swap(a[i+1] , a[high]);
return i + 1;
}
int findk(int a[] , int low , int high , int k)
{
if(low < high){
int q = partition(a , low , high) ;
int len = q - low + 1 ; //表示第几个位置
if(len == k)
return q ; //返回第k个位置
else if(len < k)
return findk(a , q + 1 , high , k - len) ;
else
return findk(a , low , q - 1, k ) ;
}
}
int main()
{
int a[N] = {5 ,2 ,66 ,23, 11 ,1 ,4 ,55} ;
findk(a , 0 , N - 1 , K) ;
for(int i = 0 ; i < K ; i++)
cout<<a[i]<<endl ;
system("pause") ;
return 0 ;
}解法8对应的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Solution {
public:
// 建立 set,顺序从大至小, set 的大小只需要维护 k 个size即可
int kMax(vector<int>& nums, int k){
set<int> topN;
for(auto num: nums){
topN.insert(num);
//如果超出k个,剔除掉当前的第k大数
if(topN.size()>k)
topN.erase(topN.begin());
}
//如果不存在第k大数,直接返回最大值
return (topN.size() >= k) ? *(topN.begin()): *(topN.rbegin());
}
int thirdMax(vector<int>& nums) {
return kMax(nums,3);
}
};
注意: 数组中可能存在重复值,重复值需要进行过滤。