Leetcode 二分查找(一)

目錄

744. 尋找比目標字母大的最小字母

69. x 的平方根

475. 供暖器


 

744. 尋找比目標字母大的最小字母

https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/

給你一個排序後的字符列表 letters ,列表中只包含小寫英文字母。另給出一個目標字母 target,請你尋找在這一有序列表裏比目標字母大的最小字母。在比較時,字母是依序循環出現的。舉個例子:如果目標字母 target = 'z' 並且字符列表爲 letters = ['a', 'b'],則答案返回 'a'
示例:

輸入:letters = ["c", "f", "j"],target = "a",輸出: "c"

輸入:letters = ["c", "f", "j"],target = "c",輸出: "f"

輸入:letters = ["c", "f", "j"],target = "d",輸出: "f"

輸入:letters = ["c", "f", "j"],target = "g",輸出: "j"

輸入:letters = ["c", "f", "j"],target = "j",輸出: "c"

輸入:letters = ["c", "f", "j"],target = "k",輸出: "c"
提示:letters長度範圍在[2, 10000]區間內。letters 僅由小寫字母組成,最少包含兩個不同的字母。目標字母target 是一個小寫字母。

題解

一:簡化版,用一個臨時變量res記住候選答案,這樣比較簡單。

class Solution(object):
    def nextGreatestLetter(self, letters, target):
        """
        :type letters: List[str]
        :type target: str
        :rtype: str
        """
        l, r, res = 0, len(letters) - 1, -1 
        while l <= r:
            mid = l + (r - l) // 2
            if letters[mid] > target:
                r = mid - 1
                res = mid
            else:
                l = mid + 1
        if res == -1:
            return letters[0]
        return letters[res]

二:沒有用臨時變量,故這邊r賦值的是len(letters),這樣如果不存在,r就不會改變,也就不會是合法下標,便於後面的判斷。若存在,r必定是答案,這邊是在[l,r)中二分查找,mid = l + (r - l) // 2,也是確保了mid取到l且能取不到r。

class Solution(object):
    def nextGreatestLetter(self, letters, target):
        l, r = 0, len(letters) 
        while l < r:
            mid = l + (r - l) // 2
            if letters[mid] > target:
                r = mid 
            else:
                l = mid + 1
        if r == len(letters):
            return letters[0]
        return letters[r]

69. x 的平方根

https://leetcode-cn.com/problems/sqrtx/

實現 int sqrt(int x) 函數。計算並返回 x 的平方根,其中 x 是非負整數。由於返回類型是整數,結果只保留整數的部分,小數部分將被捨去。

示例 1:輸入: 4,輸出: 2
示例 2:輸入: 8,輸出: 2,說明: 8 的平方根是 2.82842..., 由於返回類型是整數,小數部分將被捨去。

題解

一:

class Solution(object):
    def mySqrt(self, x):
        """
        :type x: int
        :rtype: int
        """
        l, r, ans = 0, x, -1
        while l <= r:         
            mid = l + (r - l) // 2
            target = mid * mid 
            if target > x:
                r = mid - 1
            else:
                ans = mid
                l = mid + 1
        return ans

二:這邊解釋一下特例0,若x=0,則不進入循環,直接返回l(l初始化爲0)。這邊是在(l,r]中二分查找,mid = l + (r - l + 1) // 2,也是確保了mid取不到l且能取到r。

class Solution(object):
    def mySqrt(self, x):
        l, r = 0, x
        while l < r:
            mid = l + (r - l + 1) // 2
            target = mid * mid 
            if target == x:
                return mid 
            elif target > x :
                r = mid - 1
            else:
                l = mid 
        return l

475. 供暖器

https://leetcode-cn.com/problems/heaters/

冬季已經來臨。 你的任務是設計一個有固定加熱半徑的供暖器向所有房屋供暖。現在,給出位於一條水平線上的房屋和供暖器的位置,找到可以覆蓋所有房屋的最小加熱半徑。所以,你的輸入將會是房屋和供暖器的位置。你將輸出供暖器的最小加熱半徑。

說明:給出的房屋和供暖器的數目是非負數且不會超過 25000。給出的房屋和供暖器的位置均是非負數且不會超過10^9。只要房屋位於供暖器的半徑內(包括在邊緣上),它就可以得到供暖。所有供暖器都遵循你的半徑標準,加熱的半徑也一樣。
示例 1:輸入: [1,2,3],[2],輸出: 1。解釋: 僅在位置2上有一個供暖器。如果我們將加熱半徑設爲1,那麼所有房屋就都能得到供暖。
示例 2:輸入: [1,2,3,4],[1,4],輸出: 1。解釋: 在位置1, 4上有兩個供暖器。我們需要將加熱半徑設爲1,這樣所有房屋就都能得到供暖。

題解

一:先排序,然後對每一個house遍歷離他最近的熱水器。排序時間複雜度O(mlgm+nlgn),其中m是houses的規模,n是heaters的規模。由於排過序,後面的房屋的最近的熱水器必定是當前房屋最近的熱水器或者他後面的熱水器,故這一部分的時間複雜度是O(m+n),因爲內層循環的cur要麼向右移動要麼不變。

        """
        :type houses: List[int]
        :type heaters: List[int]
        :rtype: int
        """
        houses, heaters = sorted(houses), sorted(heaters)
        if not houses or not heaters:
            return 0 
        cur = 0
        res, n = 0, len(heaters)

        for house in houses:
            rec = abs(house - heaters[cur])
            while cur < n:
                follow = cur + 1
                if follow >= n:
                    break 
                tmp = abs(house - heaters[follow])
                if tmp > rec:
                    break 
                else:
                    rec = tmp 
                    cur = follow 
            res = max(res, rec)
        return res

二:時間複雜度O(nlgn + mlgn),m和n的定義同法一。房屋與熱水器的距離差,要麼單調減,要麼單調增,要麼先減後增,即最小值要麼在兩端取到,要麼則中間某處取到(極小點)。

class Solution(object):
    def findRadius(self, houses, heaters):
        heaters = sorted(set(heaters))
        if not houses or not heaters:
            return 0 
        res, n = 0, len(heaters)

        for house in houses:
            l, r, local_res = 1, n - 1, abs(house - heaters[0]) 
            while l < r:
                mid = l + (r - l) // 2
                a, am1, ap1 = (abs(house - heaters[mid]),abs(house - heaters[mid - 1])
                              ,abs(house - heaters[mid + 1]))
                if a <= ap1 and a <= am1:
                    local_res = a
                    break 
                elif a > am1:
                    r = mid  
                else:
                    l = mid + 1
            res = max(res, 
                      min(local_res, 
                          min(abs(house - heaters[0]), abs(house - heaters[-1]))))

        return res

三: 時間複雜度O(nlgn + mlgn),m和n的定義同法一。找到距離房屋最近的熱水器,由於l,r是往房屋逼近,故當不重合時,最近的要麼在r,要麼在l,要麼在l-1。

class Solution(object):
    def findRadius(self, houses, heaters):
        heaters = sorted(heaters)
        if not houses or not heaters:
            return 0 
        res, n = 0, len(heaters)

        for house in houses:
            l, r, local_res = 0, n, abs(house - heaters[0]) 
            while l < r:
                mid = l + (r - l) // 2
                if heaters[mid] == house:
                    local_res = 0 
                    break
                elif heaters[mid] < house:
                    l = mid + 1 
                else:
                    r = mid 
            if local_res == 0:
                continue
            if l < n:
                local_res = min(local_res, abs(house - heaters[l]))
            if l > 0:
                local_res = min(local_res, abs(house - heaters[l - 1]))
            if r < n:
                local_res = min(local_res, abs(house - heaters[r]))
            res = max(res, local_res)

        return res

287. 尋找重複數

https://leetcode-cn.com/problems/find-the-duplicate-number/

給定一個包含 n + 1 個整數的數組 nums,其數字都在 1 到 n 之間(包括 1 和 n),可知至少存在一個重複的整數。假設只有一個重複的整數,找出這個重複的數。

示例 1:輸入: [1,3,4,2,2],輸出: 2
示例 2:輸入: [3,1,3,4,2],輸出: 3
說明:不能更改原數組(假設數組是隻讀的)。只能使用額外的 O(1) 的空間。時間複雜度小於 O(n2) 。數組中只有一個重複的數字,但它可能不止重複出現一次。

題解

一:暴力解法,雙重循環,時間複雜度O(n^2),空間複雜度O(n)

二:哈希表,時間複雜度O(n),空間複雜度O(n^2)

三:轉自官方題解,https://leetcode-cn.com/problems/find-the-duplicate-number/solution/xun-zhao-zhong-fu-shu-by-leetcode-solution/,我們定義 cnt[i] 表示 ,nums[] 數組中小於等於 i 的數有多少個,假設我們重複的數是 target,那麼 [1,target−1]裏的所有數滿足 cnt[i]≤i(沒有重複值取等號,有重複值,必存在缺失值,若缺失值在前面則取小於,若不在前面,依舊等於),[target,n] 裏的所有數滿足 cnt[i]>i(因爲只有一個重複的),且具有單調性。我們只要找出第一個滿足 cnt[i]>i的i。

時間複雜度:O(nlogn),其中 n 爲 nums[] 數組的長度。二分查找最多需要二分 O(logn) 次,每次判斷的時候需要O(n) 遍歷 nums[] 數組求解小於等於 mid 的數的個數,因此總時間複雜度爲 O(nlogn)。
空間複雜度:O(1)。我們只需要常數空間存放若干變量。

class Solution(object):
    def findDuplicate(self, nums):
        n = len(nums)
        l, r = 1, n 
        while l < r:
            # 每次用到的時候跑一遍cnt。cnt表示小於等於i的數字的個數
            # 類似於cnt[i],只不過沒有提前跑,而是用到的時候跑。
            cnt, i = 0, l + (r - l) // 2 
            for num in nums:
                if num <= i:
                    cnt += 1
            if cnt <= i:
                l = i + 1
            else:
                r = i
        return r if r != n else -1

四:類似於有環鏈表的入口。

class Solution(object):
    def findDuplicate(self, nums):
        slow, fast = 0, 0
        while True:
            slow = nums[slow]
            fast = nums[nums[fast]]
            if fast == slow:
                break 
        slow = 0 

        while True:
            slow = nums[slow]
            fast = nums[fast]
            if slow == fast:
                return slow

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章