LeetCode:274. H-Index & 275. H-Index II
这一题算是第一次写桶排序,然后自己又没有看出来可以用二分法。
二分法真的很重要,只要可以用,基本就是最优的方法。符合之前总结的规律就可以用。大多数情况下不是一眼就能够看出来,所以需要时刻提醒一下自己,是不是可以用二分法来做。
首先是自己的做法:
class Solution {
public:
int hIndex(vector<int>& citations) {
sort(citations.begin(), citations.end());
int sz = citations.size();
if (sz == 0)
return 0;
int num = citations[sz-1];
int cnt = 0;
int i = sz-1;
while (num > 0) {
while (i >= 0 && citations[i] == num) {
++cnt;
--i;
}
if (cnt >= num)
return num;
--num;
}
return num;
}
};
从最大数开始往下降,统计比它大的数有多少个。时间O(nlogn),优点是不用额外空间。
然后是桶排序的方法:
class Solution {
public:
int hIndex(vector<int>& citations) {
int sz = citations.size();
if (sz == 0)
return 0;
int maxNum = *max_element(citations.begin(), citations.end());
vector<int> cnts(maxNum+1, 0);
for (int num : citations)
cnts[num]++;
int cnt = 0;
for (int i = cnts.size()-1; i >= 0; --i) {
cnt += cnts[i];
if (cnt >= i)
return i;
}
return 0;
}
};
缺点是需要额外空间。优点是时间O(n)。
然后二分法:
应该可以观察到:最终答案上界:数组最大值,下界:1。然后其中以某个数i为界,左边k(k <= i)都可以满足“比k大的数至少有k个“;右边不能够满足。所以这一题能够用二分法。
这是第二道的代码
class Solution {
public:
int hIndex(vector<int>& citations) {
int sz = citations.size();
if (sz == 0)
return 0;
int lo = 1, hi = citations[sz-1];
int mid;
while (lo <= hi) {
mid = lo + (hi - lo) / 2;
int idx = binarySearch(citations, 0, sz-1, mid);
int rightCnt = sz - idx;
if (rightCnt >= mid)
lo = mid + 1;
else
hi = mid - 1;
}
return hi;
}
private:
int binarySearch(vector<int>& nums, int lo, int hi, int target) {
//如果有重复的,返回最左边的;如果找不到,返回该在位置的右边那个
int i = lo, j = hi;
int mid;
while (i <= j) {
mid = i + (j - i) / 2;
if (target < nums[mid])
j = mid - 1;
else if (target > nums[mid])
i = mid + 1;
else {
if (mid == lo || nums[mid-1] != target)
return mid;
else
j = mid - 1;
}
}
return i;
}
};
//啊啊啊又是一题可以用二分来做的,怎么又没有看出来???
//要有这样的直觉啊。
应该要有这样的直觉的。