zoukankan      html  css  js  c++  java
  • 【LeetCode】有序旋转数组的查找(4)

      有序旋转数组是指将有序数组向左或者向右移动k个位置得到的结果,其查找算法不难理解,因为局部有序,因此很容易想到二分查找是最合适的方法,时间复杂度O(nlogn),本文总结四道相关的算法题目。

    (一)旋转数组

    题目:189. 旋转数组

    题目描述:

      给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。

    示例 1:

    	输入: [1,2,3,4,5,6,7] 和 k = 3
    	输出: [5,6,7,1,2,3,4]
    
    	解释:
    	向右旋转 1 步: [7,1,2,3,4,5,6]
    	向右旋转 2 步: [6,7,1,2,3,4,5]
    	向右旋转 3 步: [5,6,7,1,2,3,4]
    

    解题思路:三次反转

      实际上本题不难想到解答,也是在面试中很常见的一个题目,实际上它类似于【剑指Offer】43、左旋转字符串,对于左旋转,可以分为前k个元素和其他元素,而右旋转可以分为后k个元素和前面的其他元素,仍然可以借鉴三次反转的算法来实现。

    代码思路:

    class Solution {
        public void rotate(int[] nums, int k) {
            //右移k位,三次反转
            if(nums==null || nums.length==0 || k==0)
                return ;
            int len=nums.length;
            k=k%len;
    
            reverse(nums,len-k,len-1);
            reverse(nums,0,len-k-1);
            reverse(nums,0,len-1);
        }
        //数组反转
        public void reverse(int[] nums,int begin,int end){
            int i=begin,j=end;
            while(i<j){
                int temp=nums[i];
                nums[i]=nums[j];
                nums[j]=temp;
                i++;
                j--;
            }
        }
    }
    

    (二)有序数组的查找

      在有序数组中查找一个特定的值target,这就是典型的二分查找算法,其过程为:

    • 若 target == nums[mid],直接返回
    • 若 target < nums[mid],则 target 位于左侧区间 [left,mid) 中。令 right = mid-1,在左侧区间查找
    • 若 target > nums[mid],则 target 位于右侧区间 (mid,right] 中。令 left = mid+1,在右侧区间查找
        public boolean search(int[] nums, int target) {
            if(nums==null || nums.length==0)
                return false;
            int low=0,high=nums.length-1;
            while(low<=high){
                int mid=low+(high-low)/2;
                if(nums[mid]==target)
                    return true;
                else if(target<nums[mid]){ 
                    high=mid-1;
                }else{
                    low=mid+1;
                }
            }
            return false;
        }
    

    (三)搜索旋转排序数组

    题目:33. 搜索旋转排序数组

    题目描述:

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

      搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。你的算法时间复杂度必须是 O(log n) 级别。

    示例:

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

    解题思路:二分查找

      对于本题,二分查找是自然而然可以想到的思路,特别是更要求算法时间复杂度是O(logn),那几乎二分查找就成为了唯一的选择。但是,由于本题并不是完全有序,而是经过旋转后的局部有序

      因此,对于这种局部有序的数组,我们可以观察到一个特点:从任一位置进行分隔,则必有其中一半是有序的。因此,我们可以仍然从中点位置进行分隔,然后通过将最左边元素nums[low]和nums[mid]进行比较,从而可以判断出来,是左侧还是右侧是连续有序的,进而可以选择接下来继续选择哪一边进行继续查找。

    1、若 target == nums[mid],直接返回

    2、若 nums[left] <= nums[mid],说明左侧区间 [left,mid]「连续递增」。此时:

      若 nums[left] <= target <= nums[mid],说明 target 位于左侧。令 right = mid-1,在左侧区间查找

      否则,令 left = mid+1,在右侧区间查找

    3、否则,说明右侧区间 [mid,right]「连续递增」。

      此时:
    若 nums[mid] <= target <= nums[right],说明 target 位于右侧区间。令 left = mid+1,在右侧区间查找

      否则,令 right = mid-1,在左侧区间查找

    代码实现:

    class Solution {
        public int search(int[] nums, int target) {
            if(nums==null || nums.length==0)
                return -1;
            int low=0,high=nums.length-1;
            while(low<=high){
                int mid=low+(high-low)/2;
                if(nums[mid]==target)
                    return mid;
                else if(nums[low]<=nums[mid]){  //左侧连续
                    if(target<nums[mid] && target>=nums[low])
                        high=mid-1;
                    else
                        low=mid+1;
                }else{      //右侧连续
                    if(target>nums[mid] && target<=nums[high])
                        low=mid+1;
                    else
                        high=mid-1;
                }
            }
            return -1;
        }
    }
    

    (四)搜索旋转排序数组II

    题目:81. 搜索旋转排序数组 II

    题目描述:

      假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。

    示例 :

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

    解题思路:

      本题是上一题的延续,其区别在于数组中是否包含重复元素,当数组中允许元素重复时,如果nums[low]和nums[mid]是相等的,那我们就无法判断究竟是哪一边连续有序,这时我们可以让指针只移动一位,相当于排除一个元素。其余和上题基本相同。

    代码实现:

    class Solution {
        public boolean search(int[] nums, int target) {
            if(nums==null || nums.length==0)
                return false;
            
            int low=0,high=nums.length-1;
            while(low<=high){
                int mid=low+(high-low)/2;
                if(nums[mid]==target)
                    return true;
                if(nums[low]==nums[mid]){
                    low++;
                }else if(nums[low]<nums[mid]){  //左边有序
                    if(target>=nums[low] && target<nums[mid])
                        high=mid-1;
                    else
                        low=mid+1;
                }else{ //右边有序
                    if(target>nums[mid] && target<=nums[high])
                        low=mid+1;
                    else
                        high=mid-1;
                }
            }
            return false;
        }
    }
    

    总结:

      本文总结了关于旋转排序数组的四道题目,重点是二分查找算法的变形应用,是一个重要的算法知识点。

  • 相关阅读:
    项目实战之天天酷跑(三):缓冲加载游戏界面
    项目实战之天天酷跑(二):开始游戏界面
    项目实战之天天酷跑(一):登录界面
    DBeaver安装
    基于SpringBoot+Vue开发的前后端分离博客项目完整教学
    freemarker教程
    ftl 常用指令及语法详解
    FTL 入门
    IDEA右侧Maven消失解决方法
    nodejs和npm的关系
  • 原文地址:https://www.cnblogs.com/gzshan/p/12570332.html
Copyright © 2011-2022 走看看