Java實現多執行緒的三種方式(3) ------實現Callable<V>介面
該面對的總是要面對
LeetCode34. 在排序陣列中查詢元素的第一個和最後一個位置
題意是,在某個排序數組裡找到target的第一次出現和最後一次出現。
有兩種解法
第一種 [l,r]
while(l<=r) { int mid = (l+r)/2; if(nums[mid]<target) l = mid+1; else r = mid-1; } a[0] = l; if((a[0]==nums.size())||nums[a[0]]!=target) a[0] = -1;
由於 [l,r]
-
合法區間
把問題轉換為對合法區間的切分,在尋找左端點的情況下,相當於<target為合法,>=target為不合法。當mid值小於target時,說明此時l到mid都是合法的(包括mid)因此將l右移縮小範圍。當mid大於等於target時,說明l到mid不合法,可以捨棄,使r = mid-1(即一個有可能的合法值)。 -
邊界問題
二分到最後l=r,(l+r)/2=l=mid,此時- 若合法:l=mid+1,結果就是l成為了不合法的那一個(因為mid也就是r合法)
- 若不合法:r=mid-1,l不變,l依舊不合法。
此時l=r+1。找到的最終合法值是r,在此題中就是第一個target的左邊那一個,所以理論上l應該是第一個target。
-
若查詢失敗
如果停在了陣列內部,那麼找到的r依舊是比target小一點的那一個,但nums[l]!=target。
如果全部數字都大於target,依舊可以用nums[l]!=target。
如果全部數字小於target此時l應該越界了,判斷是否等於n即可。
對右邊端點的查詢同理,合法區間變為<=target和>target,最終l非法r合法。找到的最大一個target是r,l是比target大一點點那個。
l = 0, r = nums.size()-1; while(l<=r) { int mid = (l+r)/2; if(nums[mid]<=target) l = mid+1; else r = mid-1; } a[1] = r; if(l==0||nums[a[1]]!=target) a[1] = -1;
查詢失敗的情況有所不同,因為最終判斷合法是通過判斷nums[pos]?=target,所以在這裡應該用r來判斷,r超出陣列範圍的情況就是r=-1,l=0。
第二種 [l,r)
在區間選擇上,天生l合法r不合法,因此當l=r(衝突)時退出迴圈。
int l = 0, r = n;
while (l < r) {
int mid = (l + r) / 2;
if (nums[mid] < target) l = mid + 1;
else r = mid;
}
if (r == n || nums[r] != target) return {-1, -1};
-
合法區間
在轉移的時候r=mid,而不是mid-1,因為此時mid已經驗證不合法,然後r剛好是一個不合法值。感覺就是l代表合法陣營,r代表不合法陣營,在搜尋他們那個分界點,更直觀? -
邊界問題
二分到最後l=r-1,(l+r)/2=l=mid,此時- 若l合法:l+1,最終l不合法了
- 若l不合法:r=l,l不變,l依舊不合法。
此時l=r=第一個target。
-
若查詢失敗
如果停在了陣列內部,nums[l]!=target。
如果全部數字都大於target,依舊可以用nums[l]!=target。
如果全部數字小於target此時l應該越界了,判斷是否等於n即可。
實際上可以假裝nums[-1]的座標有一個-inf,nums[n]有一個inf,就不用判斷邊界辣