【每日算法】兩種二分基本思路(附二分目錄) |Python 主題月

本文正在參加「Python主題月」,詳情查看 活動連接html

題目描述

這是 LeetCode 上的 275. H 指數 II ,難度爲 中等git

Tag : 「二分」github

給定一位研究者論文被引用次數的數組(被引用次數是非負整數),數組已經按照 升序排列 。編寫一個方法,計算出研究者的 h 指數。算法

h 指數的定義: 「h 表明「高引用次數」(high citations),一名科研人員的 h 指數是指他(她)的 (N 篇論文中)總共有 h 篇論文分別被引用了至少 h 次。(其他的 N - h 篇論文每篇被引用次數很少於 h 次。)"數組

示例:markdown

輸入: citations = [0,1,3,5,6]

輸出: 3 

解釋: 給定數組表示研究者總共有 5 篇論文,每篇論文相應的被引用了 0, 1, 3, 5, 6 次。
     因爲研究者有 3 篇論文每篇至少被引用了 3 次,其他兩篇論文每篇被引用很少於 3 次,因此她的 h 指數是 3。
複製代碼

說明:app

若是 h 有多有種可能的值 ,h 指數是其中最大的那個。less

 

進階:ide

  • 這是 H 指數 的延伸題目,本題中的 citations 數組是保證有序的。
  • 你能夠優化你的算法到對數時間複雜度嗎?

基本分析

本題與 274. H 指數 的主要不一樣有兩方面:函數

  1. 數據範圍不一樣:切換成英文能夠發現,在 274. H 指數 n n 的範圍爲 5000 5000 ,而本題 n n 的範圍爲 1 0 5 10^5
  2. 給定數組是否有序:在 274. H 指數 中數組不必定有序,本題則是有序。

顯然,增長了數組有序特性,擴大了數據範圍。能夠猜到利用此特性,存在時間複雜度更低的算法實現。

二分答案(線性 check

(題解) 274. H 指數 中,咱們使用了 O ( n log n ) O(n\log{n}) 的二分作法,算法的主要瓶頸在於 O ( n ) O(n) 複雜度的 check

固然對於 1 0 5 10^5 的數據量,使用 O ( n log n ) O(n\log{n}) 複雜度沒有任何問題。

Java 代碼:

class Solution {
    public int hIndex(int[] cs) {
        int n = cs.length;
        int l = 0, r = n;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (check(cs, mid)) l = mid;
            else r = mid - 1;
        }
        return r;
    }
    boolean check(int[] cs, int mid) {
        int ans = 0;
        for (int i : cs) if (i >= mid) ans++;
        return ans >= mid;
    }
}
複製代碼

Python 3 代碼:

class Solution:
    def hIndex(self, citations: List[int]) -> int:
        def check(cs, mid):
            return sum(i>=mid for i in cs) >= mid

        n = len(citations)
        l, r = 0, n
        while l < r:
            mid = l + r + 1 >> 1
            if check(citations, mid):
                l = mid
            else:
                r = mid - 1
        return r
複製代碼
  • 時間複雜度:對 [ 0 , n ] [0, n] 作二分,複雜度爲 O ( log n ) O(\log{n}) check 函數須要對數組進行線性遍歷,複雜度爲 O ( n ) O(n) 。總體複雜度爲 O ( n log n ) O(n\log{n})
  • 空間複雜度: O ( 1 ) O(1)

二分下標(根據與 c i t a t i o n s [ i ] citations[i] 關係)

在解法一中,顯然咱們沒有利用本題的「數組有序」的特性。

根據對 H 指數 定義,若是 c i t a t i o n s citations 升序,在最大的符合條件的分割點 x x 的右邊(包含分割點),必然知足 c i t a t i o n s [ i ] > = x citations[i] >= x ,咱們應當對其進行計數,對於分割點的左邊,必然不知足 c i t a t i o n s [ i ] > = x citations[i] >= x ,無需進行計數。

所以,咱們能夠利用 分割點右邊書的個數與分割點 c i t a t i o n s [ x ] citations[x] 的大小關係進行二分

假設存在真實分割點下標 x x ,其值大小爲 c i t a t i o n s [ x ] citations[x] ,分割點右邊的數值個數爲 n x n - x ,根據 H 指數 的定義,必然有 c i t a t i o n s [ x ] > = n x citations[x] >= n - x 關係:

  • 在分割點 x x 的右邊: c i t a t i o n s [ i ] citations[i] 非嚴格單調遞增,而書的個數嚴格單調遞減,仍然知足 c i t a t i o n s [ i ] > = n i citations[i] >= n - i 關係;
  • 在分割點 x x 的左邊: c i t a t i o n s [ i ] citations[i] 非嚴格單調遞減,書的個數嚴格單調遞增, x x 做爲真實分割點,所以必然不知足 c i t a t i o n s [ i ] > = n i citations[i] >= n - i 關係。

利用此「二段性」進行二分便可,二分出下標後,再計算出書的個數。

Java 代碼:

class Solution {
    public int hIndex(int[] cs) {
        int n = cs.length;
        int l = 0, r = n - 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (cs[mid] >= n - mid) r = mid;
            else l = mid + 1;
        }
        return cs[r] >= n - r ? n - r : 0;
    }
}
複製代碼

Python 3 代碼:

class Solution:
    def hIndex(self, citations: List[int]) -> int:
        n = len(citations)
        l, r = 0, n - 1
        while l < r:
            mid = l + r >> 1
            if citations[mid] >= n - mid:
                r = mid
            else:
                l = mid + 1
        return n - r if citations[r] >= n - r else 0
複製代碼
  • 時間複雜度: O ( log n ) O(\log{n})
  • 空間複雜度: O ( 1 ) O(1)

其餘「二分」相關內容

題目 題解 難度 推薦指數
4. 尋找兩個正序數組的中位數 LeetCode 題解連接 困難 🤩🤩🤩🤩
29. 兩數相除 LeetCode 題解連接 中等 🤩🤩🤩
33. 搜索旋轉排序數組 LeetCode 題解連接 中等 🤩🤩🤩🤩🤩
34. 在排序數組中查找元素的第一個和最後一個位置 LeetCode 題解連接 中等 🤩🤩🤩🤩🤩
35. 搜索插入位置 LeetCode 題解連接 簡單 🤩🤩🤩🤩🤩
74. 搜索二維矩陣 LeetCode 題解連接 中等 🤩🤩🤩🤩
81. 搜索旋轉排序數組 II LeetCode 題解連接 中等 🤩🤩🤩🤩
153. 尋找旋轉排序數組中的最小值 LeetCode 題解連接 中等 🤩🤩🤩
154. 尋找旋轉排序數組中的最小值 II LeetCode 題解連接 困難 🤩🤩🤩
220. 存在重複元素 III LeetCode 題解連接 中等 🤩🤩🤩
274. H 指數 LeetCode 題解連接 中等 🤩🤩🤩
278. 第一個錯誤的版本 LeetCode 題解連接 簡單 🤩🤩🤩🤩
354. 俄羅斯套娃信封問題 LeetCode 題解連接 困難 🤩🤩🤩
363. 矩形區域不超過 K 的最大數值和 LeetCode 題解連接 困難 🤩🤩🤩
374. 猜數字大小 LeetCode 題解連接 簡單 🤩🤩🤩
778. 水位上升的泳池中游泳 LeetCode 題解連接 困難 🤩🤩🤩
852. 山脈數組的峯頂索引 LeetCode 題解連接 簡單 🤩🤩🤩🤩🤩
981. 基於時間的鍵值存儲 LeetCode 題解連接 中等 🤩🤩🤩🤩
1004. 最大連續1的個數 III LeetCode 題解連接 中等 🤩🤩🤩
1011. 在 D 天內送達包裹的能力 LeetCode 題解連接 中等 🤩🤩🤩🤩
1208. 儘量使字符串相等 LeetCode 題解連接 中等 🤩🤩🤩
1438. 絕對差不超過限制的最長連續子數組 LeetCode 題解連接 中等 🤩🤩🤩
1482. 製做 m 束花所需的最少天數 LeetCode 題解連接 中等 🤩🤩🤩
1707. 與數組中元素的最大異或值 LeetCode 題解連接 困難 🤩🤩🤩
1751. 最多能夠參加的會議數目 II LeetCode 題解連接 困難 🤩🤩🤩

最後

這是咱們「刷穿 LeetCode」系列文章的第 No.275 篇,系列開始於 2021/01/01,截止於起始日 LeetCode 上共有 1916 道題目,部分是有鎖題,咱們將先把全部不帶鎖的題目刷完。

在這個系列文章裏面,除了講解解題思路之外,還會盡量給出最爲簡潔的代碼。若是涉及通解還會相應的代碼模板。

爲了方便各位同窗可以電腦上進行調試和提交代碼,我創建了相關的倉庫:github.com/SharingSour…

在倉庫地址裏,你能夠看到系列文章的題解連接、系列文章的相應代碼、LeetCode 原題連接和其餘優選題解。

相關文章
相關標籤/搜索