本章节来自
参考:代码随想录
总结
两数之和 就不能使用双指针法,因为1.两数之和要求返回的是索引下标, 而双指针法一定要排序,一旦排序之后原数组的索引就被改变了。
如果1.两数之和 (opens new window)要求返回的是数值的话,就可以使用双指针法了。
N Sum 问题(N>=2)
参考:一个函数秒杀 2Sum 3Sum 4Sum 问题
采用递归方式,base case是2Sum
/* 注意:调用这个函数之前一定要先给 nums 排序 */
vector<vector<int>> nSumTarget(
vector<int>& nums, int n, int start, int target) {
int sz = nums.size();
vector<vector<int>> res;
// 至少是 2Sum,且数组大小不应该小于 n
if (n < 2 || sz < n) return res;
// 2Sum 是 base case
if (n == 2) {
// 双指针那一套操作
int lo = start, hi = sz - 1;
while (lo < hi) {
int sum = nums[lo] + nums[hi];
int left = nums[lo], right = nums[hi];
if (sum < target) {
while (lo < hi && nums[lo] == left) lo++;
} else if (sum > target) {
while (lo < hi && nums[hi] == right) hi--;
} else {
res.push_back({left, right});
while (lo < hi && nums[lo] == left) lo++;
while (lo < hi && nums[hi] == right) hi--;
}
}
} else {
// n > 2 时,递归计算 (n-1)Sum 的结果
for (int i = start; i < sz; i++) {
vector<vector<int>>
sub = nSumTarget(nums, n - 1, i + 1, target - nums[i]);
for (vector<int>& arr : sub) {
// (n-1)Sum 加上 nums[i] 就是 nSum
arr.push_back(nums[i]);
res.push_back(arr);
}
while (i < sz - 1 && nums[i] == nums[i + 1]) i++;
}
}
return res;
}
简单
242. 有效的字母异位词
参考:242. 有效的字母异位词 - 力扣(LeetCode)
把数组当哈希表
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
int[] record=new int[26]; // 小写字母有26个
// 先把字母相对'a'的位置映射到record数组中,先++再--
// 如果最后数组有不为0的则返回false
for(char c:s.toCharArray()){
record[c-'a']++;
}
for(char c:t.toCharArray()){
record[c-'a']--;
}
for(int item:record){
if(item!=0){
return false;
}
}
return true;
}
}
// 排序
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) {
return false;
}
char[] str1 = s.toCharArray();
char[] str2 = t.toCharArray();
Arrays.sort(str1);
Arrays.sort(str2);
return Arrays.equals(str1, str2);
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/valid-anagram/solution/you-xiao-de-zi-mu-yi-wei-ci-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
349. 两个数组的交集
参考:349. 两个数组的交集 - 力扣(LeetCode)
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set1=new HashSet<>();
Set<Integer> resultSet=new HashSet<>();
//遍历num1
for(int item:nums1){
set1.add(item);
}
//遍历num2的过程中判断哈希表中是否存在该元素
for(int item:nums2){
if(set1.contains(item)){
resultSet.add(item);
}
}
//将resultSet转为数组
int[] resultArr=new int[resultSet.size()];
int i=0;
for(int item:resultSet){
resultArr[i++]=item;
}
return resultArr;
}
}
1. 两数之和
class Solution {
public int[] twoSum(int[] nums, int target) {
int[] result=new int[2];
Map<Integer,Integer> map=new HashMap<>();
// 先构造map,并把nums映射到其中
for(int i=0;i<nums.length;i++){
map.put(nums[i],i);
}
for(int i=0;i<nums.length;i++){
int temp=target-nums[i]; // 两数之差
// 如果temp在map中且不是nums[i]本身
if(map.containsKey(temp)&& map.get(temp)!=i){
result[0]=i;
result[1]=map.get(temp);
return result;
}
}
return result;
}
}
202. 快乐数
参考:202. 快乐数 - 力扣(LeetCode)
参考:代码随想录
题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!
使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。
class Solution {
public boolean isHappy(int n) {
Set<Integer> set=new HashSet<>();
while(true){
int sum=getSum(n);
if(sum==1){
return true;
}
// 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
if(set.contains(sum)){
return false;
}else{
set.add(sum);
n=sum;
}
}
}
// 取数值各个位上的单数之和
public int getSum(int n){
int sum=0;
while(n>0){
sum+=(n % 10) * (n % 10);; // n%10取最高位
n/=10; // 降低一位
}
return sum;
}
}
383. 赎金信
依然是数组在哈希法中的应用。
一些同学可能想,用数组干啥,都用map完事了,其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
//记录magazine字符串中每个字符的出现次数
int[] a=new int[26]; // 最多有26个小写字母
for(int i=0;i<magazine.length();i++){
// 算出字符与'a'相对位置 'a'为0,'b'为1,以此类推
int index=magazine.charAt(i)-'a';
a[index]++;
}
for(int i=0;i<ransomNote.length();i++){
int index=ransomNote.charAt(i)-'a';
//对于ransomNote中的每一个字符都在数组中查找
//找到相应位减一,否则找不到返回false
if(a[index]>0){
a[index]--;
}else{
return false;
}
}
return true;
}
}
// 官方
class Solution {
public boolean canConstruct(String ransomNote, String magazine) {
if (ransomNote.length() > magazine.length()) {
return false;
}
int[] cnt = new int[26];
for (char c : magazine.toCharArray()) {
cnt[c - 'a']++;
}
for (char c : ransomNote.toCharArray()) {
cnt[c - 'a']--;
if(cnt[c - 'a'] < 0) {
return false;
}
}
return true;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/ransom-note/solution/shu-jin-xin-by-leetcode-solution-ji8a/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
中等
454. 四数相加 II
参考:454. 四数相加 II - 力扣(LeetCode)
就相当于两数之和等于0,第一个数是前两个数组之和,第二个数是后两个数组之和
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer, Integer> map = new HashMap<>();
int result=0;
int temp=0;
// 统计两个数组中的元素之和,同时统计出现的次数,放入map
// key为元素之和,value为出现的次数
for(int i:nums1){
for(int j:nums2){
temp=i+j;
if(map.containsKey(temp)){
map.put(temp,map.get(temp)+1);
}else{
map.put(temp,1);
}
}
}
//统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时加上出现的次数
for(int i:nums3){
for(int j:nums4){
temp=0-i-j;
if(map.containsKey(temp)){
result += map.get(temp);
}
}
}
return result;
}
}
15. 三数之和
哈希表法
参考:由浅入深-递进四解法<叫我小白就好>【三数之和】认真是一种态度 - 三数之和 - 力扣(LeetCode) 写得非常好
参考:三数和,哈希表 - 三数之和 - 力扣(LeetCode)
// map法
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result=new ArrayList<List<Integer>>();
int n=nums.length;
if(n<3) return result;
Arrays.sort(nums); // 先排序 满足从左到右遍历时,得到的数字a<=b<=c
Map<Integer,Integer> map=new HashMap<>();
// 把数组映射到集合中
for(int i=0;i<n;i++){
map.put(nums[i],i);
}
// i:[0,n-1) i<n也可以,就是后面几次是无效遍历,下同
for(int i=0;i<n-2;i++){
// 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if (nums[i] > 0) {
break;
}
// 第一个数去重 比如i=1,nums[0]=num[1]时可跳过,即连续两个数及以上相同的可以跳过
if(i>0 && nums[i]==nums[i-1]) {
continue;
}
// j:[0,n)
for(int j=i+1;j<n-1;j++){
// 第二个数去重
if(j>i+1&&nums[j]==nums[j-1]){
continue;
}
int c=0-nums[i]-nums[j]; // 第三个数
// map.get(c); // 有则返回下标,没有则返回null
// a<=b<=c且map中有第三个数且第三个数的下标应该大于j
if (map.get(c) != null && map.get(c) > j) {
ArrayList list=new ArrayList<Integer>();
list.add(nums[i]);
list.add(nums[j]);
list.add(c);
result.add(list);
}
}
}
return result;
}
}
// set法 [1,2,-2,-1]无法通过
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result=new ArrayList<List<Integer>>();
int n=nums.length;
if(n<3) return result;
Arrays.sort(nums); // 先排序 满足从左到右遍历时,得到的数字a<=b<=c
HashSet<Integer> set=new HashSet<Integer>();
// 把数组映射到集合中
for(int item:nums){
set.add(item);
}
int target=0;// 目标值
for(int i=0;i<n-2;i++){
// 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if (nums[i] > 0) {
break;
}
// 第一个数去重 比如i=1,nums[0]=num[1]时可跳过,即连续两个数及以上相同的可以跳过
if(i>0 && nums[i]==nums[i-1]) {
continue;
}
target=0-nums[i];
for(int j=i+1;j<n-1;j++){
// 第二个数去重
if(j>i+1&&nums[j]==nums[j-1]){
continue;
}
int c=target-nums[j]; // 第三个数
// a<=b<=c且set中有第三个数
if(set.contains(c)){
// 如果nums有重复的数,j=那个数时这里就取大于等于号,否则为大于号,比较麻烦,所以直接用map。
if(c>=nums[j]) {
ArrayList list=new ArrayList<Integer>();
list.add(nums[i]);
list.add(nums[j]);
list.add(c);
result.add(list);
}
}
}
}
return result;
}
}
双指针法
来自官方题解:排序 + 双指针(其实只用了一个右指针,左指针用了for循环代替)
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result=new ArrayList<List<Integer>>();
int n=nums.length;
if(n<3) return result;
Arrays.sort(nums); // 先排序 满足从左到右遍历时,得到的数字a<=b<=c
for(int first=0;first<n;first++){
// 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if (nums[first] > 0) {
break;
}
// 第一个数去重 比如i=1,nums[0]=num[1]时可跳过,即连续两个数及以上相同的可以跳过
if(first>0 && nums[first]==nums[first-1]) {
continue;
}
int third=n-1;
int target=-nums[first];// 目标值
for(int second=first+1;second<n;second++){
// 第二个数去重
if(second>first+1 && nums[second]==nums[second-1]) {
continue;
}
// 右指针向左移动
while(second<third && nums[second]+nums[third] > target){
-- third;
}
if(second==third){
break;
}
if(nums[second]+nums[third] == target){
// 直接从数组中创建List
result.add(Arrays.asList(nums[first],nums[second],nums[third]));
}
}
}
return result;
}
}
while 双指针 效果更好
import java.util.*;
class Solution {
public static void main(String[] args) {
int[] nums = { -1, 0, 1, 2, -1, -4 };
System.out.println(threeSum(nums));
}
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
int n = nums.length;
if (n < 3)
return result;
Arrays.sort(nums); // 先排序 满足从左到右遍历时,得到的数字a<=b<=c
for (int i = 0; i < n - 2; i++) {
// 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if (nums[i] > 0) {
break;
}
// 第一个数去重 比如i=1,nums[0]=num[1]时可跳过,即连续两个数及以上相同的可以跳过
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = n - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum > 0) {
right--;
} else if (sum < 0) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 去重 当前的数与下一个数相等时跳一步,直到指向相同数中的最后一个数
while (left < right && nums[left] == nums[left + 1])
left++;
while (left < right && nums[right] == nums[right - 1])
right--;
// 1.while中有相同的,此时left指向最后一个相同的数,再left++,指向下一个不同的数
// 2.while中没有,left++,也指向下一个不同的数
left++;
right--;
}
}
}
return result;
}
}
18. 四数之和
参考:此题解可通用于5数之和、6数之和…:注释详细,思路清晰 - 四数之和 - 力扣(LeetCode)
双指针法
for+右指针
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
int n = nums.length;
if (n < 4) return result;
Arrays.sort(nums); // 先排序 满足从左到右遍历时,得到的数字a<=b<=c<=d
for (int first = 0; first < n-3; first++) {
// 第一个数去重 比如i=1,nums[0]=num[1]时可跳过,即连续两个数及以上相同的可以跳过
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
for (int second = first + 1; second < n-2; second++) {
// 第二个数去重
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
for (int third = second + 1; third < n-1; third++) {
// 第三个数去重
if (third > second + 1 && nums[third] == nums[third - 1]) {
continue;
}
int four = n - 1; // 右指针指向最后一个位置
// 右指针向左移动
while (third < four && nums[first] + nums[second] + nums[third]+nums[four] > target) {
--four;
}
if (four == third) {
break;
}
if (nums[first] + nums[second] + nums[third] + nums[four] == target) {
result.add(Arrays.asList(nums[first], nums[second], nums[third], nums[four]));
}
}
}
}
return result;
}
}
while 双指针
import java.util.*;
class Solution {
public static void main(String[] args) {
int[] nums = { 1, -2, -5, -4, -3, 3, 3, 5 };
System.out.println(fourSum(nums, -11));
}
public static List<List<Integer>> fourSum(int[] nums, int target) {
List<List<Integer>> result = new ArrayList<List<Integer>>();
int n = nums.length;
if (n < 4)
return result;
Arrays.sort(nums); // 先排序 满足从左到右遍历时,得到的数字a<=b<=c<=d
for (int i = 0; i < n - 3; i++) {
// 第一个数去重 比如i=1,nums[0]=num[1]时可跳过,即连续两个数及以上相同的可以跳过
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
for (int j = i + 1; j < n - 2; j++) {
// 第二个数去重
if (j > i + 1 && nums[j] == nums[j - 1]) {
continue;
}
int left = j + 1;
int right = n - 1;
while (left < right) {
int sum = nums[i] + nums[j] + nums[left] + nums[right];
if (sum > target) {
right--;
} else if (sum < target) {
left++;
} else {
result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
// 去重 当前的数与下一个数相等时跳一步,直到指向相同数中的最后一个数
while (left < right && nums[left] == nums[left + 1])
left++;
while (left < right && nums[right] == nums[right - 1])
right--;
// 1.while中有相同的,此时left指向最后一个相同的数,再left++,指向下一个不同的数
// 2.while中没有,left++,也指向下一个不同的数
left++;
right--;
}
}
}
}
return result;
}
}