1. 程式人生 > 實用技巧 >Java實現多執行緒的三種方式(3) ------實現Callable<V>介面

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]

  1. 合法區間
    把問題轉換為對合法區間的切分,在尋找左端點的情況下,相當於<target為合法,>=target為不合法。當mid值小於target時,說明此時l到mid都是合法的(包括mid)因此將l右移縮小範圍。當mid大於等於target時,說明l到mid不合法,可以捨棄,使r = mid-1(即一個有可能的合法值)。

  2. 邊界問題
    二分到最後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。

  3. 若查詢失敗
    如果停在了陣列內部,那麼找到的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};
  1. 合法區間
    在轉移的時候r=mid,而不是mid-1,因為此時mid已經驗證不合法,然後r剛好是一個不合法值。感覺就是l代表合法陣營,r代表不合法陣營,在搜尋他們那個分界點,更直觀?

  2. 邊界問題
    二分到最後l=r-1,(l+r)/2=l=mid,此時

    • 若l合法:l+1,最終l不合法了
    • 若l不合法:r=l,l不變,l依舊不合法。

    此時l=r=第一個target。

  3. 若查詢失敗
    如果停在了陣列內部,nums[l]!=target。
    如果全部數字都大於target,依舊可以用nums[l]!=target。
    如果全部數字小於target此時l應該越界了,判斷是否等於n即可。

實際上可以假裝nums[-1]的座標有一個-inf,nums[n]有一個inf,就不用判斷邊界辣