二分查找
点击查看代码
//闭合区间二分查找
public int Binary_Search_1(int[] num, int target){
int left = 0;
int right = num.length - 1;
while(left <= right){
int mid = left + (right - left)/2;
if(num[mid] < target){
left = mid + 1;
}
else{
right = mid - 1;
}
}
return left;//return right+1;
}
//左闭右开二分查找
public int Binary_search_2(int[] num, int target){
int left = 0;
int right = num.length;
while(left < right){
int mid = left + (right - left)/2;
if(num[mid] < target){
left = mid + 1;
}
else{
right = mid;
}
}
return left;//return right;
}
//开区间二分查找
public int Binary_Search_3(int[] num,int target){
int left = -1;
int right = num.length;
while(left+1 < right){
int mid = left + (right - left)/2;
if(num[mid] < target){
left = mid;
}
else{
right = mid;
}
}
return right;
}
74.搜索二维矩阵
第一次提交index超出边界,直接使用index搜索数值是错误的。
点击查看代码
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
for(int i = 0;i < m ; i++){
int index = Binary_Search(matrix[i],target);
if(matrix[i][index] == target){//error,正确写法为if(index < n && matrix[i][index] == target)
return true;
}
}
return false;
}
public int Binary_Search(int[] nums,int target){
int left = 0 ;
int right = nums.length;
while(left < right){
int mid = left + (right - left)/2;
if(nums[mid] < target){
left = mid + 1 ;
}
else{
right = mid;
}
}
return left;
}
}
第二次提交优化先判断是否需要使用二分查找
点击查看代码
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
for(int i = 0;i < m ; i++){
if(matrix[i][n-1] < target){//免除不必要的二分查找。
continue;
}
int index = Binary_Search(matrix[i],target);
if(index < n && matrix[i][index] == target){
return true;
}
}
return false;
}
public int Binary_Search(int[] nums,int target){
int left = 0 ;
int right = nums.length;
while(left < right){
int mid = left + (right - left)/2;
if(nums[mid] < target){
left = mid + 1 ;
}
else{
right = mid;
}
}
return left;
}
}
第三次提交,坐标映射,二维矩阵可以看作一维数组。
点击查看代码
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
int index = Binary_Search(matrix,target);
int row = index/n;
int col = index%n;
if(index < m*n && matrix[row][col] == target){
return true;
}
return false;
}
public int Binary_Search(int[][] nums,int target){
int m = nums.length;
int n = nums[0].length;
int left = 0;
int right = m*n;
while(left < right){
int mid = left + (right - left)/2;
int row = mid/n;
int col = mid%n;
if(nums[row][col] < target){
left = mid + 1;
}
else{
right = mid;
}
}
return left;
}
}
34.在排序数组中查找元素第一个和最后一个位置
两次二分查找,找target和target+1
点击查看代码
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] res = new int[]{-1,-1};
int start_index = Binary_Search(nums,target);
int end_index = Binary_Search(nums,target + 1) - 1;
if(start_index < nums.length){
if(nums[start_index] == target){
res[0] = start_index;
res[1] = end_index;
return res;
}
}
return res;
}
public int Binary_Search(int[] nums, int target){
int left = 0;
int right = nums.length;
while(left < right){
int mid = left + (right - left)/2;
if(nums[mid] < target){
left = mid + 1;
}
else{
right = mid;
}
}
return left;
}
}
33.搜索旋转排序数组
难题啊,思路:二分查找就是为了每次减少一半的数组,现在数组不是有序的了,我们无法直接通过去左半边还是右半边。我们现在只能先看对于当前的mid而言,左半边是有序的or右半边是有序的。ok如果左半边有序,左半边有序就是我们的依据,根据左半边有序看一下左半边是否包含目标值,包含就放到左区间,不包含就放到右区间。
我们只能通过相信一半的有序区间来确定到底怎么移动指针。
点击查看代码
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
while(left <= right){
int mid = left + (right - left)/2;
//1.找到target直接返回
if(nums[mid] == target){
return mid;
}
//2.先看左边有序还是右边有序
//2.1左边有序区间!
if(nums[mid] >= nums[left]){
//如果左侧有序区间包含target,移动右指针到左侧有序空间。
//左侧有序区间不包含target,移动左指针到右侧无序区间。
if(nums[left] <= target && target < nums[mid]){
right = mid - 1;
}
else{
left = mid + 1;
}
}
//2.2右边有序区间!
else{
//如果右侧有序区间包含target,移动左指针到右侧有序区间。
//右侧有序区间不包含target,移动右指针到左侧无序区间。
if(nums[mid] < target && target <= nums[right]){
left = mid + 1;
}
else{
right = mid - 1;
}
}
}
return -1;
}
}
153.寻找旋转排序数组中的最小值
思路:ok依旧旋转排序数组,看着就不会做。狐假虎威!其实非常简单,只需要明白旋转点=断裂点=最小值的点。当前mid的值小于最右边的值,那最小值只有可能是在左边区间或者mid这个值上,移动右指针到左区间。当前mid的值大于最右边的值,那断裂点肯定在右区间,移动指针到右区间并跳过当前值。
153不是一个标准的“找精确值”的问题,而是一个“找边界”或“找结构断点”的问题,必须使用保留性,也就是所谓的开闭区间。当二分查找的目的是寻找一个边界、最小值或插入位置时,我们倾向于使用 while (left < right) 配合 right = mid 的保留性逻辑,以确保答案不会被跳过。这是由问题本身的结构需求所决定的。
初始化只是定义了闭区间,实际上依然是左闭右开的区间选择。实际上如何定义区间取决于我们的目的,我们究竟要得到什么?
点击查看代码
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while(left < right){
int mid = left + (right - left)/2;
if(nums[mid] > nums[right]){
left = mid + 1;
}
else{
right = mid;
}
}
return nums[left];
}
}
4.寻找两个正序数组的中位数
思路:没有模板可言,依旧二分查找。
主函数通过辅助函数求的K小的值返回最终结果。
辅助函数寻找两个数组中第K小的数:二分查找在于每次扔一半也就是K的二分之1,看是nums1的一半还是nums2的一半,比较每个数组的前二分之K个的数,然后删除那个结尾最小的数组的一部分。循环删除,直到终止条件满足
点击查看代码
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int n = nums1.length;
int m = nums2.length;
int total = n + m;
if(total%2 == 1){
// 奇数
return find_k_min(nums1,nums2,total/2 + 1);
}
else{
// 偶数
return (find_k_min(nums1,nums2,total/2)+find_k_min(nums1,nums2,total/2+1)) / 2.0;
}
}
public int find_k_min(int[] nums1, int[] nums2, int K){
int index1 = 0;
int index2 = 0;
while(true){
//1. 终止条件
// 1.1 【优先判断】数组耗尽:如果 index 已经走到数组末尾
if(index1 == nums1.length){
return nums2[index2 + K - 1];
}
if(index2 == nums2.length){
return nums1[index1 + K - 1];
}
// 1.2 【其次判断】K=1:两个数组都还有元素,直接比较起点
if(K == 1){
return Math.min(nums1[index1], nums2[index2]);
}
//2. 分治思路
int half = K/2;
// 确定要比较的索引 p1 和 p2
// 使用 Math.min(index + half, length) - 1 来确保索引不会越界
int p1 = Math.min(index1 + half, nums1.length) - 1;
int p2 = Math.min(index2 + half, nums2.length) - 1;
// 比较删除谁的数并更改索引
if(nums1[p1] < nums2[p2]){
int eliminatedCount = (p1 - index1 + 1);
K = K - eliminatedCount;
index1 = p1 + 1;
}
else{
int eliminatedCount = (p2 - index2 + 1);
K = K - eliminatedCount;
index2 = p2 + 1;
}
}
}
}
浙公网安备 33010602011771号