動態維護集合第k小數字 樹狀陣列 二分

2021-10-08 05:17:36 字數 1748 閱讀 3859

設計一種資料結構能夠實現以下幾個操作

在結構中插入乙個數字x

在結構中刪除乙個數字x(如果沒有該數字,應當不操作)

查詢結構中的第k小數字(相同數字應當累計,如 1 5 3 3 1,第 3 大的數字是 3, 第 5 大的數字是 5.

其中1 <= x <= 100000

一秒需要執行100000次隨機操作,所以三項操作時間複雜度應當不超過log級。

插入乙個數字x就是令陣列的第x位置 + 1,實際上就是個桶排序。

刪除乙個數字就是令陣列的第x位置 - 1,如果本來就是 0 了就不操作。

查潤第 k 小數字,我們只需要從 1 開始列舉個數cnt,直到 cnt 第一次大於等於 k 那麼作為答案輸出。

在上面的思路中可以看到,插入和刪除都是o(1)的,但是查詢操作是o(n)的。可以可能就會有乙個**的資料,插入乙個數字100000然後查詢99999次第1小的數字,那麼時間複雜度就會是o(n2),然後就會tle 。

不過可以看到實際上第三個步驟就是一直在求前n項的和,所以選擇用樹狀陣列(線段樹)維護該項操作,使得查詢某一段的數字的個數變成o(logn),比如我要快速知道0 ~ 1000 元素的個數是多少個,只需要o(log)運算(10次),而原先需要o(n)運算(1000次列舉)。代價是插入和刪除的時間複雜度都會變成o(logn)灑灑水啦 。

最後,在查詢的是時候使用二分設定左邊界為0,右邊界為n,先看看0 ~ n之間的個數,如果小於 k 直接返回 -1 表示查詢不合法,如果大於 k ,說明這部分元素太多了,求出 mid = (l + r) / 2,求得 0 ~ mid 之間有多少個數字,如果大於 k 再往左邊( r = mid)查詢, 否則說明數字個數比較少了,往右邊(l = mid + 1)查詢,直到 l >= r 結果收斂,把 l 作為結果返回。

向集合中插入乙個元素的最壞時間複雜度為logn(樹狀陣列修改)

刪除集合中乙個元素的最壞時間複雜度為logn(樹狀陣列修改)

查詢集合中第k小元素的最壞時間複雜度為logn * log n(二分 + 樹狀陣列查詢)

#include

using

namespace std;

const

int n =

100010

;/****

設計一種資料結構能夠實現以下幾個操作

向集合中插入乙個數字x

向集合中刪除乙個數字x(如果沒有該數字,應當不操作)

查詢集合中的第k小數字(相同數字應當累計,如 1 5 3 3 1,第 3 大的數字是 3, 第 5 大的數字是 5.

其中 1 <= x <= 100000

一秒需要執行100000次隨機操作,所以三項操作時間複雜度應當不超過log級

***/

int tr[n]

;void

init()

intlowbit

(int x)

void

add(

int x,

int k)

intquery

(int x)

void

insert

(int x)

intdel

(int x)

return

false;}

intkth

(int k)

return l;

}int

main()

字典序的第K小數字

給定整數 n 和 k,找到 1 到 n 中字典序第 k 小的數字。1 k n 109。n 10時,字典序 1,10,2,3,4,5,6.可以把這n個數字看成字典樹,那麼每乙個字首都代表乙個數字,其中每個節點有10個兒子,0 9.那麼字典樹的先序遍歷的第k個就是答案。所以得到了乙個o k 的做法。這類...

第 K 小數字問題 (優先佇列實現)

首先了解優先佇列 可以理解優先佇列就是乙個含有內部排序的佇列,具有佇列的一切屬性,本質上是通過堆來實現的。定義語法 priority queue type 是資料型別 注意 資料元素的型別可以是pair型別 container 由陣列實現的一些資料結構,如 vector,deque,不可以是list...

廈門1165 第K小數 樹狀陣列 不重複數字

1165.第k小數 time limit 1000 ms memory limit 65536 k total submissions 467 117 users accepted 117 66 users my solution description 給定乙個空的正整數集合s,然後再給定一組操作...