線段樹離散化 unique + 二分查找 模板 (轉載)

離散化,把無限空間中有限的個體映射到有限的空間中去,以此提升算法的時空效率。
通俗的說,離散化是在不改變數據相對大小的條件下,對數據進行相應的縮小。例如:
原數據:1,999,100000,15;處理後:1,3,4,2;
原數據:{100,200},{20,50000},{1,400};
處理後:{3,4},{2,6},{1,5};算法

離散化是程序設計中一個經常使用的技巧,它能夠有效的下降時間複雜度。其基本思想就是在衆多可能的狀況中,只考慮須要用的值。離散化能夠改進一個低效的算法,甚至實現根本不可能實現的算法。要掌握這個思想,必須從大量的題目中理解此方法的特色。例如,在建造線段樹空間不夠的狀況下,能夠考慮離散化。數組

數據的離散化數據結構

有些數據自己很大, 自身沒法做爲數組的下標保存對應的屬性。若是這時只是須要這堆數據的相對屬性, 那麼能夠對其進行離散化處理。當數據只與它們之間的相對大小有關,而與具體是多少無關時,能夠進行離散化。
例 1:
91054 與 52143的逆序對個數相同。
例 2:
設有4個數: 123456七、12345678九、1234567八、123456
排序:123456<1234567<12345678<123456789
=>1<2<3<4
那麼這4個數能夠表示成:二、四、三、1
例 3:
好比給你n個數:98998988,32434234,433234556,32434234,8384733,……
讓你統計其中每一個數出現的次數,傳統的作法有好幾種,好比一遍一遍的掃過去,比對疊加,這樣算法的效率是O(n2),效率低下;
再好比先排序,再統計連續的相同的個數,這裏的效率已經有所提升了,不過假如上面的數據是一道線段樹的題目給出的數據,那麼建樹須要的空間開銷實在是太大了。
再改進一下,採用哈希的方法,開一個大於其中最大數的數組並初始化爲零,O(n)掃一下,在該數字對應的下標的元素上+1,若是對於比較小的數字還好說,可是對於上面出現的數字直接採用哈希對空間的開銷是十分大的也是沒有必要的,因此這裏用到了數據的離散化。
首先將數字排序:32434234,32434234,43324556,8384733,98998988
去重後給予其對應的索引: 0,0,1,2,3 (一一映射)
分別對應每一個數,就能夠簡化不少操做,減小了不少沒必要要的資源開銷。
除了對於較大整數須要使用離散化以外,對於一些須要使用整型數據結構,但給出的數據倒是小數的也可使用離散化,將其索引爲整數就能夠了。函數

那麼能夠總結出離散化的步驟:spa

一、排序.net

二、去重設計

三、索引
爲了簡化代碼,咱們採用STL算法離散化:code

/*
用離散化以前先用 sort()排序,再用 unique() 進行去重
用 lower_bound() 或者 upper_bound() 進行二分查找位置
*/
int a[n], b[n], sub[n];
// a[n]是即將被離散化的數組,b[n]是a[n]的副本,sub用於排序去重後提供離散化後的值
sort(sub, sub + n);
int size = unique(sub, sub + n) - sub;
for(int i = 0; i < n; i++)
    a[i] = lower_bound(sub, sub + size, a[i]) - sub;
 //即a[i]爲b[i]離散化後對應的值

  

一、unique()函數————返回值是去重以後的長度blog

unique() 的做用是「去掉」容器中相鄰元素的重複元素(不必定要求數組有序),即去重
它會把重複的元素添加到容器末尾(因此數組大小並無改變),而返回值是去重以後的尾地址
若是要刪去重複元素,能夠把尾巴刪去便可(或者直接定義新的長度!)
例如:排序

sz=unique(b+1,b+n+1)-(b+1);//減去的(b+1) 及 a  是起始地址
sz=unique(a,a+n)-a;

二、二分查找——lower_bound()、upper_bonud()

/*
upper_bound(i) 返回的是鍵值爲i的元素能夠插入的最後一個位置(上界)
lowe_bound(i) 返回的是鍵值爲i的元素能夠插入的位置的第一個位置(下界)。
*/

怎麼理解呢,舉例:
在升序的set裏面
set裏沒有元素i的時候,兩個元素的返回值是同樣的。
1 2 4 5 這個序列,upp(3)和low(3)都返回位置2(下標)

若是隻有一個元素i,low返回那個元素的位置,而upp返回那個元素的位置的後一個位置。
1 2 4 5 這個序列upp(2)返回下標2而low(2)返回下標1

多個元素i,low返回那個元素的位置,upp返回那多個元素中的最後一個的後一個位置。
1 2 2 4 5 這個序列 upp(2)返回下標3的位置,low(2)返回下標1的位置。

!!!!!!!!!!!!!
特別注意:舉例在一個升序的容器裏,若是全部元素都大於i則,upp和low都返回begin。都小於i則返回end(越界了)。

最後再來一句,看是否好理解一些。

terator lower_bound( const key_type &key ): 返回一個迭代器,指向鍵值>= key的第一個元素。
iterator upper_bound( const key_type &key ):返回一個迭代器,指向鍵值<=key的最後一個元素的後一個元素。
★降序排列的容器:
iterator lower_bound( const key_type &key ): 返回一個迭代器,指向鍵值<= key的第一個元素。
iterator upper_bound( const key_type &key ):返回一個迭代器,指向鍵值>=key的最後一個元素的後一個元素。
例如:

bool cmp(int a,int b)
{
    return a<b;
}
int main()
{
    int a[10]={2,7,1,4,4,6};
    sort(a,a+6,cmp);        // 去重以前先排序
    int m=unique(a,a+6)-a; // 去重
    cout<<m<<endl;        // 輸出去重以後的長度
    for(int i=0;i<m;i++)
        cout<<a[i]<<' '; // 輸出去重以後的數
    cout<<endl;
    int tem=upper_bound(a,a+6,4)-a;
    //按從小到大 4 最多能插入數組 a 的哪一個位置
    int p=lower_bound(a,a+6,4)-a;
    //按從小到大,4最少能插入數組 a  的哪一個位置
    cout<<tem<<endl;
    cout<<p<<endl;
}
 
輸出
5
1 2 4 6 7
3 
2

  

 

--------------------- 本文來自 __zcy 的CSDN 博客 ,全文地址請點擊:https://blog.csdn.net/zcy19990813/article/details/81141035?utm_source=copy 

相關文章
相關標籤/搜索