【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;
    }
}

總結:

  本文總結了關於旋轉排序數組的四道題目,重點是二分查找算法的變形應用,是一個重要的算法知識點。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM