1. 程式人生 > >簡單數據結構總結

簡單數據結構總結

lowbit 更新 統計 列表 int lld node 設置 小寫字母

1.樹狀數組

? 現階段,樹狀數組主要用於維護序列前綴和 (其實還支持區間和、區間異或和、區間乘積和RMQ等具有交換律的問題) 。

樹狀數組看似復雜的構造,其實都是以一個思想為基礎:

將$i$二進制分解,最小的二的次冪記為lowbit(i)

結合差分思想,做到區間修改,單點查詢。洛谷P3368

*樹狀數組求逆序對(二維偏序)洛谷P1908  洛谷P1774:

//定義數組,b[]為讀入的數組,a[]為要離散化後的數組,c[]為樹狀數組
struct node
{int v,id;}a[N];
bool cmp(const node &x,const node &y)
{return x.v<y.v;}
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i].v),a[i].id=i;
    sort(a+1,a+n+1,cmp);//按價值從大到小排序
    int cnt=1;
    for(int i=1;i<=n;i++)//離散化+去重
    {
        if(i!=1 &&a[i].v!=a[i-1].v)cnt++;
        b[a[i].id]=cnt;
    }
    for(int i=1;i<=n;i++)
        update(b[i],1),ans=ans+i-sum(b[i]);//因為是排完序之後,所以之前加入的一定比後加入的大
                                        //然後在查詢當前這個數前面位置的數,就是逆序對的個數了
    printf("%lld\n",ans);
    return 0;
}

總結:

時間復雜度:$O(nlogn)$(上限) 空間復雜度:$O(n)$

樹狀數組以其代碼量少,思想簡潔著稱,可以單獨使用,也可以和其他數據結構結合,達到錦上添花的效果。不足之處呢,就是能維護的的東西局限較大,難以單獨實現其他算法。

練習:洛谷P1774(結合冒泡排序思想就變成逆序對裸題)

?  *洛谷P3810(三位偏序,要用到CDQ分治) 還沒弄懂

2.線段樹

? 線段樹之所以稱為“樹”,是因為其具有樹的結構特性。線段樹由於本身是專門用來處理區間問題的(包括RMQ、RSQ問題等。

幾點基礎性質:

1.線段樹每個節點都是一個區間

2.對於每一個區間為$[l,r]$非葉節點$k$,它的左兒子是$k<<1$,區間為$[l,mid]$,右兒子是$k<<1|1$,區間為$[mid+1,r]$。其中$mid=(l+r)>>1$

下面以維護區間和,支持區間修改的線段樹為例,實現它的各種操作。

關於區間修改操作:

延遲標記(Lazy tag)

詳細地說,我們在執行修改命令時,可以先不對所有子節點立刻修改,而是在受影響的父節點上標記,表示“該節點已被修改,但其子節點尚未更新”。這樣在詢問向下遞歸時,就可以順便計算父節點標記(修改)對它的影響。

我們以add數組代表標記數組,每次如果查詢子節點就將標記下傳

洛谷P3372:

void pushdown(int k,int l,int r)//標記下傳
{
    if(add[k]==0) return;
    int mid=(l+r)>>1;
    Add(k<<1,l,mid,add[k]);
    Add(k<<1|1,mid+1,r,add[k]);
    add[k]=0;
}

*標記一個以上,記得考慮運算優先級!洛谷P3373

其他的題目也差不多,分析好維護的是什麽,怎麽修改,怎麽設置標記,怎麽下傳。註意細節,線段樹的初級應用還是不難的。

例題:

洛谷P2574(維護xor)         洛谷SP7259雙倍經驗的紫題

總結:

時間復雜度:$O(nlogn)$ 空間復雜度:$O(4n)^+$

線段樹可擴展的空間很大,但是我還沒學到那麽多,在此就不多贅述了。總而言之,線段樹真的是一個非常強的數據結構,在區間維護問題上幾乎是萬能的存在。我還是慢慢研究吧。

3.散列表(Hash)

? 與離散化類似的是,Hash能把若幹復雜的信息映射到一個容易維護的值域內,從而降低統計難度。哈希的過程,其實可以看作對一個串的單向加密過程,並且需要保證所加的密不能高概率重復

? Hash算法應用較廣泛,我只學習了 字符串哈希

直接上例題理解吧:洛谷P3370

? 給定N個字符串(第i個字符串長度為Mi,字符串內包含數字、大小寫字母,大小寫敏感),請求出N個字符串中共有多少個不同的字符串。

對於100%的數據:N<=10000,Mi≈1000。

進制哈希

給出出一個固定進制base,將一個串的每一個元素看做一個進制位上的數字,所以這個串就可以看做一個base進制的數,那麽這個數就是這個串的哈希值;則我們通過比對每個串的的哈希值,即可判斷兩個串是否相同。

long long hashe(char s[])
{
    int len=strlen(s);
    long long ans=0;
    for (int i=0;i<len;i++)
    ans=(ans*base+s[i])%mod+prime;   //核心操作,可類比十進制。
                                    //+prime可增加hash的可行度(不加本題可能會被卡哦)
    return ans;                     //mod 最好取一個較大的質數,作用同上。
}

如果字符串較多,難免出現Hash值一樣的字符串,這種現象叫做哈希碰撞

怎麽解決?

1、無錯哈希:掛鏈表之類的操作。

2、多重哈希:就是字面意思

總結:

Hash表與其說是一種數據結構,不如說是一種sao操作算法。它對於復雜信息的統計有著很大的應用。但是考試時其實Hash的應用不算特別多(我反正基本沒用過),而且哈希函數的選擇也比較玄學,因此這裏就不詳細講了。

簡單數據結構總結