模板
70%题目是单调性的
25%是一段满足,一段不满足,存在一个两段性的性质
当我们将区间
[l, r]
划分成[l, mid]
和[mid + 1, r]
时,其更新操作是r = mid
或者l = mid + 1;
,计算mid
时不需要加1。int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;//此时l=r;
}
当我们将区间
[l, r]
划分成[l, mid - 1]
和[mid, r]
时,其更新操作是r = mid - 1
或者l = mid
;,此时为了防止死循环,计算mid时需要加1。- 例如
r=l+1
,则mid=l
,whlie
之后l=mid
陷入死循环
如果写完之后发现是int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
return l; //此时l=r;
}
**l = mid**
,那么在计算**mid**
时需要加上**1**
,否则如果写完之后发现是**r = mid**
,那么在计算**mid**
时不能加**1**
。
**
二分的流程:
- 例如
- 确定二分边界
- 编写二分代码框架
- 设计一个check()
- 判断一下区间如何更新
- 如果更新方式
- l = mid,r = mid-1
二分查找
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
int mid = 0;
while(left<=right) {
mid = (left+right)>>1;
if(target < nums[mid]) {
right = mid-1;
}else if(target > nums[mid]) {
left = mid+1;
}else {
return mid;
}
}
return -1;
}
}
以二分查找为模板的题目
35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
- l = mid,r = mid-1
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例 2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
class Solution {
public int searchInsert(int[] nums, int target) {
// 第一个>=target的位置
if(nums.length==0)
return 0;
int left = 0,right = nums.length;
int mid = 0;
while(left < right) {
mid = left+right>>1;
if(nums[mid] >= target) {
right = mid;
} else {
left = mid+1;
}
}
return left;
}
}
153. 寻找旋转排序数组中的最小值
class Solution {
public int findMin(int[] nums) {
int l = 0 , r = nums.length-1;
int length = nums.length-1;
while(l < r) {
int mid= l+r>>1;
if(nums[mid] > nums[length]) {
l = mid+1;
} else {
r = mid;
}
}
return nums[l];
}
}
287. 寻找重复数
这个是无法套用模板的题目
使用抽屉原理, n*logn
class Solution {
public int findDuplicate(int[] nums) {
int left = 1;
int right = nums.length-1;
while(left<right) {
int mid = left+right>>1;
int cnt = 0;
for(int x:nums){
if(x>=left && x<=mid) {
cnt++;
}
}
if(cnt>mid-left+1) {
right = mid;
} else {
left = mid+1;
}
}
return left;
}
}
没有模板时候自己做的题目
1. 旋转数组的最小数字
做了很久,很恶心
**
https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/
class Solution {
public int minArray(int[] numbers) {
int left = 0,right=numbers.length-1;
int mid= 0;
while(left<=right) {
mid = (left+right)>>1;
if(numbers[mid] < numbers[right]) {
right = mid;
} else if (numbers[mid] > numbers[right]) {
left = mid+1;
} else {
right-=1;
}
}
return numbers[left];
}
}
34. 在排序数组中查找元素的第一个和最后一个位置
这个题目的**binarySearch需要仔细看一下,一个函数实现两个功能,可以分别查找target与target+1的元素的起始位置**
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public int[] searchRange(int[] nums, int target) {
int leftIdx = binarySearch(nums, target, true);
int rightIdx = binarySearch(nums, target, false) - 1;
if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {
return new int[]{leftIdx, rightIdx};
}
return new int[]{-1, -1};
}
public int binarySearch(int[] nums, int target, boolean lower) {
int left = 0, right = nums.length - 1, ans = nums.length;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] > target || (lower && nums[mid] >= target)) {
right = mid - 1;
ans = mid;
} else {
left = mid + 1;
}
}
return ans;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/solution/zai-pai-xu-shu-zu-zhong-cha-zhao-yuan-su-de-di-3-4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
4. 寻找两个正序数组的中位数.
寻找第k大的数字,每次比较num1[(k/2)-1] 和 num2[(k/2)-1];
- 两者中的小值,因为二者前面都有(k/2)-1个,所以较小值前面最多有k-2个数字,所以他不可能是第k大的数字
- 所以较小值和它前面的所有值都可以舍去
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return findMedianSortedArrays(nums2, nums1);
}
int m = nums1.length;
int n = nums2.length;
int left = 0, right = m;
// median1:前一部分的最大值
// median2:后一部分的最小值
int median1 = 0, median2 = 0;
while (left <= right) {
// 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]
// 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]
int i = (left + right) / 2;
int j = (m + n + 1) / 2 - i;
// nums_im1, nums_i, nums_jm1, nums_j 分别表示 nums1[i-1], nums1[i], nums2[j-1], nums2[j]
int nums_im1 = (i == 0 ? Integer.MIN_VALUE : nums1[i - 1]);
int nums_i = (i == m ? Integer.MAX_VALUE : nums1[i]);
int nums_jm1 = (j == 0 ? Integer.MIN_VALUE : nums2[j - 1]);
int nums_j = (j == n ? Integer.MAX_VALUE : nums2[j]);
if (nums_im1 <= nums_j) {
median1 = Math.max(nums_im1, nums_jm1);
median2 = Math.min(nums_i, nums_j);
left = i + 1;
} else {
right = i - 1;
}
}
return (m + n) % 2 == 0 ? (median1 + median2) / 2.0 : median1;
}
}