(笔试算法)Leetcode101之二分查找

二分也是一种双指针,记录左右区间,每次将目标值与左右区间的中位数进行判断,用于在有序数组中搜索某个值。

69. x 的平方根 – 简单

计算给定值的算术平方根。结果只保留整数部分

输入:x = 8
输出:2
解释:8 的算术平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
/**
	二分: 
	注意:这里 l=1,解决x=0的问题
	返回值是r,因为最终循环结束是 l = r+1, 但是r的右边是 平分和大于x的情况,l的左边是平方和小于x的情况
**/
class Solution {
    public int mySqrt(int x) {
    	int l = 1, r = x;
    	while (l <= r) {
    		int mid = l + (r-l)/2;
    		if (mid == x/mid) return mid;
    		else if (mid > x / mid) r = mid - 1;
    		else l = mid + 1;
    	}
    	return r;
    }
}

//  牛顿迭代法 a = ( a + x / a ) / 2        ---  使用于 f(x) = 0 这种函数
class Solution {
    public int mySqrt(int x) {
        long a = x;
        while (a*a > x) {
            a = (a + x/a)/2;
        }
        return (int)a;
    }
}

34. 在排序数组中查找元素的第一个和最后一个位置 – 中等

找到目标值的起始位置和结束位置。不存在返回 [-1,-1]。

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
/**
	思路:双指针+二分查找
		找到一个target 同时更新 左右边界,并且 查找 [l,target-1] [target + 1] 两个边界
**/
class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = {-1,-1};
        findLocation(nums, target, 0, nums.length - 1, res);
        return res;
    }

    private void findLocation(int[] nums, int target, int l, int r, int[] res) {
		while (l <= r) {
			int mid = l + (r-l)/2;
			if (nums[mid] > target) r = mid - 1;
			else if (nums[mid] < target) l = mid + 1;
			else {
				res[0] = res[0] == -1?mid:Math.min(res[0], mid);
				res[1] = Math.max(res[1], mid);
				findLocation(nums, target, l, mid - 1, res);
				findLocation(nums, target, mid + 1, r, res);
				break;
			}
		}
	}
}

81. 搜索旋转排序数组 II – 中等

一个非递减的数组,经过旋转了一次,判断一个数是否在这旋转数组中。

输入:nums = [2,5,6,0,0,1,2], target = 0
输出:true
//	[0 0 1 2 2 5 6]  旋转一次->  [2,5,6,0,0,1,2]
/**
	思路:二分查找
    	判断 [l,mid] 和 [mid,r] 区间的递增递减情况,然后分类讨论。
    	nums[mid] > nums[l] 那么 [l,mid] 必然是递增区间 [mid,r] 不确定
    	nums[mid] < nums[r] 那么 [mid.r] 必然是递减区间 [l,mid] 不确定
    	nums[mid] == nums[l] 或者 nums[mid] == nums[r] 不能确定任何区间情况 所以可以 ++l / --r
**/
class Solution {
    public boolean search(int[] nums, int target) {
        int l = 0, r = nums.length - 1;
    	while(l <= r) {
    		int mid = (r+l)>>>1;
    		if (nums[mid] == target || nums[l] == target || nums[r] == target) return true; 
    		// l - mid 是单调递增区间
    		if (nums[mid] > nums[l]) {
    			if(nums[mid] > target && nums[l] < target) r = mid - 1;
    			else l = mid + 1;
    		// mid - r 是单调递增区间
    		} else if (nums[mid] < nums[r]) {
    			if(nums[mid] < target && target < nums[r]) l = mid + 1;
    			else r = mid - 1;
    		} else ++l;
    	}
    	return false;
    }
}

154. 寻找旋转排序数组中的最小值 II – 困难

求旋转n次排序数组的最小值。
旋转规则: 每次旋转把最后一个元素旋转到第一个位置 [0,1,4,4,5,6,7] -> [7,0,1,4,4,5,6]

输入:nums = [2,2,2,0,1]
输出:0
/*
	二分: nums[mid] < nums[r] 说明最小值在 [l,mid] 之间
          nums[mid] > nums[r] 说明最小值在 [mid+1, r] 之间
    	  nums[mid] == nums[r] 说明最小值在 [l, r-1]之间
   结束条件 l < r 即:当 l==r 结束循环,此时就是结果
*/
class Solution {
    public int findMin(int[] nums) {
    	int l = 0, r = nums.length - 1;
    	while (l < r) {
    		int mid = (l + r)>>>1;
    		if (nums[mid] > nums[r]) {
    			l = mid + 1;
    		} else if (nums[mid] < nums[r]) {
    			r = mid;
    		} else --r;
    	}
    	return nums[l];
    }
}

540. 有序数组中的单一元素 – 中等

在不递减的数组中查找只出现一次的元素 (其他元素均出现两次),要求,时间复杂度O(logn)、空间复杂度O(1)

输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2
/**
	思路一:二进制元素 时间复杂度O(n)

**/
class Solution {
    public int singleNonDuplicate(int[] nums) {
    	int res = 0;
    	for (int i = 0; i < nums.length; ++i) res ^= nums[i];
    	return res;
    }
}
/**
	思路二:二分
    	如果 nums[mid] == nums[mid-1], 
            [l,mid]之前元素是偶数个,说明结果在 [mid+1, r]之间
            [l,mid]之前元素是奇数个,说明结果在 [l, mid - 2]之间
        如果 nums[mid] == nums[mid+1]
            对右区间做相同考虑
        如果上面都不成立,说明nums[mid]只出现一次
**/
class Solution {
    public int singleNonDuplicate(int[] nums) {
    	int l = 0, r = nums.length - 1;
    	while (l < r) {
    		int mid = (r+l)>>>1;
    		if (nums[mid] == nums[mid - 1]) {
    			if ((mid - l + 1) % 2 == 0) l = mid + 1;
    			else r = mid - 2;
    		} else if (nums[mid] == nums[mid + 1]) {
    			if ((r - mid + 1) % 2 == 0) r = mid - 1;
    			else l = mid + 2;
    		} else {
    			return nums[mid];
    		}
    	}
    	return nums[l];
    }
}

4. 寻找两个正序数组的中位数 – 困难

在两个长为m+n不递减的数组中,找到中位数。要求时间复杂度为 0(log(m+n))。

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
/**
	如果不考虑时间复杂度:
        思路:双指针,指向两个数组起点位置,每次移动较小的数  (类似归并两个数组)
	二分:对较小的数组进行二分,根据较小的数组的长度 计算另一个数组元素的位置
**/
class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    	int m = nums1.length, n = nums2.length;
    	if (m > n) return findMedianSortedArrays(nums2, nums1);
    	
    	int rMid = (m+n+1)>>>1;
    	
    	int low = 0, high = m;
    	while (low < high) {
    		// 这里取中位数+1,防止low=mid在两个元素时进入死循环
    		int mid = low + (high - low + 1)/2;	// nums1数组右侧第一个元素  
    		int rLoc = rMid - mid;			// nums2数组右侧第一个元素
    		if (nums1[mid-1] > nums2[rLoc]) high = mid - 1;
    		else low = mid;
    	}

    	// 定义四个子数组相邻的边界元素
    	int rNeed = rMid - low;
    	int s1LMax = low==0?Integer.MIN_VALUE:nums1[low-1];
    	int s1RMin = low==m?Integer.MAX_VALUE:nums1[low];
    	int s2LMax = rNeed==0?Integer.MIN_VALUE:nums2[rNeed-1];
    	int s2RMin = rNeed==n?Integer.MAX_VALUE:nums2[rNeed];
    	if ((m+n)%2 == 0) return (Math.max(s1LMax, s2LMax) + Math.min(s1RMin, s2RMin))/2.0;
    	else return Math.max(s1LMax, s2LMax);
    }
}

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值