有关求和:
1.三数之和(力扣15)
双指针的应用,注意在什么条件下指针如何移动,还有就是要求返回的三元组不重复。
public List<List<Integer>> threeSum(int[] nums) { Set<List<Integer>> r = new HashSet<>(); List<List<Integer>> res = new ArrayList<>(); if(nums.length < 3) return res; Arrays.sort(nums); if(nums[0] > 0 || nums[nums.length-1] < 0) return res; if(nums[0] == 0 && nums[1] == 0 && nums[2] == 0){ res.add(Arrays.asList(0, 0, 0)); return res; } int left; int right; for (int i = 0; i < nums.length-2; i++) { if(nums[i]>0) break; left = i+1; right = nums.length-1; while (left<right) { if(nums[i]+nums[left]+nums[right] == 0) r.add(Arrays.asList(nums[i],nums[left],nums[right])); if(nums[i]+nums[left]+nums[right] < 0) left++; else right--; } } res.addAll(r); return res; }
2.四数之和(力扣18)
整体思路是在三数之和的基础上再套一个for循环,但在第一题的基础上还进行了更好的去重。
public List<List<Integer>> fourSum(int[] nums, int target) { List<List<Integer>> res = new ArrayList<>(); if(nums.length < 4) return res; Arrays.sort(nums); int left; int right; for (int i = 0; i < nums.length-3; i++) { if(i > 0 && nums[i] == nums[i-1]) continue; // 去重 for (int j = i+1; j < nums.length-2; j++) { if(j>i+1 && nums[j] == nums[j-1]) continue; // 去重 left = j + 1; right = nums.length-1; while (left<right){ int sum = nums[i]+nums[j]+nums[left]+nums[right]; if(sum == target){ res.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--; // 去重 left++; right--; } if(sum < target) left++; if(sum > target) right--; } } } return res; }
有关哈希表的练习,该结构使用空间换取时间,以便在查找某元素是否存在时快速判断出结果:
1.有效的字母异位词(力扣242):用HashMap的方式需要判断两个字符串中每个元素出现的次数是否一样;用转为字符数组的方式可以通过排序后遍历来看,若两个字符数组排序后相同则一定是有效的字母异位词。
public boolean isAnagram(String s, String t) { if(s.length() != t.length()) return false; Map<Character,Integer> hm = new HashMap<>(); for (int i = 0; i < s.length(); i++) { hm.put(s.charAt(i),hm.getOrDefault(s.charAt(i),0)+1); } for (int i = 0; i < t.length(); i++) { hm.put(t.charAt(i),hm.getOrDefault(t.charAt(i),0)-1); if(hm.get(t.charAt(i))<0) return false; } return true; // if(s.length()!=t.length()) // return false; // char[] chars = s.toCharArray(); // char[] chart = t.toCharArray(); // Arrays.sort(chars); // Arrays.sort(chart); // int i = 0; // return Arrays.equals(chars,chart); }
2.赎金信(力扣383)
问题通过哈希表解决,需要的字符在表中值加一,能满足的字符在表中值减一,若最终表中没有值大于零的字符则满足。注意:.getOrDefault(Object key,Object defaultValue)方法,当key在表中时返回对应的value,否则返回defaultValue。
public boolean canConstruct(String ransomNote, String magazine) { if(ransomNote.length()>magazine.length()) return false; HashMap<Character,Integer> hm = new HashMap<>(); for (int i = 0; i < ransomNote.length(); i++) { hm.put(ransomNote.charAt(i),hm.getOrDefault(ransomNote.charAt(i),0)+1); } for (int i = 0; i < magazine.length(); i++) { hm.put(magazine.charAt(i),hm.getOrDefault(magazine.charAt(i),0)-1); } for (Integer value : hm.values()) { if (value>0) return false; } return true; }
*3.字母异位词分组(力扣49)
由于字母异位词在对字母排序后是相同的字符串,以此作为哈希表的key,将具有相同特性的词构成一个列表作为哈希表的value。最后哈希表中values即为各个分组。注意哈希表的key可以表示同一类型(字母排序后相等的字符串)的标签,具有唯一性,value保存该类型的所有词组。该题值得复习。
public List<List<String>> groupAnagrams(String[] strs) { List<List<String>> res = new ArrayList<>(); Map<String,List<String>> hm = new HashMap<>(); for (int i = 0; i < strs.length; i++) { char[] arr = strs[i].toCharArray(); Arrays.sort(arr); String key = new String(arr); List<String> list = hm.getOrDefault(key,new ArrayList<>()); list.add(strs[i]); hm.put(key,list); } res.addAll(hm.values()); return res; }
4.找到字符串中所有字母异位词(力扣438)
由上题给的思路将短的那个字符串中字母排序后放入map,当作key,然后使用与短的字符串等长的滑动窗口在长的字符串中找到与map的key互为字母异位词的单词,将下标保存进map的value中。其中value存放的是符合条件的位置下标构成的数组。
public List<Integer> findAnagrams(String s, String p) { if(s.length()<p.length()) return new ArrayList<Integer>(); HashMap<String,ArrayList<Integer>> hm = new HashMap<>(); char[] charp = p.toCharArray(); Arrays.sort(charp); hm.put(new String(charp),new ArrayList<>()); char[] chars = s.toCharArray(); for (int i = 0; i < chars.length-(p.length()-1); i++) { String t= new String(chars,i,p.length()); char[] chart = t.toCharArray(); Arrays.sort(chart); String str = new String(chart); if(hm.get(str) != null){ ArrayList list = hm.get(str); list.add(i); hm.put(str, list); } } return hm.get(new String(charp)); }
5.两个数组的交集(力扣349)
注意保证返回的数组中的值具有唯一性。
public int[] intersection(int[] nums1, int[] nums2) { //hs用来存放第一个数组中出现过的元素 HashSet<Integer> hs = new HashSet<>(); //放入hashSet保证返回的数组中没有重复值。 HashSet<Integer> r = new HashSet<>(); for (int i = 0; i < nums1.length; i++) { hs.add(nums1[i]); } for (int i = 0; i < nums2.length; i++) { if (hs.contains(nums2[i])) r.add(nums2[i]); } int[] res = new int[r.size()]; Iterator iterator = r.iterator(); int i = 0; while (iterator.hasNext()){ res[i++] = (Integer)iterator.next(); } return res; // //双指针法,将排序后的两数组同时设置指针,若指向数字相同则放入新的数组,否则小的向后移,直至末尾,注意新数组插入的唯一性 // Arrays.sort(nums1); // Arrays.sort(nums2); // int length1 = nums1.length; // int length2 = nums2.length; // int index1 = 0; // int index2 = 0; // int index = 0; // int[] intersection = new int[length1 + length2]; // while (index1 < length1 && index2 < length2){ // if(nums1[index1] == nums2[index2]){ // if( index==0 || nums1[index1]!=intersection[index-1]) // intersection[index++] = nums1[index1]; // index1++; // index2++; // } // else // if(nums1[index1] < nums2[index2]) // index1++; // else // index2++; // } // return Arrays.copyOfRange(intersection,0,index); }
6.两个数组的交集2(力扣350)
不需要考虑返回数组中元素的唯一性,直接双指针。
public int[] intersect(int[] nums1, int[] nums2) { Arrays.sort(nums1); Arrays.sort(nums2); int[] intersect = new int[nums1.length + nums2.length]; int index1 = 0; int index2 = 0; int index = 0; while (index1 < nums1.length && index2 < nums2.length){ if(nums1[index1] == nums2[index2]){ intersect[index++] = nums1[index1]; index1++; index2++; } else if(nums1[index1] < nums2[index2]) index1++; else index2++; } return Arrays.copyOfRange(intersect,0,index); }
7.快乐数(力扣202)
由于不是快乐数就会回到原来那个数,用Set不可重复的特性做,若add失败了则必然是回到了原来的那个数。
public boolean isHappy(int n) { //若不是快乐数则必然回到那个数,则hs.add(n)返回false if(n == 1) return true; HashSet<Integer> hs = new HashSet<>(); while (n!=1 && hs.add(n)){ int sum = 0; while (n!=0){ sum += (n%10)*(n%10); n = n/10; } n = sum; } return n==1; }
8.两数之和(力扣1)
用HashMap存放target和数组中每一个数的差,key是差,value是数组中那个数的下标,再次遍历数组时若遍历到一个数是map的key且其下标和key对应的value不同(避免数组中同一个元素用了两次)则找到这两个数。即map中的key存放需要匹配的数,value存放要求返回的下标。
public int[] twoSum(int[] nums, int target) { HashMap<Integer,Integer> hm = new HashMap<>(); for (int i = 0; i < nums.length; i++) { hm.put(target-nums[i],i); } for (int i = 0; i < nums.length; i++) { if(hm.containsKey(nums[i])&&(i!=hm.get(nums[i]))) return new int[]{i,hm.get(nums[i])}; } return new int[]{}; }
*9.四数之和2(454)
继续沿用HashMap中key放所需要匹配的数的方法。
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) { Map<Integer,Integer> hm = new HashMap<>(); int res = 0; for (int i = 0; i < nums1.length; i++) { for (int j = 0; j < nums2.length; j++) { hm.put(-nums1[i]-nums2[j],hm.getOrDefault(-nums1[i]-nums2[j],0)+1); } } for (int i = 0; i < nums3.length; i++) { for (int j = 0; j < nums4.length; j++) { if(hm.containsKey(nums3[i]+nums4[j])) res+=hm.get(nums3[i]+nums4[j]); } } return res; }