[LeetCode 33. 81. 153. 154.] 旋转数组中的二分查找

旋转数组查找元素

一共四道旋转数组查找元素的题目:

  1. 无重复数组查找指定元素,
  2. 有重复数组查找指定元素,
  3. 无重复数组查找最小元素,
  4. 有重复数组查找最小元素。

LeetCode 33. Search in Rotated Sorted Array

题目描述

You are given an integer array nums sorted in ascending order (with distinct values), and an integer target.

Suppose that nums is rotated at some pivot unknown to you beforehand (i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).

If target is found in the array return its index, otherwise, return -1.

Example 1:

Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4

Example 2:

Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1

Example 3:

Input: nums = [1], target = 0
Output: -1

Constraints:

  • 1 <= nums.length <= 5000
  • -104 <= nums[i] <= 104
  • All values of nums are unique.
  • nums is guaranteed to be rotated at some pivot.
  • -104 <= target <= 104

解题思路

旋转数组的二分查找,这是二分查找的一道变形题。考察点在于二分条件的建立。

对于二分的条件,有两种思路:

  1. 始终使用 target 作为二分条件,先和 mid 比较,再和 left 比较,以确定在左半还是右半;
  2. 先考虑找出有序的半个数组,然后确定 target 是否在这半个数组中。

两种思路都可以解决,注意:
思路一并不能总是分辨出在哪一半,所以有时候需要两边都查找;
思路二使用 mid 和 left 比较来找出有序半边的时候,注意 left == mid 的特殊情况。

参考代码

/*
 * @lc app=leetcode id=33 lang=cpp
 *
 * [33] Search in Rotated Sorted Array
 */

// @lc code=start
class Solution {
    // All values of nums are unique.
public:
/*
    int search(vector<int>& nums, int target) {
        if (nums.empty()) return -1;
        int l = 0, r = nums.size();
        return bsh(nums, l, r, target);
    }
    int bsh(vector<int>& nums, int l, int r, int target) {
        while (l < r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] == target) return mid;
            if (nums[mid] < target) {
                if (nums [l] == target) return l;
                if (nums [l] < target) {
                    // r = mid;
                    int res = bsh(nums, l, mid, target);
                    if (res >= 0) return res;
                    return bsh(nums, mid+1, r, target);
                } else { // if (nums [l] > target)
                    l = mid + 1;
                }
            } else { // if (nums[mid] > target)
                if (nums [l] == target) return l;
                if (nums [l] < target) {
                    r = mid;
                } else { // if (nums [l] > target)
                    // l = mid + 1;
                    int res = bsh(nums, l, mid, target);
                    if (res >= 0) return res;
                    return bsh(nums, mid+1, r, target);
                }
            }
        }
        return -1;
    } // AC
*/
    int search(vector<int>& nums, int target) {
        if (nums.empty()) return -1;
        int l = 0, r = nums.size() - 1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if (nums[mid] == target) return mid;
            if (nums[l] <= nums[mid]) { // left half sorted
                if (nums[l] <= target && target < nums[mid]) r = mid - 1;
                else l = mid + 1;
            } else { // right half sorted
                if (nums[mid] < target && target <= nums[r]) l = mid + 1;
                else r = mid - 1;
            }
        }
        return -1;
    } // attention [3,1], target = 1, you must use nums[l]<=nums[mid]
};
// @lc code=end

LeetCode 81. Search in Rotated Sorted Array II

题目描述

There is an integer array nums sorted in non-decreasing order (not necessarily with distinct values).

Before being passed to your function, nums is rotated at an unknown pivot index k (0 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,4,4,5,6,6,7] might be rotated at pivot index 5 and become [4,5,6,6,7,0,1,2,4,4].

Given the array nums after the rotation and an integer target, return true if target is in nums, or false if it is not in nums.

Example 1:

Input: nums = [2,5,6,0,0,1,2], target = 0
Output: true

Example 2:

Input: nums = [2,5,6,0,0,1,2], target = 3
Output: false

Constraints:

  • 1 <= nums.length <= 5000
  • -104 <= nums[i] <= 104
  • nums is guaranteed to be rotated at some pivot.
  • -104 <= target <= 104

Follow up: This problem is the same as Search in Rotated Sorted Array, where nums may contain duplicates. Would this affect the runtime complexity? How and why?

解题思路

与上一题相同,同样是先确定哪半边是有序的,然后判断target是否在有序半边中,从而缩小搜索范围。
不同的地方在于,有重复元素,也就有了两端元素和中间点的元素值相同的情况,需要特殊处理:

  • 要么,直接线性搜这一段;
  • 要么,哪个端点和中间点的元素值相同,哪个端点往里移动一位。也不会影响结果正确性。

参考代码

/*
 * @lc app=leetcode id=81 lang=cpp
 *
 * [81] Search in Rotated Sorted Array II
 */

// @lc code=start
class Solution {
public:
    bool search(vector<int>& nums, int target) {
        if (nums.empty()) return false;
        int l = 0, r = nums.size() - 1;
        while (l <= r) {
            int m = l + (r - l) / 2;
            if (nums[m] == target) return true;
            if (nums[l] < nums[m]) { // left half ordered
                if (nums[l] <= target && target < nums[m]) {
                    r = m - 1; // enter ordered half
                } else {
                    l = m + 1;
                }
            } else if (nums[m] < nums[r]) { // right half ordered
                if (nums[m] < target && target <= nums[r]) {
                    l = m + 1; // enter ordered half
                } else {
                    r = m - 1;
                }
            } else { // case [1,0,1,1,1] 0
                // for (int i = l; i <= r; i++) {
                //     if (nums[i] == target) return true;
                // }
                // return false; // OK

                if (nums[l] == nums[m]) l++;
                if (nums[m] == nums[r]) r--;
            }
        }
        return false;
    } // AC
};
// @lc code=end

LeetCode 153. Find Minimum in Rotated Sorted Array

题目描述

Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,2,4,5,6,7] might become:

  • [4,5,6,7,0,1,2] if it was rotated 4 times.
  • [0,1,2,4,5,6,7] if it was rotated 7 times.
    Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]].

Given the sorted rotated array nums, return the minimum element of this array.

Example 1:

Input: nums = [3,4,5,1,2]
Output: 1
Explanation: The original array was [1,2,3,4,5] rotated 3 times.

Example 2:

Input: nums = [4,5,6,7,0,1,2]
Output: 0
Explanation: The original array was [0,1,2,4,5,6,7] and it was rotated 4 times.

Example 3:

Input: nums = [11,13,15,17]
Output: 11
Explanation: The original array was [11,13,15,17] and it was rotated 4 times.

Constraints:

  • n == nums.length
  • 1 <= n <= 5000
  • -5000 <= nums[i] <= 5000
  • All the integers of nums are unique.
  • nums is sorted and rotated between 1 and n times.

解题思路

无重复元素的旋转数组查找最小元素。思路是折半查找,每次折半的时候总有一半是有序的,依据最小元素是否在此有序半边决定下一步的方向。

参考代码

/*
 * @lc app=leetcode id=153 lang=cpp
 *
 * [153] Find Minimum in Rotated Sorted Array
 */

// @lc code=start
class Solution {
public:
    // All the integers of nums are unique.
    int findMin(vector<int>& nums) {
        assert(!nums.empty());
        int l = 0, r = nums.size() - 1;
        while (l <= r) {
            if (nums[l] <= nums[r]) return nums[l]; // sorted array
            int m = l + (r - l) / 2;
            if (nums[l] <= nums[m]) l = m + 1; // left half ordered
            else r = m; // nums[l] == nums[m] means l+1 == r
        }
        assert(false);
        return 0;
    } // binary search, find the sorted half
};
// @lc code=end

LeetCode 154. Find Minimum in Rotated Sorted Array II

题目描述

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).

Find the minimum element.

The array may contain duplicates.

Example 1:

Input: [1,3,5]
Output: 1

Example 2:

Input: [2,2,2,0,1]
Output: 0

Note:

  • This is a follow up problem to Find Minimum in Rotated Sorted Array.
  • Would allow duplicates affect the run-time complexity? How and why?

解题思路

有重复元素的旋转数组查找最小值。这个比无重复元素的查找稍微麻烦一点在于[1,1,1,1,0,1][1,0,1,1,1,1]这种情况的处理,没有办法根据中间值与端点值的大小来判断如何折半。
解决办法是不再折半,降为收缩一边,收缩左端点或者右端点均可;但是不能都收缩,因为还有l == m这种情况。

参考代码

实现的时候还做了一点优化,只要左端点严格小于右端点,则左端点一定是最小值。

/*
 * @lc app=leetcode id=154 lang=cpp
 *
 * [154] Find Minimum in Rotated Sorted Array II
 */

// @lc code=start
class Solution {
public:
    // The array may contain duplicates.
    int findMin(vector<int>& nums) {
        assert(!nums.empty());
        int l = 0, r = nums.size() - 1;
        while (l <= r) {
            if (nums[l] < nums[r]) {
                return nums[l]; // sorted array
            } else if (nums[l] > nums[r]) {
                int m = l + (r - l) / 2;
                if (nums[l] <= nums[m]) l = m + 1;
                else r = m; // nums[l] == nums[m] means l+1 == r
            } else {
                r--; // or l++;
            }
        }
        // assert(false);
        assert(l < nums.size());
        return nums[l];
    } // binary search, find the sorted half
};
// @lc code=end
posted @ 2021-01-21 22:25  与MPI做斗争  阅读(302)  评论(0编辑  收藏  举报