牛客算法_哈希
1. 两数之和
思路:
1. map, key存放数组元素,value存放下标位置
2. 如果key重复,记录最小的下标
3. 遍历map, target-当前map位置的值, 判断差x在map中的位置
3.1 当前map位置的值 = x, 找到下一个=x的位置
3.2 当前map位置的值 != x, 直接从map中定位到下标
注意:HashMap放入元素的顺序,和map.keySet()的顺序不一致, 改成LinkedHashMap(遍历顺序与插入顺序一致)
- TreeMap(按key排序)
- 自定义排序的 TreeMap
// 按key长度排序
Map<String, Integer> map = new TreeMap<>(Comparator.comparing(String::length).thenComparing(String::compareTo)
);
public int[] twoSum (int[] numbers, int target) {if(numbers == null || numbers.length <= 0){return null;}LinkedHashMap<Integer, Integer> map = new LinkedHashMap();for(int i = 0; i < numbers.length; i++){if(map.get(numbers[i]) == null){map.put(numbers[i], i+1);}}for(Integer key : map.keySet()){int x = target - key;int index = map.get(key);if(map.get(x) != null && map.get(x) != index){int[] res = new int[2];res[0] = index;res[1] = map.get(x);return res;}if(map.get(x) != null && map.get(x) == index){int[] res = new int[2];res[0] = index;int secondIndex = findSecondVal(numbers, index, x);if(secondIndex == -1){continue;}else{res[1] = secondIndex;return res;}}}return null;}private int findSecondVal(int[] numbers, int beginIndex, int findVal) {for(int i = beginIndex; i < numbers.length; i++){if(numbers[i] == findVal){return i+1;}}return -1;}
2. 数组中次数超过一半的数字
public int MoreThanHalfNum_Solution(int[] numbers) {if(numbers == null || numbers.length <= 0){return -1;}int halfLength = numbers.length / 2;HashMap<Integer, Integer> map = new HashMap();for (int i = 0; i < numbers.length; i++) {if (map.get(numbers[i]) == null) {map.put(numbers[i], 1);} else {map.put(numbers[i], map.get(numbers[i]) + 1);}if (map.get(numbers[i]) > halfLength) {return numbers[i];}}return -1;}
3. 数组中只出现一次的两个数字
一个整型数组里除了两个数字只出现一次,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
思路:因为条件限制比较完美(数字要么出现1次 要么2次),所以下面的做法满足要求。
注意:结束TreeMap 保证key的顺序
public int[] FindNumsAppearOnce (int[] nums) {if(nums == null || nums.length <= 0){return null;}TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();for(int i = 0; i < nums.length; i++){if(map.get(nums[i]) == null){map.put(nums[i], i);}else{map.remove(nums[i]);}}int index = 0;int[] res = new int[2];for(Integer key : map.keySet()){res[index++] = key;}return res;}
4. 缺失的最小的正整数
public int minNumberDisappeared(int[] nums) {if (nums == null || nums.length <= 0) {return -1;}HashMap<Integer, Integer> map = new HashMap();for (int i = 0; i < nums.length; i++) {if (nums[i] > 0) {map.put(nums[i], 1);}}for (int i = 1; i < Integer.MAX_VALUE; i++) {if (map.get(i) == null) {return i;}}return -1;}
5. 三数之和(重点)
思路:
先对数组进行排序,这样便于去重和控制遍历顺序
固定第一个数,然后在剩余部分使用双指针寻找另外两个数
通过跳过重复元素来避免重复的三元组
步骤:
对数组进行排序
遍历数组,将当前元素作为第一个数
nums[i]
如果
nums[i] > 0
,由于数组已排序,后面的数都更大,不可能和为0,直接结束如果当前元素与前一个相同,跳过以避免重复
使用双指针
left
和right
在i+1
到n-1
的范围内寻找另外两个数根据三数之和调整指针位置
public ArrayList<Integer> generateList(int x, int y, int w) {ArrayList<Integer> list = new ArrayList<Integer>();list.add(x);list.add(y);list.add(w);return list;}public ArrayList<ArrayList<Integer>> threeSum2(int[] nums) {ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();// 边界情况处理if (nums == null || nums.length < 3) {return result;}// 排序数组Arrays.sort(nums);int n = nums.length;for (int i = 0; i < n - 2; i++) {// 如果第一个数大于0,后面的数都更大,不可能和为0if (nums[i] > 0) {break;}// 跳过重复的第一个数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) {// 找到满足条件的三元组result.add(generateList(nums[i], nums[left], nums[right]));// 跳过重复的leftwhile (left < right && nums[left] == nums[left + 1]) {left++;}// 跳过重复的rightwhile (left < right && nums[right] == nums[right - 1]) {right--;}// 移动指针继续寻找left++;right--;} else if (sum < 0) {// 和太小,需要增大,移动左指针left++;} else {// 和太大,需要减小,移动右指针right--;}}}return result;}