datawhale组队训练——LeetCode分类练习——查找2

题目链接:
1. 两数之和
15. 三数之和
16. 最接近的三数之和
18. 四数之和
49. 字母异位词分组
149. 直线上最多的点数
219. 存在重复元素 II
220. 存在重复元素 III
447. 回旋镖的数量
454. 四数相加 II

1.两数之和

题目描述
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
思路分析

方法一:
每次从nums中挑选出两个数字,看是否相加之后等于target,如果等于的话就直接返回两个数的相应位置。

方法二:
利用hashmap,先将nums中的数存入hash数组中,一边循环的时候一边查找target-nums[i]是否在数组中,如果存在的话就返回相应的位置。

复杂度分析

方法一的时间复杂度是O(n^2),空间复杂度是O(n)
—————————————————————————
方法二的时间复杂度是O(n*k),k是查询所用的平均时间,根据hashmap的存储原理,应该是log2.网上大部分都说是O(n),个人觉得忽略了查询的时间,当n很大的时候,查询也会耗用一定时间。空间复杂度也是O(n)

程序代码
c++版

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map < int , int > hash;
        vector < int > result;
        int numsize = int(nums.size());
        for(int i=0;i<numsize;i++) {
            int number=target-nums[i];
            if(hash.find(number)!=hash.end()) {
            //如果存在的话,就把相应位置放入result中,然后返回答案
                result.push_back(hash[number]);
                result.push_back(i);
                return result;
            }
            hash[nums[i]]=i;
            //不相等就继续放入hash中,边放入边查询
            //这句不能放在if语句之前,解决hash中有重复值或target-num=num的情况
        }
        return result;
    }
};

Python版

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashmap={}
        for i in range(len(nums)):
            if hashmap.get(target-nums[i]) is not None:
                return [hashmap.get(target-nums[i]),i]
            hashmap[nums[i]]=i

ps:更多Python版本请查看文章末尾的拓展资料。

15. 三数之和

题目描述
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[ [-1, 0, 1], [-1, -1, 2] ]

思路分析

方法一:c++版
先从数组中挑选出一个数,然后在从数组中挑选出两个数,如果三数之和满足题目要求就存入答案中。
将数组排序,使数组为有序状态。挑选一个数字之后,剩下两个数,分别用指针 L 和 R,从左右两端往中间缩进,遇到符合要求的就计入答案,并且还可以排除重复情况。
详情见代码中注释

复杂度分析

方法一:c++版
时间复杂度O(n^2),空间复杂度O(n)

程序代码
c++版

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int> >ans;
        //用于存放答案
        int n=nums.size();
        if (n <= 2) return ans;
	    sort(nums.begin(),nums.end());
	    //先排序,是数组有序
	    for(int i=0;i<n-2;++i) {
	    //先选一个数字
            if(nums[i] > 0) break;
		    int l=i+1;
			int r=n-1;
			//双指针
		    while(l<r) {
		    	int sum=nums[i]+nums[l]+nums[r];
		    	//三数之和
                int a=nums[i],b=nums[l],c=nums[r];		    
			    if(sum==0) {
			    	vector<int> temp{b,a,c};
			    	ans.push_back(temp);
			    	//符合题目要求就放入答案数组中
			    	while( l<r && nums[l]==nums[l+1] )l++;
			    	//如果前一个和后一个相等,就肯定重复率,直接跳过
				    while( l<r && nums[r]==nums[r-1] )r--;
				    //两个while用于去重
				    l++;
				    r--;
			    }
			    else if(sum<0)l++;
			    else if(sum>0)r--;
		    }
		    while (i + 1 < n-2 && nums[i] == nums[i + 1])i++;
	    }
        return ans;
    }
};

16. 最接近的三数之和

题目描述
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

示例:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。

提示:
3 <= nums.length <= 10^3
-10^3 <= nums[i] <= 10^3
-10^4 <= target <= 10^4
思路分析

复杂度分析

程序代码
c++版

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        int ans = nums[0] + nums[1] + nums[2];
        for(int i = 0;i < nums.size();i++)
        {
            int l = i + 1;
            int r = nums.size()-1;
            while(l < r)
            {
                int temp = nums[i] + nums[l] + nums[r];
                if(abs(target - temp) < abs(target - ans)) 
                ans = temp;
                if(temp == target)
                return ans;
                else if (temp < target)
                l++;
                else
                r--;
            }
        }
        return ans;
    }
};

18. 四数之和

题目描述
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

注意:
答案中不可以包含重复的四元组。

示例:
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
思路分析

这个题目就是三数之和拓展而来的,在三数之和的外面加一重循环就好了。并且是题目中给定的target,而不是三数之和中的固定的0。
将三数之和的代码改一下就好了。

复杂度分析
时间复杂度O(n^3)空间复杂度O(n)
程序代码
c++版
//下面这份代码只过了264的测试样例,总共282个。
输入:
[0,4,-5,2,-2,4,2,-1,4]
12
此程序输出
[[0,4,4,4]]
期待输出
[[0,4,4,4],[2,2,4,4]]

经过自己不断地调试,最终发现了问题,改正后已经通过全部测试。见注释。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int> >ans;
        int n=nums.size();
        if (n <= 3) return ans;
	    sort(nums.begin(),nums.end());
        for(int aa=0;aa<n-3;++aa) {
            //if(nums[aa]>0) break;
            //上面这一句if要删掉,因为三数之和是要等于0,
            //四数之和是由一个target,这里不一样,所以要删掉
            if(aa>0&&nums[aa]==nums[aa-1])continue;
	        for(int i=aa+1;i<n-2;++i) {
                //if(nums[i] > 0) break;
                if(i>aa+1&&nums[i]==nums[i-1])continue;
		        int l=i+1;
			    int r=n-1;
		        while(l<r) {
		    	    int sum=nums[aa]+nums[i]+nums[l]+nums[r];
                    int a=nums[i],b=nums[l],c=nums[r];		    
			        if(sum==target) {
			    	    vector<int> temp{nums[aa],a,b,c};
			    	    ans.push_back(temp);
			    	    while( l<r && nums[l]==nums[l+1] )l++;
				        while( l<r && nums[r]==nums[r-1] )r--;
				        l++;
				        r--;
			        }
			        else if(sum<target)l++;
			        else if(sum>target)r--;
		        }
		        //while (i + 1 < n-2 && nums[i] == nums[i + 1])i++;
	        }
        }
        return ans;
    }
};

c++正确程序
来自:https://leetcode-cn.com/problems/4sum/solution/shuang-zhi-zhen-jie-fa-can-zhao-san-shu-zhi-he-ge-/
此篇博客中的一个评论

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> result;
        int size=nums.size();
        for(int a=0;a<size-3;++a)
        {
            if(a>0&&nums[a]==nums[a-1])continue;
            for(int b=a+1;b<size-2;++b)//以下代码与三数之和一样的
            {
                if(b>a+1&&nums[b]==nums[b-1])continue;
                int i=b+1,j=size-1;
                while(i<j)
                {
                    int sum=nums[a]+nums[b]+nums[i]+nums[j];
                    if(sum<target)
                        while(i<j&&nums[i]==nums[++i]);
                    else if(sum>target)
                        while(i<j&&nums[j]==nums[--j]);
                    else{
                        result.push_back(vector<int>{nums[a],nums[b],nums[i],nums[j]});
                        while(i<j&&nums[i]==nums[++i]);
                        while(i<j&&nums[j]==nums[--j]);
                    }
                }
            }
        }
        return result;
    }
};

Python版

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        if not nums or len(nums) < 4:
            return []
        nums.sort()
        res = []
        for a in range(len(nums)-3):
            if a > 0 and nums[a] == nums[a-1]:
                continue
            for b in range(a+1,len(nums)-2):
                if b > a+1 and nums[b] == nums[b-1]:
                    continue
                c = b+1
                d = len(nums)-1
                while c < d:
                    sum = nums[a]+nums[b]+nums[c]+nums[d]
                    if sum == target:
                        res.append([nums[a],nums[b],nums[c],nums[d]])
                        while c<d and nums[c] == nums[c+1]:
                            c += 1
                        while c<d and nums[d] == nums[d-1]:
                            d -= 1
                        c += 1
                        d -= 1
                    elif sum < target:
                        c += 1
                    else:
                        d -= 1
        return res

49. 字母异位词分组

题目描述
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

示例:
输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]
输出:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
思路分析
复杂度分析
程序代码
Python版

class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
        Hash={}
        for i in strs:
            tmp=''.join(sorted(list(i)))
           #排序之后放到tmp中
            if Hash.get(tmp) is not None:
                Hash[tmp].append(i)
                #如果在hash中,就直接在当前元组中加入新的
            else:
                Hash[tmp]=[i]
                #如果排完序之后的存在hash里面,就添加一个新的元组进去
        return list(Hash.values())

149. 直线上最多的点数

题目描述
给定一个二维平面,平面上有 n 个点,求最多有多少个点在同一条直线上。

示例 1:

输入: [[1,1],[2,2],[3,3]]
输出: 3
解释:
^
|
| o
| o
| o
±------------>
0 1 2 3 4
示例 2:

输入: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]]
输出: 4
解释:
^
|
| o
| o o
| o
| o o
±------------------>
0 1 2 3 4 5 6
思路分析
复杂度分析
程序代码

219. 存在重复元素 II

题目描述
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的 绝对值 至多为 k。

示例 1:
输入: nums = [1,2,3,1], k = 3
输出: true

示例 2:
输入: nums = [1,0,1,1], k = 1
输出: true

示例 3:
输入: nums = [1,2,3,1,2,3], k = 2
输出: false
思路分析

方法一:对于每一个元素,扫描距离他小于等于k的元素,如果有相等的就返回true。如果没有一个符合题目要求的就返回false
方法二:我们将每个元素依次丢进map中,并且每丢进一个元素,就查找当前hashmap中是否有与这个元素相等的元素,如果有并且距离还小于等于k,就返回true,一直这样进行下去,

复杂度分析

方法一的时间复杂度:O(nK),空间复杂度:O(n)
方法二的时间复杂度:O(n
常数),常数比较小,空间复杂度为O(n)

程序代码
c++版

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_map<int,int> map ;
        for(int i=0;i<nums.size();i++) {
            if(map.find(nums[i])!=map.end()) {
                int temp=i-map[nums[i]];
                if(temp<=k) return true;
            }
            map[nums[i]]=i;
        }
        return false;
    }
};

Python版

class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
        dct = {}
        for i in range(len(nums)):
            if nums[i] in dct and dct[nums[i]] >= i-k:
                return True
            dct[nums[i]] = i
        return False

220. 存在重复元素 III

题目描述
在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ 。

如果存在则返回 true,不存在返回 false。

示例 1:
输入: nums = [1,2,3,1], k = 3, t = 0
输出: true

示例 2:
输入: nums = [1,0,1,1], k = 1, t = 2
输出: true

示例 3:
输入: nums = [1,5,9,1,5,9], k = 2, t = 3
输出: false
思路分析
ps:重点可以看看方法二,我自己想的方法,还没有去代码实现,理论可行。

方法一:桶的思想
此题主要是利用了桶的思想,按t的大小分为一个桶,将nums[i]除以t,将商相同的nums[i]放到一个桶中。
同一个桶中相差小于t,可以直接返回,相邻两个桶中相差2*t+1,这时再加一个判断就好了,如果两个相差的绝对值小于等于t,就可以返回true。
直到最后还没有符合要求的,就返回false。

方法二:排序+滑动窗口
建立一个结构体struct,每个单元存放两个值,一个是元素本身的数值大小,另一个是该元素在nums中的初始位置。然后对于这个结构体排序,第一关键字是数值大小,第二关键字是位置。
排完序之后,窗口的大小为t,因为数值和位置都是有序的,后一个肯定比前一个大,保证了有序性。这样在t大小的窗口中,只要有相邻的两个元素的位置之差的绝对值小于k,我们就可以返回true。
如果当前这个窗口没有一个符合要求,我们就开始滑动窗口。但当每次窗口移动的时候,只要看新进来的那个元素和前面那个元素位置之差是否满足要求,不满足的话继续移动。

复杂度分析

方法一 时间复杂度:O(n*k)k是一个常数,空间复杂度O(n)

程序代码
c++版(方法一)

class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        if(t<0) return false;
        //如果t小于0则直接返回false
        long long mod=t + 1LL;
        unordered_map<long long , long long>buck;
        for(int i=0;i<nums.size();++i) {
            long long nth=nums[i]/mod;
            if(nums[i]<0) nth--;
            //如果为负数,就减一,为的是防止相冲突,
            //比如-4/5和4/5的结果都是0,所以负数都减一
            if(buck.find(nth)!=buck.end()) return true;
            //如果就在自己的桶中找到了就直接返回true
            else if(buck.find(nth-1)!=buck.end()&&abs(nums[i]-buck[nth-1])<=t) return true;
            //如果在相邻的桶中找到了,并且相差的绝对值小于等于t,也是返回true
            else if(buck.find(nth+1)!=buck.end()&&abs(nums[i]-buck[nth+1])<=t) return true;
            buck[nth]=nums[i];
            //这句话一定要放到最后,防止把自己也算进去。
            if(i>=k) {
                buck.erase(nums[i-k]/mod);
                //因为有距离k的限制,所以不断更新,将前面的去掉
            }
        }
        return false;
    }
};

447. 回旋镖的数量

题目描述
给定平面上 n 对不同的点,“回旋镖” 是由点表示的元组 (i, j, k) ,其中 i 和 j 之间的距离和 i 和 k 之间的距离相等(需要考虑元组的顺序)。

找到所有回旋镖的数量。你可以假设 n 最大为 500,所有点的坐标在闭区间 [-10000, 10000] 中。

示例:
输入:
[[0,0],[1,0],[2,0]]
输出:
2
解释:
两个回旋镖为 [[1,0],[0,0],[2,0]] 和 [[1,0],[2,0],[0,0]]
思路分析

题目意思应该是很容易理解的,就是选一个点,找到与它距离相等的两个点,形成一个组合,并且因为顺序可以调换,所以答案乘以2.

先看下表:

点的个数新增个数总的个数
100
21*2=22
32*2=46
43*2=612
54*2=820

对于点i,如果有一个点跟它距离相等,不能形成一个组合,所以答案是0。
如果有两个点与点i距离相等,就可以形成一个组合,调换顺序后又是一个,所以是两个。
如果有三个点与点i距离相等,就可以形成新的两个组合,调换顺序之后就是四个。然后加上之前的一共就是6个,慢慢的就会发现这就是排列数的计算。

看了上面的解释之后就只要代码实现了,用hashmap来存储距离相等的点的个数,每新增一个就更新一下count,最后返回总的count答案。

复杂度分析
时间复杂度O(n^2),空间复杂度O(n)
程序代码
c++版

class Solution {
public:
    int numberOfBoomerangs(vector<vector<int>>& points) {
        unordered_map<double,int> hashmap;
        int count = 0;
        for(int i=0;i<points.size();++i) {
            hashmap.clear();
            //每次换点i的时候就清理一下hashmap
            for(int j=0;j<points.size();++j) {
                double dis=sqrt(pow(points[i][0] - points[j][0], 2) + pow(points[i][1] - points[j][1], 2));
            hashmap[dis]++;
            //距离相等的点加一
            count += (hashmap[dis] - 1) * 2;
            //计算结果,累加求和
            }
        }
        return count;
    }
};

python版

class Solution:
    def numberOfBoomerangs(self, points: List[List[int]]) -> int:
        count = 0 
        for i in points:
            hashmap={}
            for j in points:
                if j!=i:
                    dicts=(i[0]-j[0])**2+(i[1]-j[1])**2
                    hashmap[dicts] = hashmap[dicts] + 1 if dicts in hashmap else 1
                    count+=(hashmap[dicts]-1)*2
        return count 

ps:文章末尾附拓展资料

454. 四数相加 II

题目描述
给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -228 到 228 - 1 之间,最终结果不会超过 231 - 1 。

例如:
输入:
A = [ 1, 2]
B = [-2,-1]
C = [-1, 2]
D = [ 0, 2]
输出:
2
解释:
两个元组如下:

  1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
  2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
    思路分析

先遍历A和B数组,形成各种可能的组合,并且存在hash表中。
然后再遍历C和D数组,形成各种可能的组合的时候看是否A和B形成的组合中恰好可以与C与D的和为0,有的话就count++。
最后返回count。

复杂度分析
时间复杂度 O(n^2),空间复杂度O(n)
程序代码
c++版

class Solution {
public:
    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
        unordered_map<int, int> umap; //key:a+b的数值,value:a+b数值出现的次数
        // 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中 
        for (int a : A) {
            for (int b : B) {
                umap[a + b]++;
            }
        }
        int count = 0; // 统计a+b+c+d = 0 出现的次数
        // 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就把map中key对应的value也就是出现次数统计出来。
        for (int c : C) {
            for (int d : D) {
                if (umap.find(0 - (c + d)) != umap.end()) {
                    count += umap[0 - (c + d)];
                }
            }
        }
        return count;
    }
};

拓展资料

第一题 两数之和 Python多种解法

第八题 存在重复元素|| 详细讲解

第九题 回旋镖的数量 题目中排列组合的深入解析

Python基础学习-Python中最常见括号()、[]、{}的区别

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值