Algorithm ladder II

Dec 25, 26.

Mission:

  • lintcode 459 Closest Number in Sorted Array
  • lintcode 458 last-position-of-target
  • lintcode 28 Search a 2D Matrix
  • lintcode 585 Maximum Number in Mountain Sequence
  • lintcode 447 Search in a Big Sorted Array
  • lintcode 159 Find Minimum in Rotated Sorted Array
  • lintcode 75 Find Peak Element
  • lintcode 74 First Bad Version
  • lintcode 62 leetcode33 Search in Rotated Sorted Array classic binary search question
  • leetcode 81. Search in Rotated Sorted Array II O(n), to do
  • lintcode 600 Smallest Rectangle Enclosing Black Pixels
  • lintcode 462 Total Occurence of Target
  • lintcode 254 Drop Eggs problem
  • lintcode 14 First Position of Target, easy
  • lintcode 460. k closest numbers in sorted array; leetcode 658 Find K Closest Elements

Codes

lintcode 459 Closest Number in Sorted Array

Standard binary search

  1. condition for stopping the loop: lo+1 < hi ===> will leave 2 indexes lo and hi.
  2. the final solution will be related to lo and hi.
package algorithm_ladder_II;

/**
 * 在一个排好序的数组 A 中找到 i 使得 A[i] 最接近 target(存在重复元素时,可返回任意一个元素的下标)
 * lintcode 459 
 *
 */
public class ClosestNumber {
    
    public int findClosestNumber(int[] A, int target) {
        // corner case:
        if (A == null || A.length == 0) {
            return -1;
        }
        int lo = 0, hi = A.length-1;
        while (lo + 1 < hi) {
            int mid = lo + (hi - lo) / 2;
            if (A[mid] == target) {
                return mid;
            } else if (A[mid] > target) {
                hi = mid;
            } else {
                lo = mid;
            }
        }
        int diff_lo = Math.abs(A[lo] - target);
        int diff_hi = Math.abs(A[hi] - target);
        System.out.println("lo = " + lo + ", diff_lo = " + diff_lo);
        System.out.println("hi = " + hi + ", diff_hi = " + diff_hi);
        
        return diff_lo < diff_hi ? lo : hi;
    }
    
    public static void main(String[] args) {
        int[] A = new int[] {1,2,3,7};
        int target = -1;
        ClosestNumber cn = new ClosestNumber();
        System.out.println(cn.findClosestNumber(A, target)); // expected to be 3
    }

}

lintcode 458 last-position-of-target

关键在于 target==A[mid] 时的判断

package algorithm_ladder_II;

/**
 * 给一个升序数组,找到target最后一次出现的位置,如果没出现过返回-1
 * 关键在于==mid时的判断
 */
public class LastTarget {
    
    public int lastPosition(int[] A, int target) {
        // corner case:
        if (A == null || A.length == 0) {
            return -1;
        }
        int lo = 0, hi = A.length-1;
        while (lo + 1 < hi) {
            int mid = lo + (hi - lo) / 2;
            if (A[mid] > target) {
                hi = mid;
            } else {
                lo = mid;
            }
        }
        if (A[hi] == target) {
            return hi;
        } else if (A[lo] == target) {
            return lo;
        } else {
            return -1;
        }
    }
        
    public static void main(String[] args) {
        int[] A = new int[] {1,2,2,2,4};
        int target = 2;
        LastTarget cn = new LastTarget();
        System.out.println(cn.lastPosition(A, target)); // expected to be 2
    }
}

lintcode 28 Search a 2D Matrix

Use binary search twice

package algorithm_ladder_II;

public class Search2DMatrix {
    
    public boolean searchMatrix(int[][] matrix, int target) {
        // corner case:
        if (matrix == null || matrix.length == 0) {
            return false;
        }
        
        int[] rowHead = new int[matrix.length];
        for (int i = 0; i < matrix.length; i++) {
            rowHead[i] = matrix[i][0];
        }
        
        int[] searchRow = binarySearch(rowHead, target);
        if (searchRow.length == 1) {
            return true;
        }
        int loValue = matrix[searchRow[0]][0], hiValue = matrix[searchRow[1]][0];
        int theRowToSearch = -1;
        if (target == loValue || target == hiValue) return true;
        if (loValue > target) {
            return false;
        } else if (target > hiValue) {
            theRowToSearch = searchRow[1];
        } else {
            theRowToSearch = searchRow[0];
        }
        
        System.out.println("the row to search: " +theRowToSearch);
        int[] result = binarySearch(matrix[theRowToSearch], target);
        if (result.length == 1) return true;
        loValue = matrix[theRowToSearch][result[0]]; 
        hiValue = matrix[theRowToSearch][result[1]];
        if (target == loValue || target == hiValue) return true;
        return false;
    }
    
    private int[] binarySearch (int[] A, int target) {
        int lo = 0, hi = A.length -1;
        while (lo+1 < hi) {
            int mid = lo + (hi-lo) / 2;
            if (A[mid] == target) {
                return new int[] {mid};
            } else if (target > A[mid]) {
                lo = mid;
            } else {
                hi = mid;
            }
        }
        return new int[] {lo, hi};
    }
    
    public static void main(String[] args) {
        int[][] A = new int[3][4];
        A[0] = new int[] {1,2,5,7};
        A[1] = new int[] {10,11,16,20};
        A[2] = new int[] {23,30,34,50};
        int target = 3;
        Search2DMatrix sd = new Search2DMatrix();
        System.out.println(sd.searchMatrix(A, target)); // should be true

    }
}

lintcode 585 Maximum Number in Mountain Sequence

Given a mountain sequence of n integers which increase firstly and then decrease, find the mountain top.

package algorithm_ladder_II;

/**
 * Given a mountain sequence of n integers which increase firstly and then decrease, find the mountain top.
 * 区间两端点一阶导数异号
 */
public class MaxNumMountSeq {
    
    public int findMaxNum(int[] A) {
        // corner case
        if (A == null || A.length < 3) {
            return -1;
        }
        int lo = 0, hi = A.length-1;
        if (!(A[lo] < A[lo+1]) && !(A[hi-1] > A[hi])) { // not mountain sequence
            return -1;
        }
        
        while (lo+1 < hi) {
            int mid = lo + (hi-lo) / 2;
            if (A[mid] > A[mid-1] && A[mid] > A[mid+1]) {
                return mid;
            } else if (A[mid] > A[mid-1]) {
                lo = mid;
            } else {
                hi = mid;
            }
        }
        if (A[lo] > A[lo-1] && A[lo] > A[lo+1]) {
            return lo;
        } else {
            return hi;
        }
    }
    
    public static void main(String[] args) {
        int[] A = new int[] {1,2,3,4,5,9,8,4,3,1,-1};
        MaxNumMountSeq mnms = new MaxNumMountSeq();
        System.out.println(mnms.findMaxNum(A)); // expected to be 5
    }   
}

Lintcode 447

给一个按照升序排序的正整数数组。这个数组很大以至于你只能通过固定的接口 ArrayReader.get(k) 来访问第k个数。并且你也没有办法得知这个数组有多大。找到给出的整数target第一次出现的位置。你的算法需要在O(logk)的时间复杂度内完成,k为target第一次出现的位置的下标。如果找不到target,返回-1。

要点:可认为整个数组是无限长,递增的。使用乘法增加的思想探测index>target的地方。

package algorithm_ladder_II;

/**
 * LintCode 447
 * 给一个按照升序排序的正整数数组。这个数组很大以至于你只能通过固定的接口 ArrayReader.get(k) 来访问第k个数。
 * 并且你也没有办法得知这个数组有多大。找到给出的整数target第一次出现的位置。
 * 你的算法需要在O(logk)的时间复杂度内完成,k为target第一次出现的位置的下标。如果找不到target,返回-1
 *
 */
public class SearchInBigArray {
    public int findIndex(int target) {
        int i = 0;
        if (ArrayReader.get(i) == target) {
            return I;
        }
        i = 1;
        while (ArrayReader.get(i) < target) {
            i *= 2;
        }
        int lo = i/2, hi = I;
        while (lo + 1 < hi) {
            int mid = lo + (hi-lo) / 2;
            if (ArrayReader.get(mid) >= target) {
                hi = mid;
            } else {
                lo = mid;
            }
        }
        if (ArrayReader.get(lo) == target) {
            return lo;
        } else if (ArrayReader.get(hi) == target) {
            return hi;
        } return -1;
    }
}

Find Minimum in Rotated Sorted Array

假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2)。你需要找到其中最小的元素。你可以假设数组中不存在重复的元素。

要点:mid的判断

package algorithm_ladder_II;

/**
 * leetcode 153, lintcode 159
 */
public class MinRotSortArr {
    public int findMin(int[] A) {
        // corner case;
        if (A.length == 1) return A[0];
        
        int lo = 0, hi = A.length-1;
        while (lo+1 < hi) {
            int mid = lo + (hi-lo)/2;
            if (A[mid] < A[hi]) {
                hi = mid;
            } else {
                lo = mid;
            }
        }
        return Math.min(A[lo], A[hi]);
    }
    
    public static void main(String[] args) {
        int[] A = new int[] {4, 5, 6, 7, 0, 1, 2};
        MinRotSortArr mrsa = new MinRotSortArr();
        System.out.println(mrsa.findMin(A)); // expected to be 0;
    }
}

Lintcode 75 Find Peak Element

Algorithm ladder II_第1张图片
Find peak element

要点:根据mid是上升、下降、峰、谷分开判断

package algorithm_ladder_II;

/**
 * Lintcode 75
 * Find Peak Element 
 *
 */
public class PeakElem {
        public int findPeak(int[] A) {
        int lo = 0, hi = A.length-1;
        while (lo + 1 < hi) {
            int mid = lo + (hi-lo) / 2; // at least 3 elements in A
            if (A[mid] > A[mid-1] && A[mid] > A[mid+1]) {  // function shape: /\
                return mid;
            } else if (A[mid] < A[mid+1] && A[mid-1] < A[mid]) { // function shape: / /
                lo = mid;
            } else { // function shape: \/ OR \ \
                hi = mid;
            }
        }
        if (A[lo] > A[lo-1] && A[lo] > A[lo+1]) {
            return lo;
        } else {
            return hi;
        }
    }
    
    public static void main(String[] args) {
        int[] A = new int[] {1, 2, 1, 3, 4, 5, 7, 6};
        PeakElem pe = new PeakElem();
        System.out.println(pe.findPeak(A)); // should be index 1 or 6
    }
}

Lintcode 74 First bad version

要点:OOXX型找符合条件的最后一个

package algorithm_ladder_II;
/**
 * lintcode 74
 *
 */
public class FirstBadVersion {
    public int findFirstBadVersion(int n) {
        int lo = 1, hi = n;
        while (lo+1 < hi) {
                int mid = lo + (hi-lo) / 2;
                if (SVNRepo.isBadVersion(mid)) {
                    hi = mid;
                } else {
                    lo = mid;
                }
        }
        if (SVNRepo.isBadVersion(lo)) {
                return lo;
        } else {
                return hi;
        }
    }
}

leetcode 33. Search in Rotated Sorted Array

要点:多种情况的判断 (画图做题,把target区分在两个单调区间上讨论)

package algorithm_ladder_II;

public class SearchInRotSortArr {
    
    public int search(int[] A, int target) {
        // corner case
        if (A == null || A.length == 0) {
            return -1;
        }
        
        int lo = 0, hi = A.length-1;
        while (lo +1 < hi) {
            int mid = lo + (hi-lo)/2;
            if (target > A[hi]) {
                if (A[mid] == target) return mid;
                if (A[mid] < A[hi]) hi = mid;
                else if (A[mid] > target) hi = mid;
                else lo = mid;
            } else {
                if (A[mid] == target) return mid;
                if (A[mid] > A[hi]) lo = mid;
                else if (A[mid] < target) lo = mid;
                else hi = mid;
            }
        }
        
        if (A[lo] == target) return lo;
        if (A[hi] == target) return hi;
        return -1;
    }
    
    public static void main(String[] args) {
        int[] A = new int[] {4, 5, 6, 7, 0, 1, 2};
        int target = 1;
        SearchInRotSortArr srsa = new SearchInRotSortArr();
        System.out.println(srsa.search(A, target)); // should be 5
    }
}

leetcode 81. Search in Rotated Sorted Array II

重复元素导致不能根据导数来判断左右区间。

要点: No need to check two parts. We must have one part sorted while the other part rotated.
solution

public boolean search(int[] nums, int target) {
    int start  = 0, end = nums.length - 1;
    
    //check each num so we will check start == end
    //We always get a sorted part and a half part
    //we can check sorted part to decide where to go next
    while(start <= end){
        int mid = start + (end - start)/2;
        if(nums[mid] == target) return true;
        
        //if left part is sorted
        if(nums[start] < nums[mid]){
            if(target < nums[start] || target > nums[mid]){
                //target is in rotated part
                start = mid + 1;
            }else{
                end = mid - 1;
            }
        }else if(nums[start] > nums[mid]){
            //right part is rotated
            
            //target is in rotated part
            if(target < nums[mid] || target > nums[end]){
                end = mid -1;
            }else{
                start = mid + 1;
            }
        }else{
            //duplicates, we know nums[mid] != target, so nums[start] != target
            //based on current information, we can only move left pointer to skip one cell
            //thus in the worest case, we would have target: 2, and array like 11111111, then
            //the running time would be O(n)
            start ++;
        }
    }
    
    return false;
}

lintcode 600, leetcode 302 Smallest Rectangle Enclosing Black Pixels

Algorithm ladder II_第2张图片
leetcode 302

经典题, 可以逐层优化

  1. O(mn) brutal-force search
  2. DFS O(B), where B is number of black pixels. (similar to LeetCode200 number of islands)
  3. Binary search: 4 searches, searching for left, right, top, bottom bounds.
  • when searching columns (left and right), use binary search in columns O(mlogn)
  • when searching rows (top and bottom), use bs in rows O(nlogn)
package algorithm_ladder_II;

/**
 * leetcode 302
 * 4层binary search。二刷优化代码。
 */
public class SmallestRectangleEnclosingBlackPixels {
    
    private char[][] A;
    public int minArea(char[][] A, int x, int y) {
        this.A = A;
        int m = A.length; // numRows
        int n = A[0].length; // numCols
        
        int lo, hi;
        int left, right, top, bottom;
        
        // binary search for LEFT BOUND within col (0, y);
        lo = 0; hi = y;
        while (lo+1 < hi) {
            int mid = lo + (hi-lo) /2;
            if (!hasBlackPixel(mid, 0, m-1, true)) {
                lo = mid;
            } else {
                hi = mid;
            }
        }
        if (hasBlackPixel(lo, 0, m-1, true)) left = lo;
        else left = hi;
        
        // binary search for RIGHT BOUND within col (y, n);
        lo = y; hi = n-1;
        while (lo+1 < hi) {
            int mid = lo + (hi-lo) /2;
            if (hasBlackPixel(mid, 0, m-1, true)) {
                lo = mid;
            } else {
                hi = mid;
            }
        }
        if (hasBlackPixel(hi, 0, m-1, true))  right = hi;
        else right = lo;
        
        // bs for TOP BOUND within row (0, x);
        lo = 0; hi = x;
        while (lo + 1 < hi) {
            int mid = lo + (hi-lo)/2;
            if (!hasBlackPixel(mid, left, right, false)) {
                lo = mid;
            } else {
                hi = mid;
            }
        }
        if (hasBlackPixel(lo, left, right, false)) top = lo;
        else top = hi;
        
        // bs for bottom BOUND within row (x, m);
        lo = x; hi = m-1;
        while (lo+1 < hi) {
            int mid = lo + (hi-lo) /2;
            if (hasBlackPixel(mid, left, right, false)) {
                lo = mid;
            } else {
                hi = mid;
            }
        }
        if (hasBlackPixel(hi, left, right, false))  bottom = hi;
        else bottom = lo;
        
        System.out.println("left: " + left);
        System.out.println("right: " + right);
        System.out.println("top: " + top);
        System.out.println("bottom: " + bottom);
        //System.out.println(hasBlackPixel(2, 0, 3, false));

        return (right - left + 1 ) * (bottom - top + 1);
        
    }
    
    private boolean hasBlackPixel(int colOrRow, int lowerBound, int upperBound, boolean isCol) {
        if (isCol) { // search a column
            for (int i = lowerBound; i<= upperBound; i++) {
                if (A[i][colOrRow] == '1') {
                    return true;
                }
            }
            return false;
        } else { // search a row
            for (int i = lowerBound; i<= upperBound; i++) {
                if (A[colOrRow][i] == '1') {
                    return true;
                }
            }
            return false;
        }
    }
    
    public static void main(String[] args) {
        char[][] A = new char[3][4];
        A[0] = new char[] {'0','0','1','0'};
        A[1] = new char[] {'0','1','1','0'};
        A[2] = new char[] {'0','1','0','0'};
        SmallestRectangleEnclosingBlackPixels s = new SmallestRectangleEnclosingBlackPixels();
        System.out.println(s.minArea(A, 1, 2));
    }
}

lintcode 462 Total occurrence of target

要点,两次二分法,查first and last (mid的判断)。

package algorithm_ladder_II;

/**
 * Lintcode 462 
 * Given a target number and an integer array sorted in ascending order. 
 * Find the total number of occurrences of target in the array.
 *
 */
public class TotalOccuranceTarget {
    
    public int totalOccurance(int[] A, int target) {
        if (A == null || A.length == 0) {
            return 0;
        }
        int indexOfFirst = findTarget(A, target, true);
        if (indexOfFirst == -1) return 0;
        
        int indexOfLast = findTarget(A, target, false);
        return indexOfLast - indexOfFirst + 1;
    }
    
    // findFirst = true : find the first target;
    // findFirst = false : find the last target;
    // return -1 if not found;
    private int findTarget(int[] A, int target, boolean findFirst) {
        int lo = 0, hi = A.length-1;
        while (lo + 1 < hi) {
            int mid = lo + (hi-lo) / 2;
            if (target > A[mid]) {
                lo = mid;
            } else if (target < A[mid]) {
                hi = mid;
            } else {
                if (findFirst) {
                    hi = mid;
                } else {
                    lo = mid;
                }
            }
        }
        
        if (findFirst) {
            if (A[lo] == target) return lo;
            else if (A[hi] == target) return hi;
            else return -1;
        } else {
            if (A[hi] == target) return hi;
            else if (A[lo] == target) return lo;
            else return -1;
        }
            
    }
    
    public static void main(String[] args) {
        int[] A = new int[] {1,2,3,4,4,4,4,4,6,6,6,6,9,10};
        int target = 4;
        TotalOccuranceTarget tot = new TotalOccuranceTarget();
        System.out.println(tot.totalOccurance(A, target)); // should be 5
        
    }
}

LintCode 254 Drop Eggs problem

一个general的最优化解: DP (see geek4geeks有很好的dp解这题的讨论)
回归到这道题,2个鸡蛋100楼,只需要n + (n-1) + (n-2) + ...+1 > 100 see 讨论

这道题值得讨论 -- to do

  • 一个鸡蛋: brutal force
  • 无数个鸡蛋:binary search
  • n eggs, k storys: dynamic programming: f[I][j] = 1 + Max(f[i-1][j-1], f[i][k-j])

lintcode 14

search first , mid的判断。

package algorithm_ladder_II;

/**
 * Lintcode 14 easy
 */
public class FirstPositionOfTarget {
    public int binarySearch(int[] A, int target) {
        // corner case;
        if (A == null || A.length == 0) 
            return -1;
        
        int lo = 0, hi = A.length-1;
        while (lo + 1 < hi) {
            int mid = lo + (hi-lo) / 2;
            if (target > A[mid]) {
                lo = mid;
            } else if (target < A[mid]) {
                hi = mid;
            } else {
                hi = mid;
            }
        }
        if (A[lo] == target) 
            return lo;
        else if (A[hi] == target) 
            return hi;
        else 
            return -1;
    }
    
    public static void main(String[] args) {
        int[] A = new int[] {1, 2, 3, 3, 4, 5, 10};
        int target = 3;
        FirstPositionOfTarget fp = new FirstPositionOfTarget();
        System.out.println(fp.binarySearch(A, target)); // should be 2
    }
}

lintcode 460 k closest numbers in sorted array; leetcode 658 Find K Closest Elements

我自己想的办法,先找到x的左右界left/right(如果array中没有x就找小于x的最大数,和大于x的最小数); O(log N)
再在[left-k, right-k]按照与x的difference来排序找出前几个。。。再排序返回。O(klogk);

看了一下别人的答案 很巧啊

  • 先找first x (只用到lo and hi就可以了)
  • 由[lo, hi] 双指针,不断线性扩张区间。
  • Time complexity: O(logN + k);
package algorithm_ladder_II;

import java.util.LinkedList;
import java.util.List;

/**
 *  leetcode 658
 *
 */
public class FindKClosestElements {
    public List findClosestElements(int[] A, int k, int x) {
        LinkedList res = new LinkedList();
        // corner case
        if (A == null || A.length == 0)
                return res;
        
        // search for first x;
        int lo = 0, hi = A.length;
        while (lo + 1 < hi) {
                int mid = lo + (hi - lo) / 2;
                if (x > A[mid]) {
                    lo = mid;
                } else {
                    hi = mid;
                }
        }
        
        // expanding ...
        while (res.size() < k) {
                if (lo >= 0 && hi < A.length) {
                    if (Math.abs(x - A[hi]) < Math.abs(x-A[lo])) {
                        res.addLast(A[hi]);
                        hi++;
                    } else {
                        res.addFirst(A[lo]);
                        lo--;
                    }
                } else if (lo >= 0) {
                    res.addFirst(A[lo]);
                    lo--;
                } else {
                    res.addLast(A[hi]);
                    hi++;
                }
        }
        return res;  
    }
    
    public static void main(String[] args) {
        int[] A = new int[] {1,2,3,4,5};
        int k = 4;
        int x = 3;
        FindKClosestElements f = new FindKClosestElements();
        List res = f.findClosestElements(A, k, x);
        for (int i : res) {
            System.out.print(i + " "); // should be 1,2,3,4
        }
    }
}

你可能感兴趣的:(Algorithm ladder II)