Jerry's Blog

Recording what I learned everyday

View on GitHub


12 September 2019

LeetCode(6) -- 342, 349, 350, 367, 371

by Jerry Zhang

P342. Power of Four (Easy)

给一个整数,判断它是否是4的幂。

我的思路

首先必须是2的幂,同时二进制里的那个1,必须在奇数位上,比如4是100,16是10000。。。

0x55555555 = 0101 0101 0101 0101 0101 0101 0101 0101 奇数位全是1。 用它跟自己做“和”运算,还应该等于它本身。

我的代码

public class E_342_PowerOfFour {
    public boolean isPowerOfFour(int num) {
        if (num <= 0) return false;
        return (num & (num - 1)) == 0 && (0x55555555 & num) == num;
    }
}

超过100%

最优解

class Solution {
    public boolean isPowerOfFour(int num) {
        if(num < 1){
            return false;
        }

        while(num % 4 == 0){
            num = num / 4;
        }

        if(num == 1){
            return true;
        }

        return false;
    }
}

这个估计还是因为测试次数不够。如果测试数量足够大时,应该还是数学方法最快。

P349. Intersection of Two Arrays (Easy)

给两个数组,把它们看作集合,求交集。

Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2]

我的思路

把这两个数组都放在HashSet里,然后求HashSet的交集。

我的代码

public class E_349_IntersectionOfTwoArrays {
    public int[] intersection(int[] nums1, int[] nums2) {
        HashSet<Integer> set1 = new HashSet<>();
        HashSet<Integer> set2 = new HashSet<>();
        for (int i : nums1) {
            set1.add(i);
        }
        for (int i : nums2) {
            set2.add(i);
        }
        set1.retainAll(set2);
        int[] arr = new int[set1.size()];
        int i = 0;
        for (Integer integer : set1) {
            arr[i++] = integer;
        }
        return arr;
    }
}

超过37%

最优解

import java.util.Arrays;
class Solution {
    public int[] intersection(int[] nums1, int[] nums2) {
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;

        for(int i :nums1){
            if(i < min) min = i;
            if(i > max) max = i;
        }

        boolean[] table = new boolean[max - min + 1];
        for(int s : nums1) table[s - min] = true;
        int[] temp = new int[table.length];
        int size = 0;
        for(int j :nums2){
            if(j <= max && j >= min && table[j - min]){
                temp[size++] = j;
                table[j - min] = false;
            }
        }
        int[] res = new int[size];
        for(int m =0 ; m < size; m++)  res[m] = temp[m];

        return res;
    }
}

先算出第一个数组的最大值和最小值,然后做了一个手工的哈希表,布尔型。

答案

方法一:

class Solution {
  public int[] set_intersection(HashSet<Integer> set1, HashSet<Integer> set2) {
    int [] output = new int[set1.size()];
    int idx = 0;
    for (Integer s : set1)
      if (set2.contains(s)) output[idx++] = s;

    return Arrays.copyOf(output, idx);
  }

  public int[] intersection(int[] nums1, int[] nums2) {
    HashSet<Integer> set1 = new HashSet<Integer>();
    for (Integer n : nums1) set1.add(n);
    HashSet<Integer> set2 = new HashSet<Integer>();
    for (Integer n : nums2) set2.add(n);

    if (set1.size() < set2.size()) return set_intersection(set1, set2);
    else return set_intersection(set2, set1);
  }
}

方法二:

class Solution {
  public int[] intersection(int[] nums1, int[] nums2) {
    HashSet<Integer> set1 = new HashSet<Integer>();
    for (Integer n : nums1) set1.add(n);
    HashSet<Integer> set2 = new HashSet<Integer>();
    for (Integer n : nums2) set2.add(n);

    set1.retainAll(set2);

    int [] output = new int[set1.size()];
    int idx = 0;
    for (int s : set1) output[idx++] = s;
    return output;
  }
}

P350. Intersection of Two Arrays II (Easy)

两个数组找交集,有多少次重复的,就要显示多少次。

Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2,2]

我的思路

可以用hashmap存出现的次数。或者,先把两个数组排序,然后依次找到相同的数,数重叠的次数。

我的代码

public class E_350_IntersectionOfTwoArraysII {
    public int[] intersect(int[] nums1, int[] nums2) {
        HashMap<Integer, Integer> map = new HashMap<>();
        ArrayList<Integer> result = new ArrayList<>();
        for (int i = 0; i < nums1.length; i++) {
            if (map.get(nums1[i]) == null) {
                map.put(nums1[i],1);
            } else {
                map.put(nums1[i], map.get(nums1[i]) + 1);
            }
        }
        for (int i = 0; i < nums2.length; i++) {
            if (map.containsKey(nums2[i])) {
                result.add(nums2[i]);
                int value = map.get(nums2[i]);
                if ( value > 1) {
                    map.put(nums2[i], value - 1);
                } else {
                    map.remove(nums2[i]);
                }
            }
        }
        int[] arr = new int[result.size()];
        int i = 0;
        for (Integer integer : result) {
            arr[i++] = integer;
        }
        return arr;
    }
}

超过54%

最优解

class Solution {
    public int[] intersect(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);
        Arrays.sort(nums2);
        int[] ret;
        int i, j, k;
        i = 0; j = 0; k = 0;
        while(i < nums1.length && j < nums2.length){
            if(nums1[i] == nums2[j]){
                nums1[k] = nums1[i];
                k++;
                i++;
                j++;
            }
            else if(nums1[i] < nums2[j])    i++;
            else    j++;
        }
        return Arrays.copyOfRange(nums1, 0, k);
    }

}

用了先排序的方法, 一开始两个数组齐头并进,谁小谁往前走,直到走到一样大,然后把一样大的这个数覆盖掉nums1,k++。最后把0到k作为有效的结果copy出来即可。

P367. Valid Perfect Square (Easy)

给一个正整数,判断它是否是完全平方数。不许用sqrt

我的思路

能想到的就是挨个数试了。。。1的平方,2的平方,3的平方。。。

看了答案,其实还是自己做了简易版的sqrt,还是用的牛顿开平方的方法。

讨论区

public class E_367_ValidPerfectSquare {
    public boolean isPerfectSquare(int num) {
        long r = num;
        while (r * r > num) {
            r = (r + num / r) / 2;
        }
        return r * r == num;
    }
}

要求一个数的开方,就反复猜,先猜它本身(肯定不对啊),只要猜的数的平方大于这个数,就用这个数除以猜的数,再跟猜的数r取平均值。这样就会一点一点接近真实的开方。

P371. Sum of Two Integers (Easy)

求两个整数的和,但不许用加号、减号。。。

我的思路

这个很明显是要用位运算啊。可以把它先转成二进制的字符串,再从个位数字开始一个一个做“与”运算。感觉又有点麻烦。。。不知道有没有更好的办法。

我的代码

public class E_371_SumOfTwoIntegers {
    public int getSum(int a, int b) {
//        String aString = Integer.toBinaryString(a);
//        String bString = Integer.toBinaryString(b);
//        for (int i = aString.length() - 1; i >= 0; i--) {
//            aString.charAt(i);
//        }

        int sum = Integer.sum(a, b);
        return sum;
    }
}

这大概算作弊。。。

最优解

class Solution {
    public int getSum(int a, int b) {
        int carry, sum;
        sum = a ^ b;
        carry = (a & b) << 1;
        while (carry != 0) {
            a = (sum ^ carry);
            b = (sum & carry) << 1;
            sum = a;
            carry = b;
        }
        return sum;
    }
}

第一行sum = a ^ b的意思是,把这两个整数的二进制做“XOR”运算,这样只有在一个0一个1的情况下结果才是1;如果是两个0或两个1,当前位结果都是0,这样就正好跟加法的法则相吻合。但是有一个遗留问题就是进位,carry。如果是一个0一个1,或者两个0,其结果都是正确的,没问题;但如果两个1呢?加起来当前结果虽然是0,但是需要进一位。

手工计算加法的时候,进位的问题一般是从右往左依次进位的。每一位是否有进位,都取决于右边的一位是否要进位。但我们做位运算的时候,其实可以把所有的数位是否需要进位同时算出来。这也就是第二行carry = (a & b) « 1的含义。它算的不是某一位是否要进位,而是所有的位置上是否要进位。

接下来通过while循环,不断地把a更新成异或运算, 把b更新成所有位置上的进位情况。进位本质上就是要向高一位加1,所以其实还是加法。不断地循环这一操作,最终肯定能把进位全部消除掉。最终得到的sum当然就是实际加法的结果。

tags: LeetCode