LeetCode_TwoSum

LeetCode_1_TwoSum

特点:数据无序、无重,确定有唯一解

  1. 直接双重循环暴力解决,时间 O n 2 O_{n^2} On2,空间 O 1 O_{1} O1

  2. 每次都要遍历查找,因此可以通过空间换时间缩短查找的时间开销,先遍历一次建立散列表(HashMap),再遍历一次利用散列表查找,时间 O n O_{n} On,空间 O n O_{n} On。(此外还有HashTable,HashSet)

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            Map<Integer, Integer> map = new HashMap<>();
            for (int i = 0; i < nums.length; i++) {
                map.put(nums[i], i);
            }
            for (int i = 0; i < nums.length; i++) {
                int numToFind= target - nums[i];
                if (map.containsKey(numToFind) && map.get(numToFind) != i) {
                    return new int[] { i, map.get(numToFind) };
                }
            }
            throw new IllegalArgumentException("No two sum solution");
        }
    }
    
  3. 补是相互概念在第一次遍历建立散列表的时候,就可即时查找。在遍历到答案对中出现的第一个元素时,查早了,有补但还没插入,但不用慌,后续遍历到答案对的第二个元素时,直出解。

    这里由于是先判断是否有补,再将元素插入散列表,因此,无需判断是否补为自身

    class Solution {
        public int[] twoSum(int[] nums, int target) {
            Map<Integer, Integer> map = new HashMap<>();
            for (int i = 0; i < nums.length; i++) {
                int numToFind= target - nums[i];
                if (map.containsKey(numToFind)) {
                    return new int[] { map.get(numToFind), i };
                }
                map.put(nums[i], i);
            }
            throw new IllegalArgumentException("No two sum solution");
        }
    }
    
  4. Java泛型的类型参数必须是non-primitive type。不能用 int,而是用 Integer,即其封装类型。(class, interface, array也可作为其类型参数)

LeetCode_167_TwoSum II

特点:数据升序、无重,确定有唯一解

  1. 相比于之前,数据变为有序,因此应考虑如何利用数据的顺序性带来的信息增益

  2. 双指针法之对撞指针
    定义两个指针分别从数据始末相对遍历,左指针右移时,两数和增,右指针左移时,两数和减。依次搜索至解。 t : O n t:O_{n} t:On s : O 1 s:O_{1} s:O1

    对撞指针的适用场景应是:数据必然是有序的,而其排列上,沿左右两个方向有明显的单调性,以及必定可通过对撞的方式求解(好像是句废话)。

    class Solution {
        public int[] twoSum(int[] numbers, int target){
            int i = 0;
            int j = numbers.length - 1;
            while (numbers[i] + numbers[j] != target){
                if (numbers[i] + numbers[j] < target)
                    i++;
                else 
                    j--;
            }
            return new int[] {i+1, j+1};
        }
    }
    
  3. 二分查找:由于数据有序,故可考虑使用二分查找法。那么整体思路也就很简单,在遍历的同时查找。

    class Solution {
        public int[] twoSum(int[] numbers, int target) {
            int n = numbers.length;
            for(int i=0;i<n-1;i++){
               int pos = Arrays.binarySearch(numbers,i+1,n,target-numbers[i]);
               if(pos>0) return new int[]{i+1,pos+1};
            }
        }
    }
    
  4. 二分查找源码实现

    public static int binarySearch(char[] a, int fromIndex, int toIndex,
                                   char key) {
        rangeCheck(a.length, fromIndex, toIndex);
        return binarySearch0(a, fromIndex, toIndex, key);
    }
    
    
    static void rangeCheck(int arrayLength, int fromIndex, int toIndex) {
        if (fromIndex > toIndex) {
            throw new IllegalArgumentException(
                "fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
        }
        if (fromIndex < 0) {
            throw new ArrayIndexOutOfBoundsException(fromIndex);
        }
        if (toIndex > arrayLength) {
            throw new ArrayIndexOutOfBoundsException(toIndex);
        }
    }
    
    
    private static int binarySearch0(char[] a, int fromIndex, int toIndex,
                                     char key) {
        int low = fromIndex;
        int high = toIndex - 1;
    
        while (low <= high) {
            int mid = (low + high) >>> 1;
            char midVal = a[mid];
    
            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found.
    }
    
  5. 计算机中的位操作按补码进行的。

  6. 正数的二进制原码,反码,补码都是一样的,引入此概念只是为了限制负数。原码:正负数的二进制码,其中负数的最高位为1表示负号。反码:除符号位外,其余位取反即得反码。补码:反码+1得补码,其中定义 minValue = -1000_0000 = -128(以byte类型数为例)。参考资料

  7. java中,<<为左移,低位补0,负数的符号位会被移出>>为右移,正数高位补0,负数补1>>>为无符号右移,高位补0

  8. java中,byte类型数在位移运算时,会自动被转换成 int 类型,再进行位移运算,最终得到一个 int 类型的数。参考资料

    byte ori = -20; //补码 1110_1100
    int a = (ori<<3); //a = -160
    byte b = (byte)(ori<<3); //b = 96
    int c = (ori>>>1); //c = 2147483638
    byte d = (byte)(ori>>>1); //d = -10
    byte e = (byte)((ori&(0xff))>>>1); //e = 118
    
  9. 很迷啊,源码使用的无符号右移到底有啥用?网上都是防溢出的做法。

LeetCode_653_TwoSum IV

特点:输入是二叉搜索树

  1. 最直观的想法,中序遍历一次BST建立有序数组,即可转化为 LeetCode_167_TwoSum II 的情况,之后双指针即可。脑补 t : O n , s : O n t:O_{n},s:O_{n} t:On,s:On
  2. 仔细想一下,既然已经有一个良好的搜索树结构,那就想办法利用这个优势。故可层序遍历BST,同时搜索是否有补。脑补 t : O n l o g n , s : O 1 t:O_{nlogn},s:O_{1} t:Onlogn,s:O1(如果是平衡二叉的话)。
    好吧,这种做法确实利用了搜索树结构的优势,但本质就是最直接的暴力解法。
  3. 好吧,solution 提供的解法1用了HashSet,核心思想与之前的一致,也就是时间换空间, t : O n , s : O n t:O_{n},s:O_{n} t:On,s:On。这个应该比我的第一个想法要快(只遍历一次)。但这个好像没用到二叉搜索树的特点,随便一个二叉树都能用这种方法,佛了…。
    class Solution {
        public boolean findTarget(TreeNode root, int k) {
            HashSet<Integer> set = new HashSet<>();
            return  find(root, k, set);
        }
        public boolean find(TreeNode root, int k, HashSet set){
            if (root==null)
                return false;
            if (set.contains(k-root.val))
                return true;
            set.add(root.val);
            return find(root.left, k, set)||find(root.right, k, set);
        }
    }
    
  4. 解法2跟解法1思路一致,只不过没用递归,而是额外维护一个队列,BFS遍历整个树 t : O n , s : O n t:O_{n},s:O_{n} t:On,s:On,两者空间复杂度应该一样。
  5. 解法3,芜湖,跟我一开始想的一样!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值