二分查找学习记录

704. 二分查找

题目描述

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示:

你可以假设 nums 中的所有元素是不重复的。
n 将在 [1, 10000]之间。
nums 的每个元素都将在 [-9999, 9999]之间。

答案:

在有序数组中查找目标值用二分法

对于下标mid(区间(i,j)的中点),

nums[mid] == target,nums[mid]就是目标值

nums[mid] > targer,目标值在(i, mid - 1)之间

nums[mid] < targer,目标值在(mid + 1, j)之间

代码如下:

class Solution {
    public int search(int[] nums, int target) {
        int n = nums.length;
        int i = 0, j = n - 1;
        while (i <= j) {
            int mid = (j - i) / 2 + i;
            if (nums[mid] == target)
            return mid;
            if (nums[mid] > target) {
                j = mid - 1;
            } else {
                i = mid + 1;
            }
        }
        return -1;
    }
}

剑指 Offer II 070. 排序数组中只出现一次的数字

题目描述

给定一个只包含整数的有序数组 nums ,每个元素都会出现两次,唯有一个数只会出现一次,请找出这个唯一的数字。

你设计的解决方案必须满足 O(log n) 时间复杂度和 O(1) 空间复杂度。

示例 1:

输入: nums = [1,1,2,3,3,4,4,8,8]
输出: 2

示例 2:

输入: nums = [3,3,7,7,10,11,11]
输出: 10

提示:

1 <= nums.length <= 10^5
0 <= nums[i] <= 10^5

答案:

对于下标mid(区间(i,j)的中点),

如果mid是偶数,nums[mid] == nums[mid + 1]; mid + 1 == mid ^ 1;

如果mid是奇数,nums[mid] == nums[mid - 1]; mid - 1 == mid ^ 1;

符合上述条件,则目标值在(mid + 1,j)之间;

否则,目标值在(i,mid - 1)之间(出现一次数的后面的nums都不符合以上条件)。

代码如下:

class Solution {
    public int singleNonDuplicate(int[] nums) {
        int n = nums.length;
        int i = 0, j = n - 1;
        while (i <= j) {
            int mid = (i + j) / 2;
            if (nums[mid ^ 1] == nums[mid]) {
                i = mid + 1;
            } else {
                j = mid - 1;
            }
        }
        return nums[i];
    }
}

剑指 Offer II 072. 求平方根

题目描述

给定一个非负整数 x ,计算并返回 x 的平方根,即实现 int sqrt(int x) 函数。

正数的平方根有两个,只输出其中的正数平方根。

如果平方根不是整数,输出只保留整数的部分,小数部分将被舍去。

示例 1:

输入: x = 4
输出: 2

示例 2:

输入: x = 8
输出: 2
解释: 8 的平方根是 2.82842…,由于小数部分将被舍去,所以返回 2

提示:

0 <= x <= 2^31 - 1

答案:
class Solution {
    public int mySqrt(int x) {
        int i = 0, j = x;
        while (i <= j) {
            int mid = (i + j) / 2;
            if ((long)mid * mid <= x) {
                i = mid + 1;
            } else {
                j = mid - 1;
            }
        }
        return j;
    }
}

剑指 Offer 11. 旋转数组的最小数字

题目描述

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。

注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。

示例 1:

输入:numbers = [3,4,5,1,2]
输出:1

示例 2:

输入:numbers = [2,2,2,0,1]
输出:0

提示:

n == numbers.length
1 <= n <= 5000
-5000 <= numbers[i] <= 5000
numbers 原来是一个升序排序的数组,并进行了 1 至 n 次旋转

答案:

对于下标mid(区间(i,j)的中点),

如果numbers[mid] < numbers[j],目标值在(i,mid)之间(mid也可能是目标值);

如果numbers[mid] > numbers[j],目标值在(mid + 1,j)之间(mid不可能是目标值);

如果numbers[mid] == numbers[j],不确定目标值的位置,则 j – (忽略右端点)。

class Solution {
    public int minArray(int[] numbers) {
        int n = numbers.length;
        int i = 0, j = n - 1;
        while (i <= j) {
            int mid = (i + j) / 2;
            if (numbers[mid] < numbers[j]) {
                j = mid;
            } else if (numbers[mid] > numbers[j]) {
                i = mid + 1;
            } else {
                j--;
            }
        }
        return numbers[i];
    }
}

剑指 Offer 53 - I. 在排序数组中查找数字 I

题目描述

统计一个数字在排序数组中出现的次数。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

提示:

0 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9
nums 是一个非递减数组
-10^9 <= target <= 10^9

答案:

求出nums数组中目标值第一次和最后一次出现的位置l,r

出现的次数 = r - l + 1。

class Solution {
    public int search(int[] nums, int target) {
        int n = nums.length;
        int i = 0, j = n - 1;
        boolean flag = false;
        int l = -1, r = -1;
        while (i <= j) {
            int mid = (i + j) / 2;
            if (nums[mid] >= target) {
                j = mid - 1;
            } else {
                i = mid + 1;
            }
        }
        l = i;
        i = 0;
        j = n - 1;
        while (i <= j) {
            int mid = (i + j) / 2;
            if (nums[mid] > target) {
                j = mid - 1;
            } else {
                i = mid + 1;
            }
        }
        r = j;

        return r - l + 1;
    }
}

剑指 Offer II 068. 查找插入位置

题目描述

给定一个排序的整数数组 nums 和一个整数目标值 target ,请在数组中找到 target ,并返回其下标。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2

示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1

示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4

示例 4:

输入: nums = [1,3,5,6], target = 0
输出: 0

示例 5:

输入: nums = [1], target = 0
输出: 0

提示:

1 <= nums.length <= 10^4
-10^4 <= nums[i] <= 10^4
nums 为无重复元素的升序排列数组
-10^4 <= target <= 10^4

答案:

第一个 >= 目标值所在的位置就是所求的位置

class Solution {
    public int searchInsert(int[] nums, int target) {
        int n = nums.length;
        int i = 0, j = n - 1;
        while (i <= j) {
            int mid = (i + j) / 2;
            if (nums[mid] >= target) {
                j = mid - 1;
            } else {
                i = mid + 1;
            }
        }
        return i;
    }
}

剑指 Offer II 069. 山峰数组的顶部

题目描述

符合下列属性的数组 arr 称为 山峰数组(山脉数组) :

arr.length >= 3
存在 i(0 < i < arr.length - 1)使得:
arr[0] < arr[1] < … arr[i-1] < arr[i]
arr[i] > arr[i+1] > … > arr[arr.length - 1]
给定由整数组成的山峰数组 arr ,返回任何满足 arr[0] < arr[1] < … arr[i - 1] < arr[i] > arr[i + 1] > … > arr[arr.length - 1] 的下标 i ,即山峰顶部。

示例 1:

输入:arr = [0,1,0]
输出:1

示例 2:

输入:arr = [1,3,5,4,2]
输出:2

示例 3:

输入:arr = [0,10,5,2]
输出:1

示例 4:

输入:arr = [3,4,5,1]
输出:2

示例 5:

输入:arr = [24,69,100,99,79,78,67,36,26,19]
输出:2

提示:

3 <= arr.length <= 10^4
0 <= arr[i] <= 10^6
题目数据保证 arr 是一个山脉数组

答案:

第一个arr[mid] > arr[mid + 1] 的位置

class Solution {
    public int peakIndexInMountainArray(int[] arr) {
        int n = arr.length;
        int i = 0, j = n - 2;
        while (i <= j) {
            int mid = (i + j) / 2;
            if (arr[mid] > arr[mid + 1]) {
                j = mid - 1;
            } else {
                i = mid + 1;
            }
        }
        return i;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

I'm 程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值