leetcode hot 100-33. 搜索旋转排序数组

33. 搜索旋转排序数组

给你一个升序排列的整数数组 nums ,和一个整数 target 。

假设按照升序排序的数组在预先未知的某个点上进行了旋转。(例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

1 <= nums.length <= 5000
-10^4 <= nums[i] <= 10^4
nums 中的每个值都 独一无二
nums 肯定会在某个点上旋转
-10^4 <= target <= 10^4

思路一:暴力法

遍历数组,分别判断每个元素是否等于target

复杂度分析:

时间复杂度:O(n)。没有利用数组部分有序的特点,遍历了整个数组,所以时间复杂度为O(n)。 空间复杂度:O(1)。

思路二:三次二分查找

二分法找到旋转点,实现思路可以参考:剑指offer 6.旋转数组的最小数字 & 剑指 Offer 11. 旋转数组的最小数字

接下来二分法在前半段有序数组区间中找target;

如果在前半段区间没找到,则二分法在后半段有序数组区间中找target;

都没找到, 返回-1

 1 class Solution {
 2     public int search(int[] nums, int target) {
 3 
 4         int len = nums.length;
 5         // 二分法找到旋转点
 6         int rotate = 0;
 7         int left = 0, right = len - 1, mid;
 8         while(left < right){
 9             mid = (left + right) / 2;
10             if(nums[mid] > nums[right]){ // 说明mid在前半段区间中
11                 left = mid + 1;
12             }else if(nums[mid] < nums[right]){  // 说明mid在后半段区间中
13                 right = mid;
14             }else{
15                 right--;    // 无法确定mid在哪个区间,right--
16             }
17         }
18         rotate = left;
19         System.out.println(rotate);
20         // 二分法在前半段有序数组区间中找target
21         left = 0;
22         right = rotate - 1;
23         while(left <= right){
24             mid = (left + right) / 2;
25             if(target > nums[mid]){
26                 left = mid + 1;
27             }else if(target < nums[mid]){
28                 right = mid - 1;
29             }else{
30                 return mid;
31             }
32         }
33         // 二分法在后半段有序数组区间中找target
34         left = rotate;
35         right = len - 1;
36         while(left <= right){
37             mid = (left + right) / 2;
38             if(target > nums[mid]){
39                 left = mid + 1;
40             }else if(target < nums[mid]){
41                 right = mid - 1;
42             }else{
43                 return mid;
44             }
45         }
46 
47         // 都没找到, 返回-1
48         return -1;
49     }
50 }
leetcode 执行用时:4 ms > 15.28%, 内存消耗:37.8 MB > 92.01%

复杂度分析:

时间复杂度:O(logn)。三个循环,都是二分查找,所以时间复杂度为O(3logn), 在数组重复元素很多时,第一个循环查找旋转点因为无法判断当前元素属于前后哪个区间,使用rigiht-1来处理,所以可能退化成O(n)的复杂度,但是因为题目说明了数组元素不重复,所以这个算法的复杂度并不会退化成O(n)。 空间复杂度:O(1)。

思路二:只需一次二分查找

思路参考:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/duo-si-lu-wan-quan-gong-lue-bi-xu-miao-dong-by-swe/

 1 class Solution {
 2     public int search(int[] nums, int target) {
 3 
 4         int len = nums.length;
 5         // 二分法找到旋转点
 6         // int rotate = 0;
 7         int left = 0, right = len - 1, mid;
 8         while(left <= right){
 9             mid = (left + right) / 2;
10             if(nums[mid] == target){
11                 return mid;
12             }
13             if(nums[mid] > nums[right]){ // 说明mid在前半段区间中
14                 // 判断target在[left, mid)之间还是在[mid, right]之间
15                 if(target >= nums[left] && target < nums[mid]){
16                     right = mid - 1;
17                 }else{
18                     left = mid + 1;
19                 }
20             }else if(nums[mid] < nums[right]){  // 说明mid在后半段区间中
21                 // 判断target在[mid, right],之间还是在[left, mid]之间
22                 if(target > nums[mid] && target <= nums[right]){
23                     left = mid + 1;
24                 }else{
25                     right = mid;
26                 }
27             }else{
28                 right--;
29             }
30         }
31         // 都没找到, 返回-1
32         return -1;
33     }
34 }
leetcode 执行用时:0 ms > 100.00%, 内存消耗:37.8 MB > 90.36%

复杂度分析:

时间复杂度:O(logn)。只有一个二分查找的循环,所以时间复杂度为O(logn), 在数组重复元素很多时,查找旋转点因为无法判断当前元素属于前后哪个区间,使用rigiht-1来处理,所以可能退化成O(n)的复杂度,但是因为题目说明了数组元素不重复,所以这个算法的复杂度并不会退化成O(n)。 空间复杂度:O(1)。
上一篇:Spring Cloud【Finchley】- 20使用@RefreshScope实现配置的刷新


下一篇:树的文本实现