哈希表
- 哈希表
- 1.有效的字母异位词
- 242.有效的字母异位词
- 383.赎金信
- 49.字母异位词分组
- 438.找到字符串中所有字母异位词
 
- 2.两个数组的交集
- 349.两个数组的交集
- 350.两个数组的交集||
 
- 3.快乐数202
- 202.快乐数
 
- 4.两数之和
- 1.两数之和
 
- 5.四数相加
- 454.四数相加||
 
- 6.三数之和
- 15.三数之和
 
- 7.四数之和
- 18.四数之和
 
 
 
哈希表
1.有效的字母异位词
242.有效的字母异位词

 思路: 就是判断两个字符串包含的字母是不是一样的。我们可以用一个数组来存储这些。遍历s的时候把字符出现的个数用数组来存储,遍历t的时候再把相应的个数减去,最后判断数组里边的元素是不是都为0.
 public boolean isAnagram(String s, String t) {
         int []arr=new int[26];
         for(int i=0;i<26;i++) {
        	 arr[i]=0;
         }
         for(int i=0;i<s.length();i++) {
        	 arr[s.charAt(i)-'a']++;
         }
         for(int i=0;i<t.length();i++) {
        	 arr[t.charAt(i)-'a']--;
         }
         for(int i=0;i<26;i++) {
        	 if(arr[i]!=0) {
        		 return false;
        	 }
         }
         return true;
	    
	 }
383.赎金信

 思路:
 和上一题思路一样,这道题只不过是串的范围大小的问题,后边的字符串字母可以多几个。
 public boolean canConstruct(String ransomNote, String magazine) {
		 int []arr=new int[26];
         for(int i=0;i<26;i++) {
        	 arr[i]=0;
         }
         for(int i=0;i<magazine.length();i++) {
        	 arr[magazine.charAt(i)-'a']++;
         }
         for(int i=0;i<ransomNote.length();i++) {
        	 arr[ransomNote.charAt(i)-'a']--;
         }
         for(int i=0;i<26;i++) {
        	 if(arr[i]<0) {
        		 return false;
        	 }
         }
         return true;
	   }
49.字母异位词分组

 思路:
 字母异位词就是字母个数相同而排列顺序不同的字符。我们可以把字母排序好的字符串当作键,然后对应的字符串当作值,最后依次输出即可。
 public List<List<String>> groupAnagrams(String[] strs) {
	    Map<String,List<String>> map=new HashMap<String,List<String>>();
	    for(String str:strs) {
	    	char []array=str.toCharArray();
	    	Arrays.sort(array);
	    	String key=new String(array);
	    	List<String> list=map.getOrDefault(key, new ArrayList<String>());
	    	list.add(str);
	    	map.put(key,list);
	    }
	    return new ArrayList<List<String>>(map.values());
	 }
438.找到字符串中所有字母异位词

 思路:
 首先将p字符串出现的字符存在字符数组里面,定义distance表示有效字符的个数。
 一直在滑动窗口里的字符数组填入right指针指向的数据,当distance满足条件时,此时可以缩小窗口了,即移动左指针,当左右指针的差值恰好等于目标字符的长度时,此时就代表子串找到了。
public static List<Integer> findAnagrams(String s, String p) {
		 //将原字符串转换成字符数组
		 char []charArrayS=s.toCharArray();
		 char []charArrayP=p.toCharArray();
		 //分别记录窗口里和原字符串字母出现的个数
		 int []winFreq=new int[26];
		 int []pFreq=new int[26];
		 for(char c:charArrayP) {
			 pFreq[c-'a']++;
		 }
		 int distance=0;//窗口里满足条件的字符串的个数
		 int begin=0;//满足条件的数组的起始位置
		 int left=0,right=0;//左右指针
		 List<Integer> result=new ArrayList<>();
		 while(right<charArrayS.length) {
			 //当右指针遇见的字符串在tFreq中没有出现,右指针继续往右移动
			  if(pFreq[charArrayS[right]-'a']==0) {
				  right++;
				  continue;
			  }
			//当遇到的这个字符,窗口内的字符个数小于目标字符个数时,distance++
			  if(winFreq[charArrayS[right]-'a']<pFreq[charArrayS[right]-'a']) {
				  distance++;
				  
			  }
			  
			  winFreq[charArrayS[right]-'a']++;
			  right++;
			  while(distance==p.length()) {
				  if((right-left)==p.length())
				  {
					  result.add(left);
				  }
				  if(pFreq[charArrayS[left]-'a']==0) {
					  left++;
					  continue;
				  }
				  if(winFreq[charArrayS[left]-'a']==pFreq[charArrayS[left]-'a']) {
					  distance--;
					  
				  }
				  winFreq[charArrayS[left]-'a']--;
				  left++;
			  }
		 }
          return result;
	    }
2.两个数组的交集
349.两个数组的交集

 思路:
 用set做:将第一个数组出现的字母存在set里,然后再遍历一次,将第二个数组也在set出现的存在result里即可,result也用set来实现,可以去重。
 用数组做:只需要将对应出现的字母标志位置为1即可。
 public int[] intersection(int[] nums1, int[] nums2) {
         
	   Set<Integer> set1=new HashSet<>();
	   Set<Integer> result=new HashSet<>();
	   for(int i:nums1) {
		   set1.add(i);
	   }
	   for(int i:nums2) {
		   if(set1.contains(i)) {
			   result.add(i);
		   }
	   }
	   return result.stream().mapToInt(x->x).toArray();
	   
	 }
350.两个数组的交集||

 思路:
 用哈希表分别统计出两个数组每个字母出现的次数,然后用出现次数较小的次数加在结果数组里即可。
public  static int[] intersect(int[] nums1, int[] nums2) {
             int[] array1=new int[1005];
             int[] array2=new int[1005];
             List<Integer> result=new ArrayList<>();
             for(int i:nums1) {
            	 array1[i]++;
             }
             for(int i:nums2) {
            	 array2[i]++;
             }
             for(int i=0;i<array2.length;i++) {
            	 if(array2[i]>0&&array1[i]>0) {
            		 int number=array2[i]<array1[i]?array2[i]:array1[i];
            		 for(int j=0;j<number;j++) {
            			 result.add(i);
            		 }
            	 }
             }
             return result.stream().mapToInt(x->x).toArray();
	    
	   }
3.快乐数202
202.快乐数

 思路:
这道题的重点就是判断数字是否会出现无限循环的情况,我们可以把每次遍历的数记录起来,然后如果出现同样的数,说明这个数字就不是快乐数。如果没有出现,就让它进入循环即可。
class Solution {
    public boolean isHappy(int n) {
           Set<Integer>record=new HashSet<Integer>();
           while(n!=1&&!record.contains(n)) {
        	   record.add(n);
        	   n=nextNumber(n);
        	   
           }
           return n==1;
	    
	   }
	   public int nextNumber(int n) {
		   int res=0;
		   while(n>0) {
			   int index=n%10;
			   res+=index*index;
			   n=n/10;
		   }
		   return res;
	   }
}
4.两数之和
1.两数之和

 思路:
 用哈希表来做,数字作为键,数字下标作为值。这样只需要遍历一遍就可以找到答案。每遍历到一个数,找出target-i是否之前遍历过,如果遍历过就返回两个下标。
 至于一个元素出现两次的情况,在hashmap中这种情况,后来出现的会覆盖掉之前出现的数字。题目中要求就是只有一组解,所以这种情况就不用考虑了把。
  public int[] twoSum(int[] nums, int target) {
		   Map<Integer,Integer> maps=new HashMap<>();
		   for(int i=0;i<nums.length;i++) {
			   if(maps.containsKey(target-nums[i])) {
				   return new int[]{maps.get(target-nums[i]),i};
			   }
			   maps.put(nums[i], i);
		   }
		   return new int[]{0};
	    }
5.四数相加
454.四数相加||

 思路:
 将四个数组分成两组,前两个数组a+b的值当成键存在map中,值存这个键出现的次数。然后第二组遍历寻找对应的target值即可。此时的count不应是+1,而是加上map中对应的value值。
public static int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
		   Map<Integer,Integer> maps=new HashMap<>();
		   int count=0;
		   System.out.println(Arrays.toString(nums2));
		   for(int i=0;i<nums1.length;i++) {
			   for(int j=0;j<nums2.length;j++) {
				  
				   maps.put(nums1[i]+nums2[j],maps.getOrDefault(nums1[i]+nums2[j], 0)+1);
			   }
		   }
		   for(int i=0;i<nums3.length;i++) {
			   for(int j=0;j<nums4.length;j++) {
				  int target=0-(nums3[i]+nums4[j]);
				  if(maps.containsKey(target)) {
					  count+=maps.get(target);
				  }
			   }
		   }
		   return count;
	    }
6.三数之和
15.三数之和

 思路:
 先给原始数组排好序,然后第一次遍历代表a。然后分别用两个指针来找剩下两个数字,当三个数的和大于0时,让右指针往左移,当三个数的和小于0时,让左指针往右移。当满足条件后,将满足条件的数值放进数组里即可。
 去重问题:
 a去重:用nums[i]和nums[i-1]来比较,因为如果和它后边的数比较会出现正好出现答案而错过去的情况,例如[-1,-1,2].
 bc去重:当左指针往右走的时候,遇到和左边数值一样的情况,继续往右走即可。同理,右指针也如此。
public List<List<Integer>> threeSum(int[] nums) {
		   //保存结果集
		   List<List<Integer>> result=new ArrayList<List<Integer>>();
		   //先对数组进行排序
		   Arrays.sort(nums);
		   //第一层循环代表数字a
		   for(int i=0;i<nums.length;i++) {
			   //第一个数大于0,直接return
			   if(nums[i]>0) {
				   return result;
			   }
			   //对a进行去重,此时a应该与前一位进行比较,而不是对后一位进行比较
			   if(i>0&&nums[i]==nums[i-1]) {
				   continue;
			   }
			   int left=i+1;//left指针指向i的右一个
			   int right=nums.length-1;//right指向末尾
			   while(left<right) {
				   if((nums[i]+nums[left]+nums[right])==0) {
					   result.add(Arrays.asList(nums[i],nums[left],nums[right]));
					   //对b和c进行去重
					   while(left<right&&(nums[left]==nums[left+1])) {
						   left++;
					   }
					   while(left<right&&(nums[right]==nums[right-1])) {
						   right--;
					   }  
					   
					   left++;
					   right--;
					   
				   }else if((nums[i]+nums[left]+nums[right])<0) {
					   left++;
				   }else if((nums[i]+nums[left]+nums[right])>0) {
					   right--;
				   }
			   }
		   }
		   return result;
	    }
7.四数之和
18.四数之和

 思路
 和三数之和思路一样,只不过又多套了一层循环,剪枝操作时要注意负数的情况。
public List<List<Integer>> fourSum(int[] nums, int target) {
		List<List<Integer>> result = new ArrayList<List<Integer>>();
		Arrays.sort(nums);
		for (int k = 0; k < nums.length; k++) {
			// 剪枝操作,如果nums[k]大于0,并且nums[k]大于target的话,可以直接return了。为啥要target大于0呢,因为如果是负数的情况,会出现两个数相加越来越小的情况
			if (nums[k] > 0 && nums[k]>target) {
				return result;
			}
			// 去重操作,第一层去重k
			if (k > 0 && nums[k] == nums[k - 1]) {
				continue;
			}
			for (int i = k + 1; i < nums.length; i++) {
				// 第二层剪枝操作 java不用第二层剪枝了
//				if ((nums[k] + nums[i]) >=0 &&(nums[k] + nums[i])>target) {
//					return result;
//				}
				// 第二层去重操作
				if (i > k + 1 && nums[i] == nums[i - 1]) {
					continue;
				}
				int left = i + 1;
				int right = nums.length - 1;
				while (left < right) {
					if ((nums[k] + nums[i] + nums[left] + nums[right]) == target) {
						result.add(Arrays.asList(nums[k], nums[i], nums[left], nums[right]));
						// 对b和c进行去重
						while (left < right && (nums[left] == nums[left + 1])) {
							left++;
						}
						while (left < right && (nums[right] == nums[right - 1])) {
							right--;
						}
						left++;
						right--;
					} else if ((nums[k] + nums[i] + nums[left] + nums[right]) < target) {
						left++;
					} else if ((nums[k] + nums[i] + nums[left] + nums[right]) > target) {
						right--;
					}
				}
			}
		}
		return result;
	}


















