Recursion & Binary search

一 Recursion

  1. base case: smallest problem

  2. subproblem

  3. recursion rule

  •   fibonacci 数列

fibo(n)=fibo(n-1)+fibo(n-2)

如果单纯用递归来写,分析如下:

base case:n=1 return 1

       n=0 return 0

recursion rule: f(n)=f(n-1)+f(n-2)

进入function之后要先判断base case,check是否要停下来

    public static long fibo1(int num){
        if(num==0) return 0;
        if(num==1) return 1;
        
        return fibo1(num-1)+fibo1(num-2);
    }

它的时间空间复杂度分析如下:

时间复杂度相当于计算该树有多少个node (当前TC * 总共被调用多少层)

空间复杂度:how many memory does it cost along the active path?

TRICK:因为前面的node个数总和都没有最后一层多,所以我们分析树的tc的时候往往只看最后一行

但是使用recursion做的TC太高,所以我们通常使用DP,用array或者hashmap都可以

此时因为使用了额外的空间,所以

SC=O(num+1)=O(n)

TC=O(n)

public static long fibo(int num){
        if(num<=0) return 0;
        long[] array=new long[num+1];
        array[0]=0;
        array[1]=1;
        for(int i=2;i<array.length;i++){
            array[i]=array[i-1]+array[i-2];
        }
        return array[num];
    }
    
    public static long fibo2(int num){//hash map写法
        if(num<=0) return 0;
        HashMap<Integer, Long> hashmap=new HashMap<Integer,Long>();
        hashmap.put(0,(long) 0);
        hashmap.put(1,(long) 1);
        for(int i=2; i<num+1;i++){
            hashmap.put(i, hashmap.get(i-2)+hashmap.get(i-1));
        }
        return hashmap.get(num);
    }
  • power(a,b)
public long power(int a, int b){
        //time o(b)
        //space o(b)
        if(b==0){
            return 1;
        }
        return power(a,b-1)*a;
    }
    public long power2(int a, int b){
        //time o(b)
        //space o(1)
        for(int i=0; i<=b;i++){
            a*=a;
        }
        return a;
    }
    public long power3(int a, int b){
        //time O(b)
        //space O(logb)
        if(b==0) return 1;
        if(b%2==0){
            return power3(a,b/2)*power3(a,b/2);
        }else{
            return power3(a,b/2)*power3(a,b/2)*a;
        }
    }
    public long power4(int a, int b){//best
        //time O(logb)
        //space o(logb)
        if(b==0) return 1;
        long half =power4(a,b/2);//先存起来,下次就不用再求
        if(b%2==0){
            return half*half;
        }else{
            return half*half*a;
        }
    }

二 Binary search

Key question: What is binary search?

1. We must guarantee that the search space that (possibly contains the target to find) decrease over time (after each iteration)

2. we must guarantee that the target (if exist) cannot be ruled out accidentally

  • classic binary search

Given a target integer T and an integer array A sorted in ascending order, find the index i such that A[i] == T or return -1 if there is no such index.

public int binarySearch(int[] array, int target) {
    // Write your solution here.
    if(array==null || array.length==0) return -1;
    int left=0;
    int right=array.length-1;
    while(left<=right){
     int mid=left+(right-left)/2;
     if(array[mid]==target){
       return mid; 
     }else if(array[mid]>target){
          right=mid-1; 
     }else{
          left=mid+1; 
     }
    }
    return -1;
  }

如何debug? 当length=0或1时

循环的条件是[left,right]中无元素,说明array中无target

mid=left+(right-left)/2

不可以写成mid=(right-left)/2会integer overflow

  • First Occurance

Given a target integer T and an integer array A sorted in ascending order, find the index of the first occurrence of T in A or return -1 if there is no such index.

public int firstOccur(int[] array, int target) {
    // Write your solution here
    if(array==null || array.length==0) return -1;
    int left=0;
    int right=array.length-1;
    while(left+1<right){
     int mid=left+(right-left)/2;
     if(array[mid]>=target){
      right=mid; //因为mid可能就是我们要的
     }else{
      left=mid+1; 
     }
    }
    if(array[left]==target) return left;
    if(array[right]==target) return right;
    return -1;
  }

循环条件是left+1<right,因为当l==r的时候,我们不希望它进入循环,这样mid=l,所以l和r的位置不会变了会进入死循环

直接可以判断返回l或r的原因是,不断的缩小sorted array的范围,最后要么在l要么在r,除非不存在

  • Last Occurance

Given a target integer T and an integer array A sorted in ascending order, find the index of the last occurrence of T in A or return -1 if there is no such index.

public int lastOccur(int[] array, int target) {
    // Write your solution here
    if(array==null || array.length==0) return -1;
    int left=0;
    int right=array.length-1;
    while(left+1<right){
         int mid=left+(right-left)/2;
      if(array[mid]<=target){
           left=mid; 
      }else{
        right=mid-1; 
      }
    }
    if(array[right]==target) return right;
    if(array[left]==target) return left;
    return -1;
  }
  •  Closest in sorted array

Given a target integer T and an integer array A sorted in ascending order, find the index i in A such that A[i] is closest to T.

public int closest(int[] array, int target) {
    // Write your solution here
    if(array==null || array.length==0) return -1;
    int left=0;
    int right=array.length-1;
    while(left<right-1){//因为最后要比较l和r两个元素
      int mid=left+(right-left)/2;
      if(array[mid]==target){
       return mid; 
      }else if(array[mid]>target){
       right=mid; 
      }else{
        left=mid;
      }
    }
    if(Math.abs(array[left]-target)<Math.abs(array[right]-target)){
     return left; 
    }else{
     return right; 
    }
  }

right=mid

left=mid,因为可能mid处的数最近

  • k closest in sorted array

Given a target integer T, a non-negative integer K and an integer array A sorted in ascending order, find the K closest numbers to T in A.

public int[] kClosest(int[] array, int target, int k) {
    // Write your solution here
    int[] res=new int[k];
    if(k==0) return res;
    int left=0;
    int right=array.length-1;
    while(left<right-1){
      int mid=left+(right-left)/2;
      if(array[mid]==target){
       left=mid; 
      }else if(array[mid]>target){
       right=mid; 
      }else{
       left=mid; 
      }
    }
    
    for(int i=0; i<k;i++){
     if(left < 0 || (right <= array.length-1&&Math.abs(array[left]-target)>Math.abs(array[right]-target))){
       res[i]=array[right];
       right+=1;
     }else{
       res[i]=array[left]; 
       left-=1;
     }
  }
   return res;
  }

原理就是先找到最接近target的数,把范围缩小到l或者r对应的元素,然后进行比较,谁近移谁。

这种算法SC=O(K),因为call了一个额外的数组空间

TC=O(logn+k),logn是前面binary search的过程,K是后面iteration的部分.

第二种算法TC=O(logn+logk),用两次binary search!后面Iteration可以看成两个"sorted" array (之后更新)

  • Search in unknown size sorted array

Given a integer dictionary A of unknown size, where the numbers in the dictionary are sorted in ascending order, determine if a given target integer T is in the dictionary. Return the index of T in A, return -1 if T is not in A.

 public int search(Dictionary dict, int target) {
    // Write your solution here
    if(dict==null) return -1;
    int left=0;
    int right=left+1;
    while(dict.get(right)!=null && dict.get(right)<=target){//确定包含target的左右边界
     if(dict.get(right)==target) return right;
     else{
         left=right;
         right=right*2;
     }
    }
    
    while(left<=right){
     int mid=left+(right-left)/2;
     if(dict.get(mid)==null || dict.get(mid)>target){
      right=mid-1; 
     }else if(dict.get(mid)==target){
      return mid; 
     } else{
      left=mid+1; 
     }
    }
    return -1;
  }

(如果写成两个函数可能看起来更好吧)

需要注意的是,在进行binary search的时候,可能right是超过边界的,于是mid可能超过边界,此时需要先判断mid是否超过边界,如果是,则right=mid-1;

这样写是错的:

if(dict.get(mid)==target){
   return mid; 
}else if(dict.get(mid)>target){
      right=mid-1; 
}else {
      left=mid+1; 
}

为什么呢?如果mid越界了,第一个if会报npe的错!!

如果一定要把mid==target放在第一位,那只能每次判断条件都加!=null :)

为什么不判断right是否超界呢?

假如:dict=[1],target=1;

此时,right还未进第一个while就超界了,所以直接binary search,这时mid=left=0,dict.get(right)==null,我们执行第一个if,即right=mid-1=0,这样改变了right值,直接就跳出循环了

直接返回-1了

这不是我们要的结果,所以我们要判断mid是否越界,此时mid=left=0,没有越界,于是缩小right到0,此时再次while循环,dict.get(mid)==target,return mid,这是我们要的

为什么每次right*2而不是其他数呢?

假设我们*10

那我们计算一下时间复杂度:

                     查找边界(跳出)                        binary search

2                      log2(n)  slow                                      log2(2n) slow

10                     log10(n)   fast                                    log2(10n) fast

log2(n)+log2(2n)-(log10(n)+log2(10n))>0

所以,其实跳10倍是更快的!

(但是懒得跳:))

  • search in sorted matrix I

Given a 2D matrix that contains integers only, which each row is sorted in an ascending order. The first element of next row is larger than (or equal to) the last element of previous row.

Given a target number, returning the position that the target locates within the matrix. If the target number does not exist in the matrix, return {-1, -1}.

public int[] search(int[][] matrix, int target) {
    // Write your solution here
    int[] res={-1,-1};
    int r=matrix.length;//row number
    int c=matrix[0].length;//column number
    int left=0;
    int right=r*c-1;
    while(left<=right){
     int mid=left+(right-left)/2;
     if(matrix[mid/c][mid%c]==target){
       res[0]=mid/c;
       res[1]=mid%c;
       return res;
     }else if(matrix[mid/c][mid%c]>target){
       right=mid-1; 
     }else{
       left=mid+1; 
     }
    }
    return res;
  }

主要思想就是把这个2Dmatrix展开成1D的array,两者坐标相互对应进行binary search

注意!index转换成matrix的坐标要用c来处理!

原文地址:https://www.cnblogs.com/x1mercy/p/8571281.html