每天一道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