排序数组的问题首先想到的是二分法。

1、剑指 Offer 53 - II.0~n-1中缺失的数字

1.1 题目

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:

输入: [0,1,3] 输出: 2

示例 2:

输入: [0,1,2,3,4,5,6,7,9] 输出: 8

1.2 思路

排序数组中的搜索问题,首先想到二分法解决。
本题有两种思路:

  1. 数学法:0到数组中最后一个数字正常的前n项和 - 数组中所有元素之和,差就是缺失的数字。时间复杂度为O(n);
  2. 二分查找,时间复杂度为O(lgn)。

二分查找虽然不好想,但时间复杂度优于数学法,因此面试时还是用二分查找。

  1. 根据题意,数组可以按照以下规则划分为两部分,其中i为数组下标:
    1. 左子数组: nums[i] =i ;
    2. 右子数组: nums[i] != i;
  2. 缺失的数字等于 “右子数组的首位元素” 对应的下标;因此考虑使用二分法查找 “右子数组的首位元素”。

image.png

while循环里的结束条件是left <= right,这个可以用以下两个例子带入来确定:[0,1,2,3,4,5,6,7,9],[0,2,3,4,5]

1.3 代码

  1. class Solution {
  2. public int missingNumber(int[] nums) {
  3. int left = 0, right = nums.length - 1;
  4. // 跳出循环时,left 和 right 分别指向 “右子数组的首位元素” 和 “左子数组的末位元素” ,
  5. // 因此返回 left 即可。
  6. while (left <= right) {
  7. int mid = (left + right) >> 1;
  8. if (nums[mid] == mid) {
  9. left = mid + 1;
  10. } else {
  11. right = mid - 1;
  12. }
  13. }
  14. return left;
  15. }
  16. }

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

2.1 题目

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

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

示例 2:

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

2.2 思路

排序数组查找数字,显然用二分查找,不同的是本题数组中存在重复元素,需要返回target在数组中出现的次数。

  1. 常规的二分查找写法,算出mid,然后移动left和right向中间搜索;
  2. 当nums[mid]== target时,说明我们找到了要找的元素,此时可以:
    1. 由mid向两边搜索,一旦搜索的元素不等于target时,向右或者向左搜索的指针就停止;
    2. 由当前left和right向mid逼近,一旦nums[left]==target或者nums[right]==target,就说明找到了target在nums数组中的边界,停止,最后通过left和right计算target数组的长度,返回个数。
  3. 注意while的终止条件,当数组元素为[1], target=1时,还需要走一次while循环,因此left要==right。

    2.3 代码

    1. class Solution {
    2. public int search(int[] nums, int target) {
    3. int left = 0, right = nums.length - 1;
    4. // 当数组元素为[1], target=1时,还需要走一次while循环,因此left要==right
    5. while (left <= right) {
    6. int mid = (left + right) >> 1;
    7. if (nums[mid] > target) {
    8. right = mid - 1;
    9. } else if (nums[mid] < target) {
    10. left = mid + 1;
    11. } else {
    12. // 让left和right都逼近mid,直到与target相等停止,最后算长度就是个数
    13. while (nums[left] != target) {
    14. ++left;
    15. }
    16. while (nums[right] != target) {
    17. --right;
    18. }
    19. return right - left + 1;
    20. }
    21. }
    22. return 0;
    23. }
    24. }

    3、剑指 Offer 16. 数值的整数次方

    快速幂,真难顶。

    3.1 题目

    实现 pow(x, n) ,即计算 x 的 n 次幂函数(即,xn)。不得使用库函数,同时不需要考虑大数问题。
    示例 1:

    输入:x = 2.00000, n = 10 输出:1024.00000

示例 2:

输入:x = 2.10000, n = 3 输出:9.26100

示例 3:

输入:x = 2.00000, n = -2 输出:0.25000 解释:2-2 = 1/22 = 1/4 = 0.25

3.2 思路

image.png
二分法的应用,需要注意一点是n是负数的情况。

3.3 代码

  1. class Solution {
  2. public double myPow(double x, int n) {
  3. return n > 0? quick(x, n): 1 / quick(x, n);
  4. }
  5. private double quick(double x, int n) {
  6. if (n == 0) {
  7. return 1.0;
  8. }
  9. double y = quick(x, n / 2);
  10. return (n & 1) == 0? y * y: y * y * x;
  11. }
  12. }

4、704. 二分查找

4.1 题目

给定一个 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

4.2 思路

二分查找基础版。

4.3 代码

  1. class Solution {
  2. public int search(int[] nums, int target) {
  3. int left = 0, right = nums.length - 1;
  4. while (left <= right) {
  5. int mid = (left + right) >> 1;
  6. if (nums[mid] > target) {
  7. right = mid - 1;
  8. } else if (nums[mid] < target) {
  9. left = mid + 1;
  10. } else {
  11. return mid;
  12. }
  13. }
  14. return -1;
  15. }
  16. }