昨天面试官问的算法题,难哭了!?

文章目录

    • 前言
    • 一、粉丝提问
    • 二、自信读题
    • 三、O(n^2) 算法1
    • 四、O(n^2) 算法2
    • 五、O(nlogn) 算法
    • 六、O(nlogn) C++版本
    • 七、O(nlogn) C++优化版本

前言

英雄算法联盟 - 七月集训 已经开始 20 天,八月算法集训 将于 08月01日 正式开始,目前已经提前开始报名,报名方式参见(八月算法集训报名),想要参加的同学,建议提早报名,因为对于算法零基础的同学,会有一些提前的准备工作,比如需要 1 - 5 天的时间完成预训练 和 九日集训 提前养成刷题的习惯,再参加算法集训会更加有成效。

一、粉丝提问

  英雄老师,昨天面试遇到的,力扣 2563,难哭了,55555!
昨天面试官问的算法题,难哭了!?_第1张图片

二、自信读题

  读题!给定一个整数数组 nums 和两个整数 lower 和 upper,返回 公平数对的数目,如果 (i, j) 数对满足以下情况,则认为它是一个 公平数对。
  0 <= i < j < n,且 lower <= nums[i] + nums[j] <= upper

三、O(n^2) 算法1

  计算数组长度,存储在 n 中,初始化结果变量 ans 为 0。遍历枚举第一个下标 i,遍历枚举第二个下标 j,如果 lower ≤ num[i]+num[j] ≤ upper,则表明是一组合法的 (i, j) 数对。结果计数器加一,返回这个计数器,就是我们要求的答案啦。运行,没什么问题:

class Solution:
    def countFairPairs(self, nums, lower, upper):
        n = len(nums)
        ans = 0
        for i in range(n):
            for j in range(n):
                if(lower <= nums[i] + nums[j] <= upper):
                    ans += 1
        return ans

  啊?错辣???嘶
  哦哦哦哦哦,i 和 j 是有序的,所以 j 需要从 i+1 开始枚举,运行!非常的自信,提交!

class Solution:
    def countFairPairs(self, nums, lower, upper):
        n = len(nums)
        ans = 0
        for i in range(n):
            for j in range(i+1, n):
                if( lower <= nums[i] + nums[j] <= upper):
                    ans += 1
        return ans

  超时啦!嘶……

四、O(n^2) 算法2

  哦哦哦哦哦!n 的范围10 的 5 次, O ( n 2 ) O(n^2) O(n2) 的时间复杂度必然超时,换个算法,先把列表按照递增排序,初始化 j 这个下标的两个边界 l 和 r,j 从 i+1 枚举到 n-1,一旦发现满足条件(lowwer ≤ nums[i] + nums[j]),则将 j 赋值给 l,跳出循环(说明后面所有的 j 都满足条件);然后 j 从 n-1 倒序枚举到 i+1,一旦发现满足条件(nums[i]+nums[j] ≤ upper),则将 j 赋值给 r,跳出循环(说明前面所有的 j 都满足条件)。
  然后如果 l <= r,就说明 l 到 r 之间的都是满足情况的数对,累加到 ans 中
运行,没什么问题,提交!

class Solution:
    def countFairPairs(self, nums, lower, upper):
        n = len(nums)
        nums.sort()
        ans = 0
        for i in range(n):
            l = -1
            r = -1
            for j in range(i+1, n):
                if lower <= nums[i] + nums[j]:
                    l = j
                    break
            for j in range(n-1, i, -1):
                if nums[i] + nums[j] <= upper:
                    r = j
                    break
            if l != -1 and l <= r:
                ans += r - l + 1
        return ans

  又超时啦!……嘶

五、O(nlogn) 算法

  哦哦哦哦哦!这样做并没有改善时间复杂度,整体而言,还是 O(n^2) 的时间复杂度,删掉重新来过,不舍得啊,删掉吧,旧的不去新的不来!
  尝试一下,我们学过的双指针算法,可以先把 nums 递增排序,假设 count(nums, value) 代表一个计算满足 ≤ value的数对的函数,那么计算出 ≤ upper 的数对个数,缓存到 b 中,再计算出 ≤ lower - 1 的数对个数,缓存到 a 中,最后只要返回 b - a 就是我们要求的结果了。
  少了 self 加上。
  然后来实现一下这个函数,计算 nums 长度缓存到 n 中,初始化 l 为 0,r 为 n-1,结果 ans 为 0,如果 l < r,则判断 nums[l] + nums[r],是否小于等于 value,如果满足条件,由于 nums 是递增的,说明 nums[l] 固定,所有满足 num[l+1] 到 nums[r],都必然 ≤ value,总共 r - l 组,累加到 ans 中;否则减小 r 的值,当 l 等于 r 时跳出循环,返回结果 ans 就可以了,运行,提交!

class Solution:
    def countLessThan(self, nums, value):
        n = len(nums)
        l = 0
        r = n - 1
        ans = 0
        while l < r:
            if nums[l] + nums[r] <= value:
                ans += r - l
                l += 1
            else:
                r -= 1
        return ans

    def countFairPairs(self, nums, lower, upper):
        b = self.countLessThan(nums, upper)
        a = self.countLessThan(nums, lower-1)
        return b - a

  又错辣!
  哦哦哦哦哦
  忘记排序了!
  恶业!?!
运行,没什么问题,提交!

class Solution:
    def countLessThan(self, nums, value):
        n = len(nums)
        l = 0
        r = n - 1
        ans = 0
        while l < r:
            if nums[l] + nums[r] <= value:
                ans += r - l
                l += 1
            else:
                r -= 1
        return ans

    def countFairPairs(self, nums, lower, upper):
        nums.sort()
        b = self.countLessThan(nums, upper)
        a = self.countLessThan(nums, lower-1)
        return b - a

过啦!啦!
188 毫秒
还有什么可以优化的空间呢?
O(nlogn) 的算法
已经是最优的了呀
可恶啊
前面竟然还有这么多人
怎么可能?!
想不出来了呀
真的要止步于此了吗?
真的要放弃了吗?
怎么打败前面这些人呢?
等等
O(nlogn) 过辣
根据上一题的经验
C++的效率明显高于 python
用 C++ 来敲一遍
一定就可以了

六、O(nlogn) C++版本

  先把排序写了以免忘记,计算小于等于upper的数对,计算小于等于lower-1的数对,返回两者的差,实现一下 count 函数,基本和 python 思路一致,就是双指针调整指针位置,没什么好说的,行云流水的敲下来就好了,整体上也没什么技巧,全是感情唯手熟尔。
  运行,没什么问题,提交!

class Solution {
public:
    long long countLessThan(vector<int>& nums, int value) {
        int n = nums.size();
        int l = 0, r = n - 1;
        long long ans = 0;
        while (l < r) {
            if(nums[l] + nums[r] <= value) {
                ans += r - l;
                l++;
            }else {
                r--;
            }
        }
        return ans;
    }
    long long countFairPairs(vector<int>& nums, int lower, int upper) {
        sort(nums.begin(), nums.end());
        long long b = countLessThan(nums, upper);
        long long a = countLessThan(nums, lower-1);
        return b - a;
    }
};

  过啦!124毫秒!

七、O(nlogn) C++优化版本

竟然只打败了 93.68% 的人
怎么回事?
这代码已经非常简洁了呀

还有什么更高效的方法呢
难道要放弃了吗?
要在这里放弃了吗?
不应该啊
我肯定还遗漏了什么
是什么呢?
是什么呢?
等等
既然是C++
输入输出可以用
sync_with_stdio 和 tie
来进行优化
像这样就好啦
运行
哎???
哦哦哦哦哦
命名空间调用应该是两个冒号
加上就好了
运行
过啦!
打败了全世界的人!!!

// 最终版本
class Solution {
public:
    int fast_io = [](){
        ios::sync_with_stdio(0);
        cin.tie(0); cout.tie(0);
        return 0;
    }();
    long long countLessThan(vector<int>& nums, int value) {
        int n = nums.size();
        int l = 0, r = n - 1;
        long long ans = 0;
        while (l < r) {
            if(nums[l] + nums[r] <= value) {
                ans += r - l;
                l++;
            }else {
                r--;
            }
        }
        return ans;
    }
    long long countFairPairs(vector<int>& nums, int lower, int upper) {
        sort(nums.begin(), nums.end());
        long long b = countLessThan(nums, upper);
        long long a = countLessThan(nums, lower-1);
        return b - a;
    }
};

你可能感兴趣的:(算法)