一、二分法基本功
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
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; } }
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; } }
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; } }
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; } }
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; } }
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]; } } }
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; } }
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; } }
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; } }
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]; } }
三、二分位置
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; } } }
***两次二分,第一次由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; } }
四、二分答案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; } }
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; } }
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; } }
--联想阿里编程题待续: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; } }
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; } }
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; } }