哈希表类型 有效的字母异位词 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public boolean isAnagram (String s, String t) { int [] array =new int [26 ]; for (char qs:s.toCharArray()){ array[qs-'a' ]+=1 ; } for (char qs:t.toCharArray()){ array[qs-'a' ]-=1 ; } for (int i:array){ if (i!=0 ){ return false ; } } return true ; }
在这里我们不一定要使用Java提供的hascode方法,我们这里相当于自己重写了hascode方法,使得数组的不同位置可以代表不同的数
两个数组的交集 349. 两个数组的交集 - 力扣(LeetCode)
使用set接口提供 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public int [] intersection(int [] nums1, int [] nums2) { Set<Integer> set1 = new HashSet <>(); Set<Integer> set2 = new HashSet <>(); for (int i : nums1) { set1.add(i); } for (int j : nums2) { if (set1.contains(j)) { set2.add(j); } } int []arr=new int [set2.size()]; int j=0 ; for (int i:set2){ arr[j++]=i; } return arr; }
排序+双指针
思路:数组排序,然后用两个指针分别遍历数组,如果两个指针指向的元素相等 就是其中一个交集,否则比较两个指针指向的元素的大小,较小的向前移动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public int [] intersection(int [] nums1, int [] nums2) { Arrays.sort(nums1); Arrays.sort(nums2); ArrayList<Integer> arrayList=new ArrayList <>(); int index2=0 ; for (int index1=0 ;index1<nums1.length&&index2<nums2.length;){ if (nums1[index1]==nums2[index2]){ if (!arrayList.contains(nums1[index1])){ arrayList.add(nums1[index1]); } index1++; index2++; }else if (nums1[index1]<nums2[index2]){ index1++; }else { index2++; } } int []arr=new int [arrayList.size()]; int j=0 ; for (int i:arrayList){ arr[j]=i; j++; } return arr; }
这里调用了Java提供的集合类,其实这里不进行调用也可以进行操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { public int [] intersection(int [] nums1, int [] nums2) { Arrays.sort(nums1); Arrays.sort(nums2); int length1 = nums1.length, length2 = nums2.length; int [] intersection = new int [length1 + length2]; int index = 0 , index1 = 0 , index2 = 0 ; while (index1 < length1 && index2 < length2) { int num1 = nums1[index1], num2 = nums2[index2]; if (num1 == num2) { if (index == 0 || num1 != intersection[index - 1 ]) { intersection[index++] = num1; } index1++; index2++; } else if (num1 < num2) { index1++; } else { index2++; } } return Arrays.copyOfRange(intersection, 0 , index); } }
快乐数 202. 快乐数 - 力扣(LeetCode)
其实这道题目拥有三种可能,结果为1,得到循环,数值接近无穷大,而其实数值接近无穷大是不能存在的,快乐数 - 快乐数 - 力扣(LeetCode) ,我们知道n的范围,而且9的平方是十个数中值最大的,我们寻找出最大值n其实可以最大可以表示10位数,但是表示不到10个9,这里我们采用9999999999表示最大数,第一次进行运算之后
递归
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public boolean isHappy (int n) { ArrayList<Integer> arrayList=new ArrayList <>(); int sum=0 ; int a=n; arrayList.add(n); while (n!=0 ){ int temp=n%10 ; n=n/10 ; sum=sum+temp*temp; } if (sum==1 ){ return true ; } if (arrayList.contains(sum)){ return false ; } return isHappy(sum); }
集合 这是循环的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public boolean isHappy (int n) { ArrayList<Integer> arrayList=new ArrayList <>(); int sum=n; arrayList.add(n); while (sum!=1 ) { n=sum; sum=0 ; while (n != 0 ) { int temp = n % 10 ; n = n / 10 ; sum = sum + temp * temp; } if (arrayList.contains(sum)){ return false ; }else { arrayList.add(sum); } } return true ; }
双指针 快乐数 - 快乐数 - 力扣(LeetCode)
其实这里面有一个双指针的方法证明存在环形结构,这里其实模拟了一个单链表,只不过我们改变了单链表的next域的指向规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public int getNext (int n) { int totalSum = 0 ; while (n > 0 ) { int d = n % 10 ; n = n / 10 ; totalSum += d * d; } return totalSum; } public boolean isHappy (int n) { int slowRunner = n; int fastRunner = getNext(n); while (fastRunner != 1 && slowRunner != fastRunner) { slowRunner = getNext(slowRunner); fastRunner = getNext(getNext(fastRunner)); } return fastRunner = = 1 ; }
两数之和 暴力排序 1 2 3 4 5 6 7 8 9 10 11 class Solution { public int [] twoSum(int [] nums, int target) { int []a = new int [2 ]; for (int i=0 ;i< nums.length;i++) for (int k=i+1 ;k< nums.length;k++) if (nums[i]+nums[k]==target) {a[0 ]=i;a[1 ]=k;} return a; } }
使用Hashmap1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public int [] twoSum(int [] nums, int target) { HashMap<Integer ,Integer> hashMap=new HashMap <>(); for (int i=0 ;i<nums.length;i++){ hashMap.put(i,nums[i]); } int index1=0 ; int index2=0 ; for (int i=0 ;i<nums.length;i++){ int temp=target-nums[i]; hashMap.remove(i); if (hashMap.containsValue(temp)){ index1=i; index2=getKey(hashMap,temp); } } return new int []{index1,index2}; } public int getKey (HashMap<Integer,Integer> hashMap,Integer value) { for (Integer key: hashMap.keySet()){ if (hashMap.get(key).equals(value)){ return key; } } return -1 ; }
使用Hashmap2 这里使用的是将num数组的值作为key,而数组的下标作为value,因为结果将要返回的是数组的下标,而将数组的下标作为值,可以轻松得到值,而不用使用另外的方法去寻找相关发键
1 2 3 4 5 6 7 8 9 10 public int [] twoSum(int [] nums, int target) { Map<Integer, Integer> hashtable = new HashMap <Integer, Integer>(); for (int i = 0 ; i < nums.length; ++i) { if (hashtable.containsKey(target - nums[i])) { return new int []{hashtable.get(target - nums[i]), i}; } hashtable.put(nums[i], i); } return new int [0 ]; }
四数之和 454. 四数相加 II - 力扣(LeetCode)
对于四个数相加,我们可以先分成两组,每一组中组成的数是两个数组的和,同时设置一个专门用记录一组中一个个数的的数
基础做法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 import java.util.HashMap;import java.util.Set;class Solution { public int fourSumCount (int [] nums1, int [] nums2, int [] nums3, int [] nums4) { HashMap<Integer,Integer> hashMap1=new HashMap <>(); HashMap<Integer,Integer> hashMap2=new HashMap <>(); traverseArray(nums1,nums2,hashMap1); traverseArray(nums3,nums4,hashMap2); int count=0 ; Set<Integer> keySet1=hashMap1.keySet(); Set<Integer> keySet2=hashMap2.keySet(); for (Integer key:keySet1){ int temp= -key; if (keySet2.contains(temp)){ count=count+hashMap2.get(temp)*hashMap1.get(key); } } return count; } public void traverseArray (int [] nums1, int [] nums2,HashMap<Integer,Integer> hashMap) { for (int j : nums1) { for (int k : nums2) { int num = j + k; if (hashMap.containsKey(num)) { int a = hashMap.get(num) + 1 ; hashMap.put(num, a); } else { hashMap.put(num, 1 ); } } } } public static void main (String[] args) { int []num1=new int []{-1 ,-1 }; int []num2=new int []{-1 ,1 }; int []num3=new int []{-1 ,1 }; int []num4=new int []{1 ,-1 }; Solution solution=new Solution (); solution.fourSumCount(num1,num2,num3,num4); } }
官方做法 官方思路与上述的相同,但是效率却很不同,原来第二次我们就可以不使用再次进行遍历,在这里已经可以直接对这个值相加是否为0进行判断,而且getOrDefault可以直接是我们少很多步骤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { public int fourSumCount (int [] A, int [] B, int [] C, int [] D) { Map<Integer, Integer> countAB = new HashMap <Integer, Integer>(); for (int u : A) { for (int v : B) { countAB.put(u + v, countAB.getOrDefault(u + v, 0 ) + 1 ); } } int ans = 0 ; for (int u : C) { for (int v : D) { if (countAB.containsKey(-u - v)) { ans += countAB.get(-u - v); } } } return ans; } }
赎金信 383. 赎金信 - 力扣(LeetCode)
基础解法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { public boolean canConstruct (String ransomNote, String magazine) { HashMap<Character, Integer> hashMap = new HashMap <>(); for (char a : magazine.toCharArray()) { hashMap.put(a, hashMap.getOrDefault(a, 0 ) + 1 ); } for (char a:ransomNote.toCharArray()) { if (hashMap.containsKey(a)) { hashMap.put(a, hashMap.get(a) - 1 ); }else { return false ; } if (hashMap.containsValue(-1 )){ return false ; } } return true ; } }
先将magazine存入数组,然后在一个一个进行查找检测,遇到-1。则说明不够,直接false
巧妙自造hash规则 因为这个存放的是26位字母,数量可数,数组的大小的也确定,根据26个字母对应的ascii值,得到对应的位置,而对应的位置中存储的是重复的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public boolean canConstruct (String ransomNote, String magazine) { int [] array = new int [26 ]; for (char a : magazine.toCharArray()) { array[a - 'a' ]++; } for (char a : ransomNote.toCharArray()) { if (--array[a - 'a' ] == -1 ) { return false ; } } return true ; } }
分析 其实就算法的复杂度上,这两个方法都是m+n级别,但是最终的执行速度结果差别很大。而对比这道题和四数之和这道题,其实思路差不多,但是四数之和不进行自定义hashcode值的方式,是因为这个值的多少与大小无法确定,但是这道题目却可以确定定制的hash数组的大小等等。
三树之和 15. 三数之和 - 力扣(LeetCode)
下面是leetcode给出的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Solution { public List<List<Integer>> threeSum (int [] nums) { int n = nums.length; Arrays.sort(nums); List<List<Integer>> ans = new ArrayList <List<Integer>>(); for (int first = 0 ; first < n; ++first) { 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<Integer> list = new ArrayList <Integer>(); list.add(nums[first]); list.add(nums[second]); list.add(nums[third]); ans.add(list); } } } return ans; } }
下面是我的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import java.util.ArrayList;import java.util.Arrays;import java.util.List;class Solution { public List<List<Integer>> threeSum (int [] nums) { List<List<Integer>> listList = new ArrayList <>(); if (nums.length < 3 ) { return listList; } Arrays.sort(nums); int length = nums.length; for (int i = 0 ; i < length; i++) { if (i!=0 ){ if (nums[i]==nums[i-1 ]){ continue ; } } int temp1 = -nums[i]; int lowIndex = i + 1 ; for (; lowIndex < length; lowIndex++) { int fastIndex = lowIndex + 1 ; boolean flag=false ; if (fastIndex>=length){ continue ; } int temp2 = temp1 - nums[lowIndex]; while (temp2 != nums[fastIndex]) { fastIndex++; if (fastIndex>=length){ flag=true ; break ; } } if (flag){ continue ; } if (nums[fastIndex] == temp2) { ArrayList<Integer> arrayList = new ArrayList <>(); arrayList.add(nums[i]); arrayList.add(nums[lowIndex]); arrayList.add(nums[fastIndex]); if (!listList.contains(arrayList)) { listList.add(arrayList); } } } } return listList; } }
这两个思路完全相同,而且大部分的测试用例都可以通过,但是我的代码最终超出时间限制了,但是他对排序后的条件再一次进行了使用 ,判断出最终的值是否在应该排序的范围内部,减少了不必要的无效重复循环。