多域名网站整合营销传播策略
[ 题目描述 ]:
[ 思路 ]:
- 题目给出一个作者的n篇论文各自的引用次数,要求求一个值h,h的意义是至少有 h 篇论文被引用次数大于等于 h,h是其中最大的值
- 一个最简单的想法就是将h从1开始遍历,并记录
- 当被引用次数大于h的总数大于h时,将h加1,重复这个操作
- 当被引用次数大于h的总数大于h时,返回h
- 当被引用次数大于h的总数小于h时间,返回h-1
- 运行如下
int hIndex(int* citations, int citationsSize) {int h=1,sum=0;for(int i=0;i<citationsSize;i++){for(int j=0;j<citationsSize;j++){if(citations[j]>=h){sum++;}}if(sum>h){h++;}else if(sum==h){return h;}else{return h-1;}sum=0;}return h;
}
[ 优化 ]:
- 时间复杂度O(n2),空间复杂度O(1)
- 优先优化时间复杂度,由上面的做法来看,我们可以统计每个被引用的次数有多少篇论文,因为论文总数sum已知,所以被引用次数的情况最多只有sum次,然后遍历一次数组即可得出,但存储被引用次数的论文比较麻烦,如果用一维数组,那如果被引用次数很大的话,那么数组长度是未知的(题目给出是1000)
- 或者二维数组,第一行存储被引用次数的种类,第二行存储该被引用次数的论文数量,但空间复杂度就变为O(2n)了
- 换个思路,论文总数已知为n,那么h的最大值也就是n,那么,创建一个长度为n的数组sum记录每个h值情况下的被引用次数,数组下标+1为表示h,其值为大于等于该h值的个数;然后遍历citations数组,为下标小于citations[i]位置的值+1,最后遍历一遍sum,得出h;但这个思路的时间复杂度也为O(n2),空间复杂度为O(n),反而是增加了空间复杂度
int hIndex(int* citations, int citationsSize) {int* sum=(int*)malloc(citationsSize*sizeof(int));for(int i=0;i<citationsSize;i++){sum[i]=0;}for(int i=0;i<citationsSize;i++){for(int j=0;j<citationsSize;j++){if(citations[i]>=j+1) sum[j]++;}}for(int i=0;i<citationsSize;i++){if(sum[i]<i+1){return i;}}return citationsSize;
}
- 沿着这种思想,为什么不能直接把citations数组的下标+1当作h值,然后我们对citations数组进行排序,然后遍历数组;
- 先假设h值为1,那么被引用次数大于等于1的最少一次,如果是升序排列,则是最后一个数组元素大于等于1,为真则当前h值为1
- 然后假设h值为2,如果升序排列,则倒数第二个元素大于等于2,为真,则当前h值为2,为假,则返回之前的h值
- 运行如下
// 此处采用降序排序
int cmp(const void* a, const void* b) {return (*(int*)b) - (*(int*)a);
}int hIndex(int* citations, int citationsSize) {qsort(citations, citationsSize, sizeof(int), cmp);for(int i=0;i<citationsSize;i++){if(citations[i]<i+1){return i;}}return citationsSize;
}
- 时间复杂度O(nlogn),空间复杂度O(logn)
[ 官方题解 ]:
- 一、排序,和上述相同,但官方给出的空间复杂度是O(logn),经查阅,在 C 标准库中,qsort 的实现通常是 O(log n) 的栈空间(因为采用优化后的快速排序,递归深度平均为 log n)。
- 二、计数排序,新建并维护一个数组 counter 用来记录当前引用次数的论文有几篇,对于引用次数超过论文发表数的情况,我们可以将其按照总的论文发表数来计算即可。这样我们可以限制参与排序的数的大小为 [0,n],使得计数排序的时间复杂度降低到 O(n);从后向前遍历数组 counter,对于每个 0≤i≤n,在数组 counter 中得到大于或等于当前引用次数 i 的总论文数。
- 标红处就是这个算法的关键思路,简直天才,死脑子想半天没想到这个点
- 时间复杂度O(n),空间复杂度O(n)
int hIndex(int *citations, int citationsSize) {int n = citationsSize, tot = 0;int counter[n + 1];memset(counter, 0, sizeof(counter));for (int i = 0; i < n; i++) {if (citations[i] >= n) {counter[n]++;} else {counter[citations[i]]++;}}for (int i = n; i >= 0; i--) {tot += counter[i];if (tot >= i) {return i;}}return 0;
}
- 方法三:二分搜索,设查找范围的初始左边界 left 为 0,初始右边界 right 为 n。每次在查找范围内取中点 mid,同时扫描整个数组,判断是否至少有 mid 个数大于 mid。如果有,说明要寻找的 h 在搜索区间的右边,反之则在左边。
int hIndex(int* citations, int citationsSize){int left=0,right=citationsSize;int mid=0,cnt=0;while(left<right){// +1 防止死循环mid=(left+right+1)>>1;cnt=0;for(int i=0;i<citationsSize;i++){if(citations[i]>=mid){cnt++;}}if(cnt>=mid){// 要找的答案在 [mid,right] 区间内left=mid;}else{// 要找的答案在 [0,mid) 区间内right=mid-1;}}return left;
}