【代码随想录】刷题Day2【哈希表】

50 阅读3分钟

202. 快乐数

题目

image.png

思路

题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!,如果 sum 重复出现了,那么就不可能是快乐数了

可能的三种情况:

  1. 最终会得到 1。
  2. 最终会进入循环。
  3. 值会越来越大,最后接近无穷大。

第三个情况比较难以检测和处理。我们怎么知道它会继续变大,而不是最终得到 11 呢?我们可以仔细想一想,每一位数的最大数字的下一位数是多少。

image.png 对于3位数的数字,它不可能大于 243。这意味着它要么被困在 243 以下的循环内,要么跌到 1。4 位或 4 位以上的数字在每一步都会丢失一位,直到降到 3 位为止。所以我们知道,最坏的情况下,算法可能会在 243 以下的所有数字上循环,然后回到它已经到过的一个循环或者回到 1。但它不会无限期地进行下去,所以我们排除第三种选择。

代码

public boolean isHappy(int n) {
    Set<Integer> record = new HashSet<>();
    while (n != 1 && !record.contains(n)) {
        record.add(n);
        n = getNextNumber(n);
    }
    return n == 1;
}

private int getNextNumber(int n) {
    int res = 0;
    while (n > 0) {
        int temp = n % 10;
        res += temp * temp;
        n = n / 10;
    }
    return res;
}

454. 四数相加 II

题目

image.png

思路

假如:abcd四个数,如何判断他们的和是否为0呢?

我们可以这样做:先把a,b的所有情况存起来,然后c,d的所有情况存起来;最后再来判断他们有多少组符合条件(这个题目是四个数组还进行组合的,就不要考虑去重

代码

public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
    HashMap<Integer,Integer> map1 = new HashMap<>();

    int tmp = 0;
    for (int i = 0; i < nums1.length; i++) {
        for (int j = 0; j < nums2.length; j++) {
            tmp = nums1[i] + nums2[j];
            if(map1.get(tmp) == null) {
                map1.put(tmp,1);
            } else {
                map1.put(tmp,map1.get(tmp)+1);
            }
        }
    }

    int count = 0;
    for (int i = 0; i < nums3.length; i++) {
        for (int j = 0; j < nums4.length; j++) {
            tmp = nums3[i] + nums4[j];
            if(map1.containsKey(-tmp)) {
                count += map1.get(-tmp);
            }
        }
    }
    return count;
}

15. 三数之和

题目

image.png

思路

  1. 可以用 hash,类似于上面的四数相加,双层 for 循环,一层确定 a,一层确定 b,然后在 hash 中判断 0-(a+b)是否存在,但是去重很复杂
  2. 双指针法(滑动窗口)

2.1:先对数组进行排序,方便去重

2.2:a 遍历完整个数组,然后在 b 和 c 的区间中找符合条件的数值【a,b,c都需要去重】

代码

public List<List<Integer>> threeSum(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();

    Arrays.sort(nums);
    for (int i = 0; i < nums.length; i++) {
        // 不可能存在三元组的和 = 0了 - nums[i] 是当前区间最小的了
        if(nums[i] > 0) {
            return result;
        }

        // 去重,i-1下标开头的都添加完了
        if(i > 0 && nums[i] == nums[i-1]) {
            continue;
        }

        int left = i+1;
        int right = nums.length-1;
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            if(sum == 0) {
                List<Integer> tmp = new ArrayList<>();
                tmp.add(nums[i]);
                tmp.add(nums[left]);
                tmp.add(nums[right]);
                result.add(tmp);

                // 对left和right去重
                while(left < right && nums[left] == nums[left+1]) left++;
                while(left < right && nums[right] == nums[right-1]) right--;

                left++;
                right--;
            } else if(sum > 0) {
                right--;
            } else {
                left++;
            }
        }
    }

    return result;
}

18. 四数之和

题目

思路

跟三数相加差不多,就是需要两层循环,确认前两个的位置

1.在循环内的判断,就不能直接判断大于target,因为target可能是负数,然后两个数也是负数,负数加负数越小。对于target为负而言,如果直接判断大于target,那么就会失去一些结果,比如: {-2,-1,0,0} -3。

2.第一层的去重和三数相加没有区别,第二层的去重条件要从i+1开始判断,因为循环本身j=i+1开始的

3.那关于left和right都是没有变化的,不过本题会出现大数字,所以四数相加还得转为long型判断是否与target符合要求。

代码

public List<List<Integer>> fourSum(int[] nums, int target) {
    List<List<Integer>> result = new ArrayList<>();

    Arrays.sort(nums);

    for (int n = 0; n < nums.length; n++) {
        // 一级剪枝 + 一级去重
        if(nums[n] > target && nums[n] > 0) {
            break;
        }
        if(n > 0 && nums[n] == nums[n-1]) {
            continue;
        }

        for (int i = n+1; i < nums.length; i++) {
            // 二级剪枝 + 二级去重
            if(nums[i] + nums[n] > target && nums[i] + nums[n] > 0) {
                break;
            }
            // 去重,i-1下标开头的都添加完了
            if(i > n+1 && nums[i] == nums[i-1]) {
                continue;
            }

            int left = i+1;
            int right = nums.length-1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right] + nums[n];
                if(sum == target) {
                    List<Integer> tmp = new ArrayList<>();
                    tmp.add(nums[n]);
                    tmp.add(nums[i]);
                    tmp.add(nums[left]);
                    tmp.add(nums[right]);
                    result.add(tmp);

                    // 对left 和 right 下标去重
                    while(left < right && nums[left] == nums[left+1]) left++;
                    while(left < right && nums[right] == nums[right-1]) right--;

                    left++;
                    right--;
                } else if(sum > target) {
                    right--;
                } else {
                    left++;
                }
            }
        }
    }

    return result;
}