每天一道leetcode-81

  • 2019 年 10 月 4 日
  • 筆記

前言

目前準備每天刷一道題leetcode的題目,以此來激勵那些零基礎入門的人,千萬不要被什麼科班和非科班的說法嚇倒了,電腦這個行業只要你肯努力,沒有什麼逾越不了的鴻溝。

只要你肯努力,一份努力,一份汗水,在程式設計師這個職業,你的每一份付出都會得到對應的那一份彙報,尊重學習規律,循序漸進,別想著一口吃個胖子,羅馬也不是一天建成的,有朝一日,你終會變成你想成為的人。

題目目前可能需要一定的演算法與數據結構基礎才能看懂,後序會寫一下零基礎也能看懂的入門知識,然後就可以看懂我編寫的題目了~

案例

題目

leetcode-81 在有重複數字的有序數組中尋找n

tag(分類): 二分查找這一類

鏈接:

https://leetcode.com/problems/search-in-rotated-sorted-array-ii/

題目詳述

假設按照升序排序的數組在預先未知的某個點上進行了旋轉。

( 例如,數組 [0,0,1,2,2,5,6] 可能變為 [2,5,6,0,0,1,2] )。

編寫一個函數來判斷給定的目標值是否存在於數組中。若存在返回 true,否則返回 false

示例 1:

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

示例 2:

輸入: nums = [2,5,6,0,0,1,2], target = 3輸出: false

進階:

  • 這是 搜索旋轉排序數組 的延伸題目,本題中的 nums 可能包含重複元素。
  • 這會影響到程式的時間複雜度嗎?會有怎樣的影響,為什麼?

題解

先上程式碼~

class Solution {      public boolean search(int[] nums, int target) {          int left = 0;          int right = nums.length - 1;          while(left <= right)          {              int mid = left + (right-left)/2;              if(nums[mid] == target || nums[left] == target || target == nums[right])                  return true;              if(nums[mid] > nums[left])              {                  if(target > nums[mid])                       left = mid + 1;                  else{                      if(target > nums[left])                          right = mid - 1;                      else                          left = mid + 1;                  }              }else if(nums[mid] < nums[left])              {                  if(target < nums[mid])                      right = mid -1;                  else{                      if(target > nums[left])                          right = mid - 1;                      else                          left = mid + 1;                  }              }else{                  left++;//right--;              }          }          return false;      }  }  

上一篇題目:每天一道leetcode-33

本道題目是對上一篇題目的加強版,在有序數組的中加入了有重複數字這一條件,題目變得稍微難了一些些,思路也是很類似的,就是分情況討論。

對於這種題目,數組,有序這兩個字眼結合起來,就是要第一時間考慮到二分查找。

先舉個例子,假設有旋轉數組[3,3,3,4,5,6,1,2,3,3],以圖形化的方式表示出這個數組,3,3,3,4,5,6為前半段,1,2,3,4為後半段,後續的圖不是這個數組。

思路:由於無法確定target,nums[mid]誰大誰小,以及target和nums[mid]到底在前半段,還是在後一段,不同的情況下,對於令left = mid+1還是right = mid-1是不一樣的情況,所以這裡分情況進行討論,把所有的情況討論一遍,然後就知道,到底是變化left還是變化right,進而去縮小這個區間,找到我們要找到的數,在這裡唯一不同的就是多了一種nums[mid]與nums[left]如果相等了的情況,該怎麼處理。

nums[mid] >nums[left]

第10行,nums[mid]>nums[left]判斷為真,說明nums[mid]在前半段。如下圖所示

target>nums[mid]

緊接著到了12行,target>nums[mid],看圖得知,target只能是在前半段。

所以觀察得知,令left=mid+1往target去逼近。

target<nums[mid]

當12行程式碼判斷為假,也就是要進入到14行。

而target小於nums[mid]時候,由於不知道target是在前半段還是後半段,所以的分情況討論。

target<nums[mid]且target在前半段

當target在前半段,也就是程式碼第15行,target>nums[left],如下圖所示。

觀察圖可知,16行,明顯應該令right=mid-1,right減小去往target逼近。

target<nums[mid]且target在後半段

當target在後半段,也就是程式碼15行判斷為假,也就是nums[left]>target,進入到18行。

觀察圖可知,明顯應該是left=mid+1,來往target去逼近。

到這,10-20行程式碼解讀完畢。

nums[mid]<nums[left]

當第10行程式碼判斷為假,也就是nums[mid]<nums[left],nums[mid]落入到後半段,此時進入到程式碼21這個分支。如下圖所示。

由於不知道target與nums[mid]的關係得分情況討論。

target<nums[mid]

當target<nums[mid]時候,也就22行程式碼,觀察上圖得知,target只能在後半段,如下圖所示。

明顯應該是right=mid-1,來逼近taeget. ,23行程式碼所示。

target>nums[mid]

當22行程式碼判斷為假,進入到25行程式碼,也就是target大於nums[mid]。

當target大於nums[mid]的時候,target可以在前半段大於nums[mid],也可以在後半段大於nums[mid],所以得分兩種情況討論。

target>nums[mid]且target在前半段

target在前半段,也就是程式碼25行,target>nums[mid]

如上圖所示,明顯令right=mid-1去逼近target,也就是程式碼26行。

target>nums[mid]且target在後半段

target在後半段,也就是程式碼25行判斷為假,target<nums[left],進入到28行.

觀察圖得知,left=mid+1;

nums[mid]=nums[left]

當著二者相等的時候,無法討論,這是為啥呢?

這裡舉個例子,幫助大家理解。

對於數組

[3,3,3,3,4,5,6,7],nums[mid]=nums[left]對吧,這時候如果要找6,target=6,那麼應該是left=mid+1;往6去逼近。

而對於數組

[3,3,3,3,4,5,6,7,1,2,3,3,3,3,3,3,3,3,3,3,3,3],nums[mid]=nums[left]也是相等的對吧,但是這是候去找6,就應該是right=mid-1,去逼近這個6。

所以對於這種nums[mid]=nums[left]的情況你無法去確定nums[mid]是在前半段還是後半段沒所以也就無法討論。

所以只能是一次移動一個去逼近target,也就是程式碼31行,left++;或者right–都可以去去逼近target。

結束語

總體來說思路與leetcode31這道理差不多,但是多了一個nums[mid]=nums[left]的情況,把這個情況單獨拿出來談論即可。

END