- 找到某个值
- 确定左右边界
- 快速收缩搜索区间
爱吃香蕉的珂珂
思路
- 吃香蕉的速度和时间存在函数关系
- 寻找吃香蕉的最小速度
- 寻找速度的左侧边界
public long getHours(int[]piles,int x){
long hours=0;
for (int pile : piles) {
hours += pile / x;
if (pile % x > 0)
hours++;
}
return hours;
}
public int minEatingSpeed(int[] piles, int h) {
int left=1,right=1;
for(int pile:piles){
right=Math.max(right,pile);
}
while (left<=right){
int mid=left+(right-left)/2;
long hours=getHours(piles,mid);
if(hours==h){
right=mid-1;
} else if (hours<h) {
right=mid-1;
} else if (hours>h) {
left=mid+1;
}
}
return left;
}
在D天内送达包裹的能力
思路
- 运输能力和运输时间存在函数关系
- 根据函数关系由运输能力确定运输时间
- 寻找最小运输能力,寻找最左侧边界
public int getDays(int[]weights,int x){
int days=0;
for(int i=0;i<weights.length;){
int cap=x;
while (i<weights.length){
if(cap<weights[i]) break;
else cap-=weights[i];
i++;
}
days++;
}
return days;
}
public int shipWithinDays(int[] weights, int days) {
int left=0,right=0;
for(int weight:weights){
left=Math.max(left,weight);
right+=weight;
}
while (left<=right){
int mid=left+(right-left)/2;
int conveyDays=getDays(weights,mid);
if(conveyDays==days){
right=mid-1;
} else if (conveyDays<days) {
right=mid-1;
} else if (conveyDays>days) {
left=mid+1;
}
}
return left;
}
分割数组的最大值
思路
public int splitArray(int[] nums, int k) {
return shipWithinDays(nums,k);
}
搜索二维矩阵 I
思路
- 将矩阵转化成数组a[k](0<=k<=m*n-1),
- 矩阵元素matrix[i]lj]对应数组k=i*n+j(n是矩阵的列数)
- 根据矩阵元素特点,转化成数组后,按照二分法搜索target
public int getMatrixValue(int[][]matrix,int index){
int m=matrix.length,n=matrix[0].length;
int i=index/n,j=index%n;
return matrix[i][j];
}
public boolean searchMatrix(int[][] matrix, int target) {
int m=matrix.length,n=matrix[0].length;
int left=0,right=m*n-1;
while (left<=right){
int mid=left+(right-left)/2;
int matrixValue=getMatrixValue(matrix,mid);
if(matrixValue==target){
return true;
} else if (matrixValue<target) {
left=mid+1;
} else if (matrixValue>target) {
right=mid-1;
}
}
return false;
}
搜索二维矩阵 II
思路
- 从矩阵右上角开始搜索数组元素
- 如果矩阵元素比target大,则向左搜索;矩阵元素比target小,则向下搜索
public boolean searchMatrix2(int[][] matrix, int target) {
int m=matrix.length,n=matrix[0].length;
int i=0,j=n-1;
while (i<m && j>=0){
if(matrix[i][j]==target){
return true;
} else if (matrix[i][j]<target) {
i++;
} else if (matrix[i][j]>target) {
j--;
}
}
return false;
}
找到k个最接近的元素
思路
- 寻找最接近x的左侧边界p
- 从p-1和p分别向数组两头寻找
public List<Integer> findClosestElements(int[] arr, int k, int x) {
int p= leftBound(arr,x);
int left=p-1,right=p;
LinkedList<Integer>res=new LinkedList<>();
while (k>=0){
if(left==-1){
res.addLast(arr[right]);
right++;
} else if (right==arr.length) {
res.addFirst(arr[left]);
left--;
} else if (arr[right]-x<x-arr[left]) {
res.addLast(arr[right]);
right++;
} else {
res.addFirst(arr[left]);
left--;
}
k--;
}
return res;
}
寻找峰值
思路
- 采用二分搜索缩小区间,mid=left+(right-left)/2
- 如果nums[mid]>nums[mid+1],则mid或者mid左侧存在峰值,如果nums[mid]<nums[mid+1],则mid右侧存在峰值
public int findPeakElement(int[] nums) {
int left=0,right=nums.length-1;
while (left<right){
int mid=left+(right-left)/2;
if(nums[mid]>nums[mid+1]){
right=mid;
}else if(nums[mid]<nums[mid+1]){
left=mid+1;
}
}
return left;
}
山脉数组的峰顶索引
思路同上题
public int peakIndexInMountainArray(int[] arr) {
int left=0,right=arr.length-1;
while (left<right){
int mid=left+(right-left)/2;
if(arr[mid]>arr[mid+1]){
right=mid;
}else {
left=mid+1;
}
}
return left;
}
统计目标成绩的出现次数
思路
- 寻找目标成绩的左右边界索引left,right
- 返回right-left+1
public int countTarget(int[] scores, int target) {
int left = leftBound(scores,target),right = rightBound(scores,target);
return right-left+1;
}
点名
思路
- 数组元素当前值等于索引值,则left=mid+1,如果数组元素当前值大于索引值,则right=mid-1
public int takeAttendance(int[] records) {
int left=0,right=records.length-1;
while (left<=right){
int mid=left+(right-left)/2;
if(records[mid]==mid){
left=mid+1;
}else if(records[mid]>mid){
right=mid-1;
}
}
return left;
}
搜索旋转排序数组 I
思路
- 如果nums[mid]>=nums[left],说明left…mid有序,此时进入这个有序区间搜索target,如果nums[left]<=target<nums[mid],则right=mid-1,否则left=mid+1
- 如果nums[mid]<nums[left],说明mid…right有序,此时进入这个有序区间搜索target,如果nums[mid]<target<=nums[right],则left=mid+1,否则right=mid-1
public int search(int[] nums, int target) {
int left=0,right=nums.length-1;
while (left<=right){
int mid=left+(right-left)/2;
if(nums[mid]==target)
return mid;
if(nums[mid]>=nums[left]){
if(target>=nums[left] && target<nums[mid]){
right=mid-1;
}else{
left=mid+1;
}
}else {
if(target>nums[mid] && target<=nums[right]){
left=mid+1;
}else {
right=mid-1;
}
}
}
return -1;
搜索旋转排序数组 II
思路
public boolean search(int[] nums, int target) {
int left=0,right=nums.length-1;
while (left<=right){
while (left<right && nums[left]==nums[left+1]) left++;
while (left<right && nums[right]==nums[right-1]) right--;
int mid=left+(right-left)/2;
if(nums[mid]==target){
return true;
}
if(nums[mid]>=nums[left]){
if(nums[mid]>target && target>=nums[left]){
right=mid-1;
}else{
left=mid+1;
}
}else{
if(nums[mid]<target && target<=nums[right]){
left=mid+1;
}else{
right=mid-1;
}
}
}
return false;
}