查找所有table中的最大值_查找表超全总结:解决查找问题

LeetCode上查找问题很多,比如第一题,两数之和,找到两个数字和为target

f76805b9639422b4b09982143deab149.png

暴力枚举的方法就需要O (N^2) 先排序再用双指针的方法也需要O(NlogN)然而查找表的方法只需要O(n) ,我和我的小伙伴们都惊呆了。

可以说,优化了一个阶层,效率十分之高。

那,什么是查找表?什么时候用?以及,查找表怎么用?

下面我一一分析:


查找表,就是用来查找的表咯。 对于要查找的数据,我全部放入查找表中,时间复杂度是O(n),再根据条件一一筛选,时间复杂度也是O(n)得出答案。

举个例子,两数之和。 找到一个数组中的两个数和为目标值(以给定)。首先将数组的所有数,都是可能的结果,放入查找表中。O(n) ,再遍历这个数组O(n),然后根据条件筛选。

条件是啥? target-nums[i] 是否在查找表中。(nums[i]就是每一个数)

非常简单吧,和暴力遍历的方法差不多。

那为什么查找表的方法能优化到O(n)呢?

这就和查找表的底层实现有关了。

因为,我这里使用的查找表用unordered_map实现,也就是哈希表。

我们知道,哈希表查找元素的时间复杂度是O(1) 非常的Amazing啊!!!

发现没有?优化的地方仅仅是查找中的O(n)优化成O(1),本来结果是O(N*N)

现在变成O(n*1)

这就是查找表的本质。

看出了他的本质,再利用他解题就轻而易举了。


回答第二个问题:什么时候用查找表?

这tm还用问吗?

查找的时候啊。

当你需要在数组,string vector等序列中,找到你要的答案,此时,查找表就可以派上用场。


最后一个问题:怎么用?

这个问题太关键了,听我慢慢道来。

先回忆一下,第一个问题中,我解释的查找表的原理。还记得吗?不记得倒回去看看。

我们仅仅优化了查找的部分。

其他部分和暴力遍历是一样的。

那么,代码写成暴力遍历的样子就行了,最后稍加修改就大功告成了。

什么?很抽象?

举个栗子。

这是两数之和的代码:

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> table;
        vector<int> ret;
        for(int i=0;i<nums.size();i++){
            table[nums[i]]=i;
        }
        for(int i=0;i<nums.size();i++){
            int tmp=target-nums[i];
            if(table.count(tmp) && table[tmp]!=i){
                ret.push_back(i);
                ret.push_back(table[tmp]);
                return ret;
            }
        }
        return ret;
    }
};

只看这一段:

 for(int i=0;i<nums.size();i++){
            int tmp=target-nums[i];
            if(table.count(tmp) && table[tmp]!=i){
                ret.push_back(i);
                ret.push_back(table[tmp]);
                return ret;
            }
        }

外面的循环遍历了这个数组,里面本来是要循环查找的,但是我们有了查找表就只需要把里层循环换成查找就行了。

在LeetCode上看见一位大神的比喻十分生动:

查找问题,就相当于配对,两个人相亲。 对于某个人i,找到和他相配的人。


首先,我们把所有人都登记一下,记录他喜欢什么样的妹子。(放入查找表里)

然后遍历整个相亲大会,对于每一个人,在你登记的表里(查找表)去里面找到你喜欢的那个。

很形象的解释了。

有没有更无脑的模板?

这个可以有,我给大家总结了了一份。

1.遍历序列,将每一个元素,放入查找表中,记录信息。

2.遍历序列,对于每一个元素,在查找表中找到你需要的元素。

说了辣么多,搞点题目做一下啦。

力扣​leetcode-cn.com

aeb6e7989e804007064ed523b57e596d.png

让我抽丝剥茧一下,把这个问题的本质抽象出来。

给的序列是一个点集。这个序列中有所有的点坐标,对于每一个点坐标i,我们要找到一些点坐标,其中每一个点坐标到i的距离都相同。

本质还是查找问题。

如果是暴力解法,我们需要枚举三个点,O(n^3),查找表,是O(n^2)

关键是:放入查找表的信息是啥? 判断是结果的条件是啥?

因为我们要查找的是距离相同的点,那么,对于遍历到的每一个点坐标i,都存在一些点,到他的距离相同。我们只需要遍历每一个点,然后产生一个查找表,到这个点的距离作为key

有多少个这样的点,也就是距离相等的点的个数作为value。

这张表构建完成之后,就有所有信息了。

然后遍历这张表,对于每个距离,都统计个数。

生成一张表O(n)

每个元素都要一张,O(n*n)=O(n^2)

因为(需要考虑元组的顺序)。

所以,如果对于到i这个点的距离相等的点只有一个,那么就无法构成答案,

并且考虑顺序的条件下,如果有n个,就需要从三个中选出两个组成答案,总共有

9c9846349919a1cf7abb47c81049686e.png

种。

计算结果是N*(N-1),然而这样的方法提交死活超时。

统计结果的时候用了这个公式:

f8bea548aa9cfc5d34964ce988dafff5.png
class Solution {
public:
    int dis(const vector<int> &a,const vector<int>& b){
        return (a[0]-b[0])*(a[0]-b[0])+
               (a[1]-b[1])*(a[1]-b[1]);
    }
    int numberOfBoomerangs(vector<vector<int>>& points) {
        int res=0;
        unordered_map<int,int> record;
        for(int i=0;i<points.size();i++){
            record.clear();
            for(int j=0;j<points.size();j++){
            int dis=pow(points[i][0]-points[j][0],2)+pow(points[i][1]-points[j][1],2);//取距离的平方,不开根号,避免了浮点数的误差问题
                if(j!=i){
                    record[dis]++;
                }
                if(record[dis]>1) res+=2*(record[dis]-1);
            }
            
        }
        return res;
    }
};

9d52df950e4dd26812be2e3364259dd8.png

可以看出,数据量是挺大的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值