classSolution{
publicintsearch(int[] nums,int target){
int left =0, right = nums.length -1;// 区间为 [left, right]while(left <= right){
int mid = left +(right - left)/2;if(nums[mid]== target){
return mid;}elseif(nums[mid]> target){
right = mid -1;}else{
left = mid +1;}}return-1;}}
classSolution{
publicintsearch(int[] nums,int target){
// 寻找左侧边界的二分查找。即:如果数组为:[1,2,2,2,3],找 2,那么找的是最左边下标的 2int left =0, right = nums.length -1;// 区间为 [left, right]while(left <= right){
int mid = left +(right - left)/2;if(nums[mid]== target){
right = mid -1;}elseif(nums[mid]> target){
right = mid -1;}else{
left = mid +1;}}// 检查出界的情况if(left >= nums.length || nums[left]!= target){
return-1;}return left;}}
classSolution{
publicintsearch(int[] nums,int target){
// 寻找左侧边界的二分查找。即:如果数组为:[1,2,2,2,3],找 2,那么找的是最左边下标的 2int left =0, right = nums.length;// 区间为 [left, right)while(left < right){
int mid = left +(right - left)/2;if(nums[mid]== target){
right = mid;}elseif(nums[mid]> target){
right = mid;}else{
left = mid +1;}}// 检查出界的情况if(left >= nums.length || nums[left]!= target){
return-1;}return left;// return right; 因为 while 的终止条件是 left == right,所以 left 和 right 是一样的}}
寻找右侧边界的二分查找
classSolution{
publicintsearch(int[] nums,int target){
// 寻找右侧边界的二分查找。即:如果数组为:[1,2,2,2,3],找 2,那么找的是最右边下标的 2int left =0, right = nums.length -1;// 区间为 [left, right]while(left <= right){
int mid = left +(right - left)/2;if(nums[mid]== target){
left = mid +1;}elseif(nums[mid]> target){
right = mid -1;}else{
left = mid +1;}}// 检查出界的情况if(right <0|| nums[right]!= target){
return-1;}return right;}}
classSolution{
publicintsearch(int[] nums,int target){
// 寻找右侧边界的二分查找。即:如果数组为:[1,2,2,2,3],找 2,那么找的是最右边下标的 2int left =0, right = nums.length;// 区间为 [left, right)while(left < right){
int mid = left +(right - left)/2;if(nums[mid]== target){
left = mid +1;}elseif(nums[mid]> target){
right = mid;}else{
left = mid +1;}}// 检查出界的情况if(right -1<0|| nums[right -1]!= target){
return-1;}return right -1;// return left - 1; 因为 while 的终止条件是 left == right,所以 left 和 right 是一样的}}
69. Sqrt(x)
classSolution{
publicintmySqrt(int x){
int left =0, right = x;while(left <= right){
int mid = left +(right - left)/2;if((long)mid * mid == x){
return mid;}elseif((long)mid * mid > x){
right = mid -1;}else{
left = mid +1;}}return right;}}
classSolution{
publicintfindDuplicate(int[] nums){
int n = nums.length;int left =1, right = n -1;while(left < right){
int mid = left +(right - left)/2;int cnt =0;// 统计原始数组中小于等于 mid 的元素的个数 cntfor(int num : nums){
if(num <= mid){
cnt++;}}if(cnt > mid){
right = mid;}else{
left = mid +1;}}return left;}}
875. 爱吃香蕉的珂珂
二分法:
classSolution{
publicintminEatingSpeed(int[] piles,int h){
int max =Integer.MIN_VALUE;for(int i =0; i < piles.length; i++){
max =Math.max(max, piles[i]);}// 寻找左侧边界的二分搜索。如果以当前的速度可以吃完香蕉,还需要继续往左找,因为左边可能还有以更小的速度也可以吃完香蕉int left =1, right = max;while(left <= right){
int mid = left +(right - left)/2;if(canFinish(piles, mid, h)){
right = mid -1;}else{
left = mid +1;}}return left;}// 以 speed 的速度能否在 h 小时内吃完香蕉publicbooleancanFinish(int[] piles,int speed,int h){
long time =0;// 以速度为 speed 时吃完所有香蕉需要多少时间for(int i =0; i < piles.length; i++){
if(piles[i]% speed ==0){
time += piles[i]/ speed;}else{
time += piles[i]/ speed +1;}}return time <= h;}}
暴力解法:
classSolution{
publicintminEatingSpeed(int[] piles,int h){
int max =Integer.MIN_VALUE;for(int i =0; i < piles.length; i++){
max =Math.max(max, piles[i]);}for(int speed =1; speed <= max; speed++){
// 每小时最少能吃 1 根,最多能吃 max 根if(canFinish(piles, speed, h)){
return speed;}}return-1;}// 以 speed 的速度能否在 h 小时内吃完香蕉publicbooleancanFinish(int[] piles,int speed,int h){
long time =0;// 以速度为 speed 时吃完所有香蕉需要多少时间for(int i =0; i < piles.length; i++){
if(piles[i]% speed ==0){
time += piles[i]/ speed;}else{
time += piles[i]/ speed +1;}}return time <= h;}}
classSolution{
publicintshipWithinDays(int[] weights,int days){
int left =Integer.MIN_VALUE;// 载重的最小值是 max(weights)int right =0;// 载重的最大值是 sum(weights)for(int weight : weights){
left =Math.max(left, weight);
right += weight;}// 寻找左侧边界的二分搜索。如果以当前的载重可以运完包裹,还需要继续往左找,因为左边还可能有以更小的载重也可以运完包裹while(left <= right){
int mid = left +(right - left)/2;if(canFinish(weights, days, mid)){
right = mid -1;}else{
left = mid +1;}}return left;}// 如果载重为 capacity,是否能在 days 天内运完货物publicbooleancanFinish(int[] weights,int days,int capacity){
int index =0;for(int i =0; i < days; i++){
int temp = capacity;while(temp - weights[index]>=0){
temp -= weights[index];
index++;if(index == weights.length){
returntrue;}}}returnfalse;}}
410. 分割数组的最大值
classSolution{
// 问题转换:当 nums 的连续子数组的和的最大值为 max 时,至少可以将 nums 分割成几个子数组publicintsplitArray(int[] nums,int m){
// 连续子数组的和的最大值一定在 [max(nums), sum(nums)] 之间:// (1)当数组中每个元素都是一个子数组时,连续子数组的和的最大值就是 max(nums)// (2)当数组中只有一个子数组时,连续子数组的和的最大值就是 sum(nums)int left =Integer.MIN_VALUE;int right =0;for(int num : nums){
left =Math.max(left, num);
right += num;}// 寻找左侧边界的二分搜索。如果以当前的 max 可以将 nums 分割成 m 个子数组,那么还需要继续往左找,因为左边还可能有更小的 max 也可以将 nums 分割成 m 个子数组while(left <= right){
int mid = left +(right - left)/2;int n =split(nums, mid);if(n == m){
right = mid -1;}elseif(n > m){
// 当前的 max 小了,导致分的组数变多了
left = mid +1;}else{
// 当前的 max 大了,导致分的组数变少了
right = mid -1;}}return left;}// 如果 nums 的每个连续子数组的和都不超过 max,计算 nums 最少可以被分割成几个数组// 比如:nums = [7, 2, 5, 10, 8], max = 12,最少可以被分为 [7,2] [5] [10] [8] 4 个publicintsplit(int[] nums,int max){
int count=1;// 记录连续子数组的数量int sum =0;// 记录每个连续子数组的元素和for(int i =0; i < nums.length; i++){
if(sum + nums[i]<= max){
sum += nums[i];}else{
count++;
sum = nums[i];}}return count;}}
33. 搜索旋转排序数组
classSolution{
publicintsearch(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;}// 在下标 [0, mid] 间为有序数组if(nums[0]<= nums[mid]){
// 有 = 是因为 mid 可能也为 0if(nums[0]<= target && target < nums[mid]){
right = mid -1;}else{
left = mid +1;}}else{
// 如果 nums[mid] < nums[0],那么在下标 [mid + 1, nums.length - 1] 为有序if(nums[mid]< target && target <= nums[nums.length -1]){
left = mid +1;}else{
right = mid -1;}}}return-1;}}
74. 搜索二维矩阵
二分法:
以下的时间复杂度:O(log m + log n) = O(log mn)
classSolution{
publicbooleansearchMatrix(int[][] matrix,int target){
int rowIndex =binarySearchFirstColumn(matrix, target);if(rowIndex <0){
returnfalse;}returnbinarySearchRow(matrix[rowIndex], target);}// 对矩阵的第一列的元素二分查找,找到最后一个不大于目标值的元素publicintbinarySearchFirstColumn(int[][] matrix,int target){
int left =0, right = matrix.length -1;while(left <= right){
int mid = left +(right - left)/2;if(target == matrix[mid][0]){
return mid;}elseif(target < matrix[mid][0]){
right = mid -1;}else{
left = mid +1;}}return left -1;}// 对矩阵的行进行二分查找,找到符合要求的元素publicbooleanbinarySearchRow(int[] row,int target){
int left =0, right = row.length -1;while(left <= right){
int mid = left +(right - left)/2;if(target == row[mid]){
returntrue;}elseif(target < row[mid]){
right = mid -1;}else{
left = mid +1;}}returnfalse;}}
以下的时间复杂度:O(m + n)。访问到的下标的行最多减少 m 次,列最多增加 n 次,因此该算法最多循环 m + n 次。