457.经典二分叉中分问题:点击打开链接
注意:二分查找模板
牢记二分查找精髓,不断缩小范围,然后手动找出结果(并不是在缩小范围的时候就一定能找到结果)
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) {
if(nums.length==0||nums==null){
return -1;
}
int start=0, end =nums.length-1;
while(start+1<end){
int mid=start+(end-start)/2; //mid的定义方法,防止stackoverflow,但是一般情况下不会
if(nums[mid]==target){
return mid; //很多时候都不能直接return的
}else if(nums[mid]<target){
start=mid; //这边的边界不加1或者减1
}else{
end=mid; //同上
}
}
if(nums[start]==target){ //上面的while主体里不断缩小区间,直到只剩外面两种情况可判断
return start;
}
if(nums[end]==target){
return end;
}
return -1;
}
}
458.Last Position of Target:点击打开链接
注意:像这样找第一个或者最后一个的情况,两方面需要注意:
1. while里的if,else三种条件不合并成两种,而且等于target的情况要根据实际情况看是start=mid还是end=mid。
2. while出来之后,当条件减缩到可以从几个值中找出结果时,要根据实际情况看是先找end,还是先找start
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) {
if(nums==null || nums.length==0){
return -1;
}
int start=0,end=nums.length-1;
while(start+1<end){
int mid=start+(end-start)/2;
if(nums[mid]==target){ //找到一个target,不一定是最后位置的target,因此还要向后找,所以切掉前面的
start=mid; //不是直接return的
}else if(nums[mid]<target){
start=mid;
}else{
end=mid;
}
}
if(nums[end]==target){ //出来只剩start和end的时候,由于是要目标最后元素,所以先看end是否符合
return end; //如果end符合条件就直接return结果了
}
if(nums[start]==target){
return start;
}
return -1;
}
}
459.Closet 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) {
if(A==null || A.length==0){
return -1;
}
int start=0,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;
}else if(A[end]==target){
return end;
}else if(target-A[start]<=A[end]-target){
return start;
}else{
return end;
}
}
}
K Closet Numbers in Sorted Array
思路:二分法先找到start和end两点
再start向左和end向右找其他符合条件的元素
要注意start向左出左边界和end向右出右边界的问题
分析:为什么start向左走,end向右走?
因为在第一步微分法找start和end时,一般找到的start和end是相邻的两点,因为都在不断向target靠近,所以如果start向右,end向左就会重复判断点
public class Solution {
/*
* @param A: an integer array
* @param target: An integer
* @param k: An integer
* @return: an integer array
*/
public int[] kClosestNumbers(int[] A, int target, int k) {
if (A == null || A.length < k || k <= 0) {
return new int[0];
}
int[] result=new int[k];
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;
}
}
for (int i=0;i<k;i++) {
int startDiff=(start < 0) ? Integer.MAX_VALUE : Math.abs(A[start]-target);
int endDiff=(end >= A.length) ? Integer.MAX_VALUE : Math.abs(A[end]-target);
if(startDiff<=endDiff) { //这边的等于符号也很重要,因为题目要求最后的输出顺序如果diff等的话按小到大
result[i]=A[start];
start--;
}else{
result[i]=A[end];
end++;
}
}
return result;
}
}
74.First Bad Version:点击打开链接
/**
* public class SVNRepo {
* public static boolean isBadVersion(int k);
* }
* you can use SVNRepo.isBadVersion(k) to judge whether
* the kth code version is bad or not.
*/
class Solution {
/**
* @param n: An integers.
* @return: An integer which is the first bad version.
*/
public int findFirstBadVersion(int n) {
int start=0,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;
}
return end;
}
}
447.Search in a Big Sorted Array:点击打开链接
注意:确定大边界的时候是先确定再进行后边的找target,而不是边找target边确定,这样做相对来讲省事很多,不用边找边判断。
/**
* Definition of ArrayReader:
*
* class ArrayReader {
* // get the number at index, return -1 if index is less than zero.
* public int get(int index);
* }
*/
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) {
int index=0;
while(reader.get(index)<target){ //数组长度不断乘2扩展的想法,来确定一个右边界
index=2*index+1; //先看第0个位置是不是大于target,再看第1个,2,4,8...
}
int start=0, end=index;
while(start+1<end){
int mid=start+(end-start)/2;
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;
}
}
Follow up Links: http://www.cnblogs.com/grandyang/p/4325840.html
159.Find Minimum in Rotated Sorted Array:点击打开链接
思路:关于rotated sorted array题目都分成坐标轴二,四象限两条单调递增直线的想法加理解
public class Solution { //方法一
/**
* @param nums: a rotated sorted array
* @return: the minimum number in the array
*/
public int findMin(int[] nums) {
if(nums==null || nums.length==0){
return -1;
}
int start=0;
int end=nums.length-1;
int target=nums[nums.length-1]; //先将最后一个看做target,找到第一个小于target的元素就是所求
while(start+1<end){
int mid=start+(end-start)/2;
if(nums[mid]<target){
end=mid;
}else{
start=mid;
}
}
if(nums[start]<target){ //由于是要找第一个小于target的元素,因此先判断start
return nums[start];
}
return nums[end];
}
}
public class Solution { //方法二
/**
* @param nums: a rotated sorted array
* @return: the minimum number in the array
*/
public int findMin(int[] nums) {
if(nums == null || nums.length == 0) {
return -1;
}
int start = 0, end = nums.length - 1;
int target = nums[nums.length - 1]; //先将最后一个看做target,找到第一个小于target的元素就是所求
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];
}
}
}
160. Find Minimum in Rotated Sorted Array II:点击打开链接
思路:关于这题的考点九章给的解释特别好,这题考的就是能不能想到最坏情况,
[1,1,1,1...1]里有一个0,时间复杂度必须是O(n),因此写一个for循环就好
但是也并不是所有的情况都是坏情况,也可以用一个伪二分来实现,但这题考的不是会不会二分法,而是想不想得到最坏情况
public class Solution {
/**
* @param num: a rotated sorted array
* @return: the minimum number in the array
*/
public int findMin(int[] num) { //最坏情况for循环
if(num==null || num.length==0){
return -1;
}
int min=Integer.MAX_VALUE;
for(Integer n:num){
min=Math.min(min,n);
}
return min;
}
}
public class Solution {
/**
* @param num: a rotated sorted array
* @return: the minimum number in the array
*/
public int findMin(int[] num) { //伪二分
if(num==null || num.length==0){
return -1;
}
int start=0;
int end=num.length-1;
while(start+1<end){
int mid=start+(end-start)/2;
if(num[mid]==num[end]){ //去除num[end]不影响,因为num[mid]和num[end]值相等
end--;
}else if(num[mid]<num[end]){ //num[mid]<num[end],说明最小值在前面部分,最大可能是num[mid]
end=mid;
}else{ //如果num[mid]>num[end],说明num[mid]的前面也比num[end]大
start=mid; //因此要找小值还是要向后面部分找
}
}
if(num[start]<=num[end]){
return num[start];
}else{
return num[end];
}
}
}
62.Search in Rotated Sorted Array:点击打开链接
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) {
if(A==null || A.length==0){
return -1;
}
int start=0,end=A.length-1;
while(start+1<end){
int mid=start+(end-start)/2;
if(A[mid]==target){
return mid;
}
if(A[start]<A[mid]){ //这里有两个要点,如果mid的位置在红线的情况
if(A[start]<=target && target<=A[mid]){ //内部if里条件写单调递增好讨论的情况,另一种else
end=mid;
}else{
start=mid;
}
}else{ //还有就是如果mid的位置在绿线的情况
if(A[mid]<=target && target<=A[end]){ //内部if同上
start=mid;
}else{
end=mid;
}
}
}
if(A[start]==target){
return start;
}
if(A[end]==target){
return end;
}
return -1;
}
}
585.Maximum Number in Mountain Sequence:点击打开链接
思路:此题不论是在红线还是绿线位置,都是要考虑mid+1与mid的大小关系就足以定位
但是如果是一个先升后降的数组,就要考虑mid的位置在红线还是绿线
注意:本题而言,不会有出现nums[mid+1]越界的情况,可以用[1,3,2]简单数组举例。
public class Solution {
/**
* @param nums a mountain sequence which increase firstly and then decrease
* @return then mountain top
*/
public int mountainSequence(int[] nums) {
int start=0, end=nums.length-1;
while(start+1<end){
int mid=start+(end-start)/2;
if(nums[mid]<nums[mid+1]){
start=mid;
}else{
end=mid;
}
}
if(nums[start]<nums[end]){
return nums[end];
}else{
return nums[start];
}
}
}
75.Find Peak Element:点击打开链接
思路:这题就是看mid的位置是在峰,谷,上升的半山腰,还是下降的半山腰上
public class Solution {
/**
* @param A: An integers array.
* @return: return any of peek positions.
*/
public int findPeak(int[] A) {
int start=0, end=A.length-1;
while(start+1<end ){
int mid=start+(end-start)/2;
if(A[mid]>A[mid-1] && A[mid]>A[mid+1]){ //在峰直接return
return mid;
}else 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]){ //最后只剩A[start]和A[end],哪个大哪个就是peak
return end;
}else{
return start;
}
}
}
600. Smallest Rectangle Enclosing Black Pixels:点击打开链接
[ "0
01
0", "0
11
0", "0
10
0" ]
思路:找到上下左右的边界
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) {
if (image == null || image.length == 0) {
return 0;
}
int n = image.length;
int m = image[0].length;
int left = findLeft(image, 0, y);
int right = findRight(image, y, m - 1);
int top = findTop(image, 0, x);
int bottom = findBottom(image, x, n - 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; //二叉中分0~y
if (isEmptyColumn(image, mid)) { //当mid选定后,调用isEmptyColumn判断整个mid列是否都为0
start = mid; //如果mid所在的整列都为0,说明左边界还可以往右找
} else { //如果mid所在的整列有1,说明左边界还要往左找
end = mid;
}
}
if (isEmptyColumn(image, start)) { //直到范围缩减到只剩下start和end
return end; //如果start所在整列都为0,说明左边界是end所在列
} //否则左边界就是start所在列
return start;
}
private int findRight(char[][] image, int start, int end) {
while (start + 1 < end) {
int mid = start + (end - start) / 2;
if (isEmptyColumn(image, mid)) {
end = mid;
} else {
start = mid;
}
}
if (isEmptyColumn(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 isEmptyColumn(char[][] image, int col) {
for (int i = 0; i < image.length; i++) {
if (image[i][col] == '1') {
return false;
}
}
return true;
}
private boolean isEmptyRow(char[][] image, int row) {
for (int j = 0; j < image[0].length; j++) {
if (image[row][j] == '1') {
return false;
}
}
return true;
}
}
Total Occurence of Target
思路:一次二分,一次找左边界,一次找右边界,如果找左边界的时候没有就直接return 0了
public class Solution {
/*
* @param A: A an integer array sorted in ascending order
* @param target: An integer
* @return: An integer
*/
public int totalOccurrence(int[] A, int target) {
if(A==null || A.length==0){
return 0;
}
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;
}
}
int left;
if(A[start]==target){
left=start;
}else if(A[end]==target){
left=end;
}else{
return 0;
}
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;
}
}
int right;
if(A[end]==target){
right=end;
}else if(A[start]==target){
right=start;
}else{
return 0;
}
return right-left+1;
}
}
287. Find the Duplicate Number
class Solution {
public int findDuplicate(int[] nums) {
//[2,5,4,3,2,2,2], 可以极端假设有好几个重复,好理解, mid是3
int start = 0;
int end = nums.length - 1;
while(start + 1 < end)
{
int mid = start + (end - start)/2;
int nTotal = count(mid, nums);
if(nTotal <= mid)
{
start = mid;
}
else{ //数了一圈发现,比3小的个数要比3多,前面一定有重复
end = mid; //因为如果按每个数字都出现一次的话,比3小的个数应该最多是3
}
}
if(count(start, nums) <= start)
{
return end;
}
return start;
}
private int count(int mid, int[] nums)
{
int nTotal = 0;
for(int i = 0; i < nums.length; i++)
{
if(nums[i] <= mid)
{
nTotal++;
}
}
return nTotal;
}
}
Note: 这是一道非常考验二分功底的题目, 挪动的mid是时间的index