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
解释:
两个元组如下:
- (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
- (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;
}