LeetCode刷题day24

202. 快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」定义为:

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
如果 可以变为 1,那么这个数就是快乐数。
如果 n 是快乐数就返回 true ;不是,则返回 false 。

示例 1:
输入:n = 19
输出:true

解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2:
输入:n = 2
输出:false
思路分析:

注意题目中的可能会无限循环,也就是说在求和的过程中,sum会重复出现.

  • 所以我们就可以对该数进行快乐的判断,每次如果不是1,说明该数目前不是快乐数.
  • 同时要在set中寻找该数是否曾经出现过,如果出现过则说明出现了循环则一定不是快乐数.
  • 如果还没有出现则持续进行判断…
参考代码
int getSum(int n){
	int sum = 0;
	while(n){
		sum+= (n%10)*(n%10);
		n /= 10;
	}
	return sum;
}

//由于只需要存这个元素在集合中是否存在,而且不需要重复,所以我们使用unordered_set 
bool isHappy(int n) {
	unordered_set<int> S;
	int temp;
	while(1){
		temp = getSum(n);
		if(temp==1){
			return true;
		}
		if(S.find(temp)!=S.end()){
			return false;
		}else{
			S.insert(temp);
		}
		n = temp;
	}
	
}

1. 两数之和

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

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]

解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
方法一:暴力法
参考代码
vector<int> twoSum(vector<int>& nums, int target) {
	for(int i = 0; i < nums.size()-1; i++) {
		for(int j = i+1; j < nums.size(); j++) {
			if(nums[i]+nums[j]==target) {

				return {i,j};
			}
		}
	}
	return {};
}
  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
  • 空间复杂度: O ( 1 ) O(1) O(1)
方法二:hash表
  • 因为每次不仅要存数,还要存储其下标,所以我们使用map,因为不需要有序和重复性,我们可以选择:unordered_map M

  • 用i遍历所有的元素,在M中查找是否存在元素 target - nums[i],如果存在则说明找到了,返回当前元素和这个元素的下标.

  • 如果没有找到则将当前元素和下标插入到M中.

参考代码2
vector<int> twoSum(vector<int>& nums, int target) {
	unordered_map<int,int> M;
	for(int i = 0;i < nums.size();i++){
		if(M.find(target-nums[i])!=M.end()){
			return {M[target-nums[i]],i};
		}else{
			M[nums[i]] = i;
		}
	}
	return {};
}
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
方法三:双指针

排序+双指针+Set/手动去重

参考代码
// 使用set去重 
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
	sort(nums1.begin(),nums1.end());
	sort(nums2.begin(),nums2.end());
	unordered_set<int> numSet; 
	int i = 0,j = 0;
	while(i < nums1.size() && j < nums2.size()) {
		if(nums1[i] == nums2[j]) {
			numSet.insert(nums1[i]);
			i++;
			j++;
		} else if(nums1[i] < nums2[j]) {
			i++;
		} else {
			j++;
		}
	}
	vector<int> res(numSet.begin(),numSet.end());
	return res;
}


// 手动判断去重 
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
	sort(nums1.begin(),nums1.end());
	sort(nums2.begin(),nums2.end());
	int i = 0,j = 0;
	vector<int> res;
	while(i < nums1.size() && j < nums2.size()) {
		if(i > 0 && nums1[i]==nums1[i-1]){
			i++;
			continue;
		}
		if(j > 0 && nums2[j] == nums2[j-1]){
			j++;
			continue;
		}
		if(nums1[i] == nums2[j]) {
			res.push_back(nums1[i]);
			i++;
			j++;
		} else if(nums1[i] < nums2[j]) {
			i++;
		} else {
			j++;
		}
	}
	return res;
}

15. 三数之和

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

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

示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
思路分析

排序 + 双指针

算法流程:

(1). 特判,对于数组长度 n,如果数组长度小于 3,返回 []。
(2). 对数组进行排序。
(3).遍历排序后数组:

  • 若 nums[i]>0:因为已经排序好,所以后面不可能有三个数加和等于 0,直接返回结果。

  • 对nums[i] (代表的就是a)进行去重.

  • 令左指针 L=i+1,右指针 R=n-1,当 L<R 时,执行循环:

(4) 当 nums[i]+nums[L]+nums[R]==0执行循环,判断左界和右界是否和下一位置重复,去除重复解。并同时将 L,R 移到下一位置,寻找新的解
(5).若和大于 0,说明nums[R] 太大,R左移
(6).若和小于 0,说明 nums[L] 太小,L右移

复杂度分析
  • 时间复杂度:O(n^2)
    数组排序 O(n logn),遍历数组O(n),双指针遍历 O(n),总体 O(n logn)+O(n)∗O(n),
  • 空间复杂度:O(1)
参考代码
vector<vector<int>> threeSum(vector<int>& nums) {
	if(nums.size()<3) {
		return {};
	}
	sort(nums.begin(),nums.end());
	if(nums[0]>0) {
		return {};
	}
	vector<vector<int>> res;
	int len = nums.size();
	for(int i = 0; i < len; i++) {
		if(nums[i]>0) { //如果当前的最小的值都>0,则后续的肯定也>0
			return res;
		}
		if(i>0&&nums[i]==nums[i-1]) {// a元素进行去重...
			continue;
		}
		int L = i+1;
		int R = len - 1;
		while(L<R) {
			int X = nums[i]+nums[L]+nums[R];
			if(!X) {
				res.push_back({nums[i],nums[L],nums[R]});
				//进行去重
				while(L+1 <R&&nums[L]==nums[L+1]) {
					L++;
				}
				while(R-1>L&&nums[R]==nums[R-1]) {
					R--;
				}
				//这里一定要进行L++,R--哦,因为目前我们所处的位置L和while前的L对应的数组中的值是一样的。R同理
				L++;
				R--;
			} else if(X>0) {
				R--;

			} else if(X<0) {
				L++;
			}

		}
	}
	return res;
}

18. 四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。

示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]
思路分析

和三数之和解决思路基本一致,三数之和只是 i 固定,左右指针进行变化.
四数之和是i,j固定,然后左右指针进行变化,当左右指针变化一轮后,j进行后移一位.j移动完一轮后,i后移一位…

实现代码
vector<vector<int>> fourSum(vector<int>& nums, int target) {
	if(nums.size()<4) {
		return {};
	}
	int len = nums.size();
	sort(nums.begin(),nums.end());
	long temp,a,b,c,d;
	vector<vector<int>> res;

	for(a = 0; a<= len-4; a++) {
		//对a去重
		if(a>0&&nums[a]==nums[a-1]) { //进行下一个.
			continue;
		}
		for(b = a+1; b<=len-3; b++) {
			c = b+1;
			d = len - 1;
			if(b>a+1 && nums[b]==nums[b-1]) { //b去重
				continue;
			}
			while(c<d) {
				temp = 0;
				temp += nums[a];
				temp += nums[b];
				temp+=nums[c];
				temp+=nums[d];
				if(temp==target) {
					res.push_back({nums[a],nums[b],nums[c],nums[d]});
					while(c+1<d && nums[c]==nums[c+1]) {
						c++;
					}
					while(d-1>c&&nums[d]==nums[d-1]) {
						d--;
					}
					c++;
					d--;
				}else if(temp > target){
					d--;
				} else{
					c++;
				}

			}

		}
	}
	return res;
}

454. 四数相加 II

给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

示例 1:
输入:nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
输出:2

解释:
两个元组如下:

  1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
  2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
示例 2:
输入:nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
输出:1
思路分析
  • 首先定义 一个unordered_map,key放a和b两数之和,value 放a和b两数之和出现的次数。
  • 遍历大A和大B数组,统计两个数组元素之和,和出现的次数,放到map中。
  • 定义int变量cnt,用来统计a+b+c+d = 0 出现的次数。
  • 在遍历大C和大D数组,找到如果 0-(c+d) 在map中出现过的话,就用count把map中key对应的value也就是出现次数统计出来。
  • 最后返回统计值 cnt就可以了
参考代码
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
	unordered_map<int,int> M;
	int cnt = 0;
	for(int A : nums1){
		for(int B: nums2){
			if(M.find(A+B)==M.end()){
				M[A+B] = 1;
			}else{
				M[A+B]++;	
			}
		}
	}
	
	for(int C:nums3){
		for(int D: nums4){
			if(M.find(0-C-D)!=M.end()){
				cnt+=M[0-C-D];
			}
		}
	}
	return cnt;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱编程的大李子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值