簡單數據結構總結
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的應用不算特別多(我反正基本沒用過),而且哈希函數的選擇也比較玄學,因此這裏就不詳細講了。
簡單數據結構總結