二.二分法总结

一、二分法基本功

1、通用二分模板,二分T(n)= log2n

伪码:异常检查;start + 1 < end ;start + (end - start) / 2 ;A[mid] ==, <, >; A[start] A[end] ? target

start= 0, end = length - 1

while (start + 1 < end) {//1、循环条件相邻就退出

  mid = start + (end - start) // 2、mid的取值不会越界

  if [mid] == target {return mid or 赋值}

  else if [mid] <target {start = mid}

  else [mid] > target{end = mid}

}
//3、端点检测
if [start] ,[end]?= target {return start or end}

return - 1
View Code

2、题解

1)经典(http://www.lintcode.com/problem/classical-binary-search/):在一个排序数组中找一个数,返回该数出现的任意位置,如果不存在,返回-1

做过3次,已ac

public class Solution {
    /**
     * @param nums: An integer array sorted in ascending order
     * @param target: An integer
     * @return an integer
     */
    public int findPosition(int[] nums, int target) {
        // Write your code here
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (nums[start] == target) {
            return start;
        }
        if (nums[end] == target) {
            return end;
        }
        return -1;
    }
}
View Code

2)第一次出现位置(http://www.lintcode.com/problem/first-position-of-target/):给定一个排序的整数数组(升序)和一个要查找的整数target,用O(logn)的时间查找到target第一次出现的下标(从0开始),如果target不存在于数组中,返回-1

做过3次,not ac :忘记end 的检查,第4次ac

class Solution {
    /**
     * @param nums: The integer array.
     * @param target: Target to find.
     * @return: The first position of target. Position starts from 0.
     */
    public int binarySearch(int[] nums, int target) {
        //以start做返回结果
        //1、异常检查
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        //2、循环条件
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                end = mid;
                //找第一个,返回start
            } else if (nums[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        // 3、端点检查,先看start
        if (nums[start] == target) {
            return start;
        }
        if (nums[end] == target) {
            return end;
        }
        return -1;
    }
}
View Code

3)最后一个位置(http://www.lintcode.com/problem/last-position-of-target/):给一个升序数组,找到target最后一次出现的位置,如果没出现过返回-1

3遍,ac

public class Solution {
    /**
     * @param nums: An integer array sorted in ascending order
     * @param target: An integer
     * @return an integer
     */
    public int lastPosition(int[] nums, int target) {
        // Write your code here
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                start = mid;
            } else if (nums[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        //先返回end
        if (nums[end] == target) {
            return end;
        }
        if (nums[start] == target) {
            return start;
        }
        return -1;
    }
}
View Code

 2017-03-06,11:43:29 

待续.....(接下来的题)

二、二分位置

二分位置 之 OOXX :一般会给你一个数组 让你找数组中第一个/最后一个满足某个条件的位置 OOOOOOO...OOXX….XXXXXX

1)First Bad Version(http://www.lintcode.com/problem/first-bad-version/):代码库的版本号是从 1 到 n 的整数。某一天,有人提交了错误版本的代码,因此造成自身及之后版本的代码在单元测试中均出错。请找出第一个错误的版本号。

4,ac

class Solution {
    /**
     * @param n: An integers.
     * @return: An integer which is the first bad version.
     */
    public int findFirstBadVersion(int n) {
        // write your code here
        int start = 1;
        int end = n;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (SVNRepo.isBadVersion(mid)) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (SVNRepo.isBadVersion(start)) {
            return start;
        }
        if (SVNRepo.isBadVersion(end)) {
            return end;
        }
        return -1;
    }
}
View Code

2)在大数组中查找(http://www.lintcode.com/zh-cn/problem/search-in-a-big-sorted-array/):给一个按照升序排序的正整数数组。这个数组很大以至于你只能通过固定的接口 ArrayReader.get(k) 来访问第k个数。(或者C++里是ArrayReader->get(k)),并且你也没有办法得知这个数组有多大。找到给出的整数target第一次出现的位置。你的算法需要在O(logk)的时间复杂度内完成,k为target第一次出现的位置的下标。

**边界倍增。让要二分的区间倍增。

1.找到一个大于targetS的位置k,用logk
2.在区间k内二分查找,logk

2,not ac:根据参数reader去调用函数reader.get(k)

第3次,not ac:忘记异常检查,当== 时判断失误,不应直接返回,而应该end = mid;

public class Solution {
    /**
     * @param reader: An instance of ArrayReader.
     * @param target: An integer
     * @return : An integer which is the index of the target number
     */
    public int searchBigSortedArray(ArrayReader reader, int target) {
        // write your code here
        //边界倍增
        /**
         *1.找到一个大于targetS的位置k,logk
         *2.在区间k内二分查找,logk
        */
        int index = 1;
        while (reader.get(index - 1) < target) {
            index = index * 2;
        }
        int start = 0, end = index - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (reader.get(mid) == target) {
                end = mid;
            } else if (reader.get(mid) < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (reader.get(start) == target) {
            return start;
        }
        if (reader.get(end) == target) {
            return end;
        }
        return -1;
    }
}
View Code

3)寻找旋转排序数组中的最小值(http://www.lintcode.com/zh-cn/problem/find-minimum-in-rotated-sorted-array/):假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2)。你需要找到其中最小的元素。你可以假设数组中不存在重复的元素。

3,ac:

public class Solution {
    /**
     * @param nums: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] nums) {
        // write your code here
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        int target = nums[nums.length - 1];
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (nums[mid] <= target) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (nums[start] <= target) {
            return nums[start];
        } else {
            return nums[end];
        }
    }
}
View Code

4)Search a 2D Matrix(http://www.lintcode.com/en/problem/search-a-2d-matrix/):二维数组,每行从左到右递增,从上到下递增。

** :数组坐标转换num = matrix[mid / col][mid % col];

3,看,ac

4,变量名错,方法忘记啦,不用写两遍条件

public class Solution {
    /**
     * @param matrix, a list of lists of integers
     * @param target, an integer
     * @return a boolean, indicate whether matrix contains target
     */
    public boolean searchMatrix(int[][] matrix, int target) {
        // write your code here
        //异常检测
        if (matrix == null || matrix.length == 0) {
            return false;
        }
        if (matrix[0] == null || matrix[0].length == 0) {
            return false;
        }
        int row = matrix.length;
        int col = matrix[0].length;
        int start = 0;
        int end = row * col - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            int num = matrix[mid / col][mid % col];
            if (num == target) {
                return true;
            } else if (num < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (matrix[start / col][start % col] == target) {
            return true;
        }
        if (matrix[end / col][end % col] == target) {
            return true;
        }
        return false;
    }
}
View Code

related:  Search in a 2D Matrix II(http://www.lintcode.com/zh-cn/problem/search-a-2d-matrix-ii/):

写出一个高效的算法来搜索m×n矩阵中的值,返回这个值出现的次数。

这个矩阵具有以下特性:

  • 每行中的整数从左到右是排序的。
  • 每一列的整数从上到下是排序的。
  • 在每一行或每一列中没有重复的整数。

3,ac:ac 但不太熟练啦 

**从左下-->右上,两次异常检查

public class Solution {
    /**
     * @param matrix: A list of lists of integers
     * @param: A number you want to search in the matrix
     * @return: An integer indicate the occurrence of target in the given matrix
     */
    public int searchMatrix(int[][] matrix, int target) {
        // 从左下直到右上
        //异常检查
        if (matrix == null || matrix.length == 0) {
            return 0;
        }
        if (matrix[0] == null || matrix[0].length == 0) {
            return 0;
        }
        int m = matrix.length;
        int n = matrix[0].length;
        int x = m - 1;
        int y = 0;
        int end = 0;
        int count = 0;
        while (x >= 0 && y < n) {
            if (matrix[x][y] < target) {
                y++;
            } else if (matrix[x][y] > target) {
                x--;
            } else {
                count++;
                y++;
                x--;
            }
        }
        return count;
    }
}
View Code

5)Search for a Range(http://www.lintcode.com/en/problem/search-for-a-range/):target存在的区间

2,代刷,ac,忘记思路

**先找其中一个边界,然后找另一个边界,边界之差+1即为所求。

public class Solution {
    /** 
     *@param A : an integer sorted array
     *@param target :  an integer to be inserted
     *return : a list of length 2, [index1, index2]
     */
    public int[] searchRange(int[] A, int target) {
        // write your code here
        //先找其中一个边界,然后找另一个边界
        if (A == null || A.length == 0) {
            return new int[]{-1,-1};
        }
        //不会声明一个数组 
        int[] bound = new int[2];
        int start = 0;
        int end = A.length - 1;
        //左边界
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (A[mid] < target) {
                start = mid;
            } else if (A[mid] > target) {
                end = mid;
            } else {
                end = mid;
            }
        }
        if (A[start] == target) {
            bound[0] = start;
        } else if (A[end] == target) {
            bound[0] = end;
        } else {
            bound[0] = bound[1] = -1;
        }
        //右边界
        start = 0;
        end = A.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (A[mid] < target) {
                start = mid;
            } else if (A[mid] > target) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (A[end] == target) {
            bound[1] = end;
        } else if (A[start] == target) {
            bound[1] = start;
        } else {
            bound[0] = bound[1] = -1;
        }
        return bound;
    }   
}
View Code

6)Maximum Number in Mountain Sequence • http://www.lintcode.com/en/problem/maximum-number-in-mountain-sequence/:寻找最大峰值

3,ac,代刷,四分之一区间的错了,t2 = end - (end - t1) / 2,会越界,没加1或减

两种方法:距离l/4的比较或相邻比较

public class Solution {
    /**
     * @param nums a mountain sequence which increase firstly and then decrease
     * @return then mountain top
     */
    public int mountainSequence(int[] nums) {
        //Write your code here
        int start = 0;
        int end = nums.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (nums[mid] < nums[mid + 1]) {
                start = mid;
            } else if (nums[mid] > nums[mid + 1]) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (nums[start] < nums[end]) {
            return nums[end];
        } else {
            return nums[start];
        }
        // int left = 0, right = nums.length - 1;
        // while (left + 1 < right) {
        //     int m1 = left + (right - left) / 2;
        //     int m2 = right - (right - m1) / 2;
        //     if (nums[m1] < nums[m2]) {
        //         left = m1 + 1;
        //     } else if (nums[m1] > nums[m2]) {
        //         right = m2 - 1;
        //     } else {
        //         left = m1;
        //         right = m2;
        //     }
        // }
        // return nums[left] > nums[right] ? nums[left] : nums[right];
    }
}
View Code

三、二分位置

 Half half 并无法找到一个条件,形成 OOXX 的模型 但可以根据判断,保留下有解的那一半或者去掉无解的一半

1)Find Peak Element (http://www.lintcode.com/problem/find-peak-element/):

你给出一个整数数组(size为n),其具有以下特点:

  • 相邻位置的数字是不同的
  • A[0] < A[1] 并且 A[n - 2] > A[n - 1]

假定P是峰值的位置则满足A[P] > A[P-1]A[P] > A[P+1],返回数组中任意一个峰值的位置。

class Solution {
    /**
     * @param A: An integers array.
     * @return: return any of peek positions.
     */
    public int findPeak(int[] A) {
        // write your code here
        //第一个和最后一个的位置是一定不符合的。
        int start = 1;
        int end = A.length - 2;
        while (start + 1 < end) {
            int mid = (start + end ) / 2;
            if (A[mid] > A[mid - 1]) {
                start = mid;
            } else if (A[mid] > A[mid + 1]) {
                end = mid;
            } else {
                end = mid;
            }
        }
        // 边界检测
        if (A[start] < A[end]) {
            return end;
        } else {
            return start;
        }
    }
}    
View Code

  ***两次二分,第一次由mid决定二分分成红和绿,第二次分别在红绿中分1,2和3,4,由target决定

public class Solution {
    /** 
     *@param A : an integer rotated sorted array
     *@param target :  an integer to be searched
     *return : an integer
     */
    public int search(int[] A, int target) {
        // write your code here
        if (A == null || A.length == 0) {
            return -1;
        }
        int start = 0;
        int end = A.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (A[mid] == target) {
                return mid;
            } else if (A[start] <= A[mid]) {//**红段
                if (A[start] <= target && target <= A[mid]){//红的上边1
                    end = mid;
                } else {//红的下边2
                    start = mid;
                }
            } else {//绿段
                if (A[mid] <= target && target <= A[end]) {//绿的下边3
                    start = mid;
                } else {//绿的上边4
                    end = mid;
                }
            }
        }
        if (A[start] == target) {
            return start;
        }
        if (A[end] == target) {
            return end;
        }
        return -1;
    }
}
View Code

四、二分答案Binary Search on Result

不是数组让你二分, 而且题目压根看不出来是个二分法可以做的题 同样是找到满足某个条件的最大或者最小值

1)x的平方根(http://www.lintcode.com/zh-cn/problem/sqrtx/):实现 int sqrt(int x) 函数,计算并返回 x 的平方根。

tip:Last number that number^2 <= x

1,ac

class Solution {
    /**
     * @param x: An integer
     * @return: The sqrt of x
     */
    public int sqrt(int x) {
        // write your code here
        if (x < 0) {
            return -1;
        }
        if (x <= 1) {
            return x;
        }
        long start = 1, end = x;
        while (start + 1 < end) {
            long mid = start + (end - start) / 2;
            if (mid * mid <= x) {
                start = mid;
            } else {
                end = mid;
            }
        }
        return (int) start;
    }
}
View Code

2)Wood Cut --http://www.lintcode.com/problem/wood-cut/

** Last/Biggest length that can get >= k pieces

**拿最长的去切;所有答案可能在【start,end】之间,所得的切割答案mid,进行二分;如果所得的段数比k小,mid太长了,否则,mid太小,start=mid。

1,看,待二刷

public class Solution {
    /** 
     *@param L: Given n pieces of wood with length L[i]
     *@param k: An integer
     *return: The maximum length of the small pieces.
     */
    public int woodCut(int[] L, int k) {
        // write your code here
        //L中最大长度的确定
        int max = 0;
        for (int i = 0; i < L.length; i++) {
            max = Math.max(L[i], max);
        }
        // 二分,找到所得段最长的长度,使得能切的段数多于k,
        //start,mid,end 表示每段长度
        int start = 1;
        int end = max;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            //如果得到的段数比k大,就把每段长度增加
            if (count(L, mid) >= k) {
                start = mid;
            } else {
                //如果得到的段数比k小,就把每段长度减少
                end = mid;
            }
        }
        //边界检测
        if (count(L, start) >= k) {
            return start;
        }
        if (count(L, end) >= k) {
            return end;
        }
        return 0;
    }
    //以每段length长切割,可得到多少块
    public int count(int[] L, int length) {
        int sum = 0;
        for (int i = 0; i < L.length; i++) {
            sum += L[i] / length;
        }
        return  sum;
    }
}
View Code

3)Copy Books-- http://www.lintcode.com/en/problem/copy-books/ 题意:将数组切分为k个子数组,让数组和最大的最小

tip:将数组切分为k个子数组,让数组和最大的最小

 O(n log m)
public class Solution {
    /**
     * @param pages: an array of integers
     * @param k: an integer
     * @return: an integer
     */
    public int copyBooks(int[] pages, int k) {
        //抄书的用时,主要看一个人最重负担的页数,该页数越小,用时越短
        // 让最重负担sum(最大子数组和),最小
        //最重负担sum的值范围【max,total】
        if (pages.length == 0) {
            return 0;
        }
        int max = pages[0];
        int total = 0;
        for (int i = 0; i < pages.length; i++) {
            total += pages[i];
            if (max < pages[i]) {
                max = pages[i];
            }
        }
        int start = max;
        int end = total;
        while(start + 1 < end) {
            //mid 为最重负担
            int mid = start + (end - start) / 2;
            if (countCopiers(pages, mid) > k) {//抄书人多余k,需要加重抄书负担
                start = mid;
            } else {//否则减轻工作量
                end = mid;
            }
        }
        if (countCopiers(pages, start) <= k) {
            return start;
        }
        return end;
    }
    //limit为一个人最大的工作量,在当前工作量下,需要几个抄书人
    private int countCopiers(int[] pages, int limit) {
        //sum 为 1个人的工作量,当工作量>limit时,需要另加一人抄书
        int sum = pages[0];
        //limit下,需要1人
        int copiers = 1;
        for (int i = 1; i < pages.length; i++) {
            if (sum + pages[i] > limit) {//保证当前工作量都不能多余limit,加人
                copiers++;
                sum = 0;//清空sum,继续计算下个人的工作量
            }
            sum += pages[i]; //sum计算1个人的工作量
        }
        return copiers;
    }
}
View Code

--联想阿里编程题待续:https://bbs.byr.cn/#!article/Java/55292

待刷:related如下:




2017-03-0722:23:49

搜索插入位置:http://www.lintcode.com/zh-cn/problem/search-insert-position/

Binary Search: 、

• http://www.lintcode.com/problem/search-insert-position/

• http://www.lintcode.com/problem/count-of-smaller-number/

• http://www.lintcode.com/problem/search-for-a-range/

Rotate Array

•http://www.lintcode.com/problem/recover-rotated-sorted-array/

• http://www.lintcode.com/problem/rotate-string/

• 三步翻转法: • [4,5,1,2,3] → [5,4,1,2,3] → [5,4,3,2,1] → [1,2,3,4,5]

排序数组中最接近元素:http://www.lintcode.com/zh-cn/problem/closest-number-in-sorted-array/#

public class Solution {
    /**
     * @param A an integer array sorted in ascending order
     * @param target an integer
     * @return an integer
     */
    public int closestNumber(int[] A, int target) {
        // Write your code here
        if (A == null || A.length == 0) {
            return -1;
        }
        int index = firstIndex(A, target);
        if (index == 0) {
            return 0;
        }
        if (index == A.length - 1) {
            return index;
        }
        if (target - A[index -1] > A[index] - target) {
            return index;
        } else {
            return index - 1;
        }
    }
    
    public int firstIndex(int [] A, int target) {
        int start = 0;
        int end = A.length - 1;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (A[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (A[start] >= target) {
            return start;
        }
        if (A[end] >= target) {
            return end;
        }
        return A.length - 1; 
    }
}
View Code
public class Solution {
    /**
     * @param A an integer array sorted in ascending order
     * @param target an integer
     * @return an integer
     */
    public int closestNumber(int[] A, int target) {
        // Write your code here
        int start = 0;
        int end = A.length;
        int min = Integer.MAX_VALUE;
        while(start + 1 < end) {
            int mid = start + (end - start) / 2;
            int dis = 0;
            dis = Math.abs(A[mid] - target);
            if (A[mid] == target) {
                end = mid;
            } else if (A[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
            if (dis < min) {
                min = dis;
            }
        
        }
        if (Math.abs(A[start]- target) <= min ) {
            return start;
        } 
        if (Math.abs(A[end]- target) <= min) {
            return end;
        }
        return A.length - 1;
    }
}
View Code myown

Smallest Rectangle Enclosing Black Pixels :http://www.lintcode.com/zh-cn/problem/smallest-rectangle-enclosing-black-pixels/

public class Solution {
    /**
     * @param image a binary matrix with '0' and '1'
     * @param x, y the location of one of the black pixels
     * @return an integer
     */
    public int minArea(char[][] image, int x, int y) {
        // Write your code here
        if (image == null || image.length == 0 || image[0].length == 0) {
            return 0;
        }
        int m = image.length;
        int n = image[0].length;
        int left = findLeft(image, 0, y);
        int right = findRight(image,y,n - 1);
        int top = findTop(image, 0, x);
        int bottom = findBottom(image, x, m - 1);
        return (right - left + 1) * (bottom - top + 1);
    }
    private int findLeft(char[][] image, int start, int end){
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (isEmptyCol(image, mid)) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (isEmptyCol(image, start)) {
            return end;
        }
        return start;
    }
    private int findRight(char[][] image, int start, int end){
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (isEmptyCol(image, mid)) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (isEmptyCol(image, end)) {
            return start;
        }
        return end;
    }
    private int findTop(char[][] image, int start, int end){
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (isEmptyRow(image, mid)) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (isEmptyRow(image, start)) {
            return end;
        }
        return start;
    }
    private int findBottom(char[][] image, int start, int end){
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (isEmptyRow(image, mid)) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (isEmptyRow(image, end)) {
            return start;
        }
        return end;
    }
    private boolean isEmptyCol(char[][] image, int col){
        for (int i = 0; i <= image.length - 1; i++) {
            if (image[i][col] == '1') {
                return false;
            }
        }
        return true;
    }
    private boolean isEmptyRow(char[][] image, int com){
        for (int i = 0; i <= image[0].length - 1; i++) {
            if (image[com][i] == '1') {
                return false;
            }
        }
        return true;
    }
}
View Code
原文地址:https://www.cnblogs.com/lingli-meng/p/6495815.html