LeetCode系列文章
文章目录
- 一、题目描述
- 二、示例
- 三、主体思路
- 1、暴力解法
- 2、排序+双指针
- 3、计数排序+前缀和
- 四、代码实现
- 1、暴力解法
- 2、排序+双指针
- 3、计数排序+前缀和
一、题目描述
在社交媒体网站上有 nnn 个用户。给你一个整数数组 a g e sagesages,其中 a g e s [ i ] ( 1 ≤ a g e s [ i ] ≤ 120 )ages[i](1\leq ages[i] \leq 120)ages[i](1≤ages[i]≤120) 是第 iii 个用户的年龄。
如果下述任意一个条件为真,那么用户 xxx 将不会向用户 yyy 发送好友请求:
- ages[y]≤0.5×ages[x]+7ages[y]\leq0.5\times ages[x]+7 ages[y]≤0.5×ages[x]+7
- ages[y]>ages[x]ages[y]>ages[x] ages[y]>ages[x]
- ages[y]>100&&ages[x]100\&\&ages[x]<100 ages[y]>100&&ages[x]<100
否则, xxx 将会向 yyy 发送一条好友请求。
注意,如果 xxx 向 yyy 发送一条好友请求, yyy 不必也向 xxx 发送一条好友请求。另外,用户不会向自己发送好友请求。
返回在该社交媒体网站上产生的好友请求总数。
二、示例
输入: ages = [16, 16]
输出: 2
解释: 2人互发好友请求。
三、主体思路
1、暴力解法
采用暴力解法解决该题目是非常简单的,要判断一个用户能够给哪些用户发送好友请求,只需要遍历这 n−1n-1 n−1(不包括自己)个用户,依次判断是否满足发送好友请求的条件即可。
很明显这种暴力解法的时间复杂度是 O( N 2)O(N^2) O(N2),当用户数据量过大时计算消耗的时间就会暴增,因此这种解法是不推荐的。
2、排序+双指针
仔细观察题目所给的三个条件,你会发现条件三其实是蕴含在条件二当中的,如果满足了条件三那就一定满足条件二,因此只要不满足前两个条件,那么用户 xx x 就能向用户 yy y 发送好友请求,也就是用户 yy y 需要满足:0.5×ages[x]+7<ages[y]≤ages[x]0.5\times ages[x]+70.5×ages[x]+7<ages[y]≤ages[x]
将这个关系用数轴表示如下:
如果存在满足要求的用户 yy y,那么 0.5×x+7<x0.5\times x+7<x 0.5×x+7<x,即 x>14x > 14 x>14。也就是说,如果一个用户的年龄小于等于14,那么该用户将不能向任何用户发送好友请求。
现在看来,要知道用户 xx x 能够向哪些用户 yy y 发送好友请求,实际就需要求出该数轴上对应的左右边界,年龄位于该区域内的用户就是用户 xx x 能够向其发送好友请求的用户。
在寻找左右边界时可以用 leftleft left 和 rightright right 表示左右边界的下标(双指针),如果左右都采用闭区间,即 leftleft left 指向的是第一个满足要求的用户,rightright right 指向的是最后一个满足要求的用户,那么最终 right−leftright-left right−left 的值就是满足要求的用户 yy y 的个数。(注意:这里不是 right−left+1right-left+1 right−left+1,因为用户 xx x 本身也是在该区域当中的,但用户 xx x 不会向自己发送好友请求)
因此解题步骤如下:
- 对所给 nnn 个用户的年龄进行排序。
- 依次遍历这 nnn 个用户的年龄。
- 在遍历用户时,如果该用户的年龄小于等于14,则该用户能够向外发送好友请求的个数为0;如果该用户的年龄大于14,则通过双指针找到满足发送要求的区域,进而得出该用户能够向外发送的好友请求的个数。
- 最后将每个用户能够向外发送的好友请求的个数进行累加即可。
特别注意,在通过双指针确定满足要求的年龄区域时,只有第一次需要在 nn n 个用户的基础上从头进行查找。而后续在确定区域时,区域的左右边界只需要在上一个用户的左右边界的基础上向后进行查找即可。因为 0.5×x+70.5\times x+7 0.5×x+7 与 xx x 是线性的关系,当 xx x 增大时,0.5×x+70.5\times x+7 0.5×x+7 的值也会增大,因此我们无需再判断上一个用户的 leftleft left 指针之前的用户年龄是否在本次限定的区域当中。
3、计数排序+前缀和
仔细观察你会发现题目给定用户的年龄是在 [1,120][1, 120] [1,120] 范围内的,因此我们可以采用计数排序统计各个年龄段用户的个数并进行排序。
比如我们将排序结果存储到 cntcnt cnt 数组当中,此时 cnt[age]cnt[age] cnt[age] 就表示年龄为 ageage age 的用户个数,而每一个年龄为 ageage age 的用户能够向外发送的好友请求的数量就是:( ∑ i=0.5×age+8age cnt[i])−1(\sum_{i=0.5\times age+8}^{age}cnt[i])-1 (∑i=0.5×age+8agecnt[i])−1,其中减一是因为用户不会向自己发送好友请求。
为了避免每次都要遍历 cntcnt cnt 数组进行求和,我们可以计算出 cntcnt cnt 数组的前缀和数组 prepre pre,pre[age]pre[age] pre[age] 代表的就是年龄段位于1 ~ age的用户个数:pre[age]= ∑ i=1age cnt[i]pre[age]=\sum_{i=1}^{age}cnt[i] pre[age]=∑i=1agecnt[i]。
此时每一个年龄为 ageage age 的用户能够向外发送的好友请求的数量就可以写为:pre[age]−pre[0.5×age+7]−1pre[age]-pre[0.5\times age+7]-1 pre[age]−pre[0.5×age+7]−1
此时我们就能够在 O(1)O(1) O(1) 的时间内计算出一个年龄为 ageage age 的用户能够向外发送的好友请求的数量,将该值乘以 cnt[age]cnt[age] cnt[age] 就是该年龄段的所有用户总计能够向外发送的好友请求的数量。
因此解题步骤如下:
- 通过计数排序统计每个年龄段的用户个数。
- 计数出 c n tcntcnt 数组的前缀和数组 p r eprepre。
- 通过 ( p r e [ a g e ] − p r e [ 0.5 × a g e + 7 ] − 1 ) × c n t [ a g e ](pre[age]-pre[0.5\times age+7]-1)\times cnt[age](pre[age]−pre[0.5×age+7]−1)×cnt[age] 公式计算出每个年龄段的用户总计能够向外发送的好友请求的数量,将其进行累加就是最终结果。