基礎課 第二講 資料結構
阿新 • • 發佈:2021-10-27
單鏈表
826.單鏈表
雙鏈表
827.雙鏈表
棧
828.模擬棧
佇列
829.模擬佇列
單調棧
830.單調棧
處理出每個數左邊最近的比它小的數,如不存在則為 -1
cin >> a[i];
while(tt && stk[tt] >= a[i]) tt--;
if(tt) cout << stk[tt];
else cout << -1;
stk[++tt] = a[i];
131.直方圖中最大的矩形
為方便,在左右兩邊各添一個高 -1 的矩形。對每個矩形,左邊第一個小於它的位置 +1 為能拓展到的最左矩形,右邊第一個小於它的位置 -1 為能拓展到的最右矩形
#include <bits/stdc++.h> using namespace std; using ll = long long; const signed N = 1e5+10; int a[N], stk[N], tt, l[N], r[N]; signed main() { int n; while(cin >> n && n) { for(int i = 1; i <= n; i++) cin >> a[i]; tt = 0, stk[++tt] = 0, a[0] = -1; for(int i = 1; i <= n; i++) { while(a[stk[tt]] >= a[i]) tt--; l[i] = stk[tt] + 1; stk[++tt] = i; } tt = 0, stk[++tt] = n+1, a[n+1] = -1; for(int i = n; i >= 1; i--) { while(a[stk[tt]] >= a[i]) tt--; r[i] = stk[tt] - 1; stk[++tt] = i; } ll ans = 0; for(int i = 1; i <= n; i++) ans = max(ans, (ll)a[i]*(r[i]-l[i]+1)); cout << ans << '\n'; } return 0; }
單調佇列
154.滑動視窗
#include <bits/stdc++.h> using namespace std; const signed N = 1e6+10; int a[N], q[N]; signed main() { int n, k; cin >> n >> k; for(int i = 0; i < n; i++) cin >> a[i]; int hh = 0, tt = -1; for(int i = 0; i < n; i++) { //找最小值 if(hh<=tt && i-k+1>q[hh]) hh++; while(hh<=tt && a[q[tt]]>=a[i]) tt--; q[++tt] = i; if(i >= k-1) cout << a[q[hh]] << ' '; } cout << '\n'; hh = 0, tt = -1; for(int i = 0; i < n; i++) { //找最大值 if(hh<=tt && i-k+1>q[hh]) hh++; while(hh<=tt && a[q[tt]]<=a[i]) tt--; q[++tt] = i; if(i >= k-1) cout << a[q[hh]] << ' '; } return 0; }
KMP
831.KMP字串
求出模板串P在模式串S中所有出現的位置的起始下標
#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = 1000010;
int n, m;
char p[N], s[M];
int ne[N];
int main()
{ //字串都從1開始
cin >> n >> p + 1 >> m >> s + 1;
//求ne陣列, ne[1] = 0
for(int i = 2, j = 0; i <= n; i ++ )
{
while (j && p[i] != p[j + 1]) j = ne[j];
if(p[i] == p[j + 1]) j++;
ne[i] = j;
}
//kmp
for(int i = 1, j = 0; i <= m; i ++ )
{
while(j && s[i] != p[j + 1]) j = ne[j];
if(s[i] == p[j + 1]) j ++;
if(j == n)//成功匹配
{
printf("%d ",i - n);
j = ne[j]; //再次縮短長度進行下一次的匹配
}
}
return 0;
}
Trie
835.Trie字串統計
143.最大異或對
在陣列中選兩個進行異或運算的最大值
#include <bits/stdc++.h>
using namespace std;
const signed N = 1e5+10;
int ans, tr[31*N][2], idx;
void ins(int x)
{
int p = 0;
for(int i = 30; i >= 0; i--)
{
int u = x>>i&1;
if(!tr[p][u]) tr[p][u] = ++idx;
p = tr[p][u];
}
}
signed main()
{
int n; cin >> n;
while(n--)
{
int x; cin >> x;
ins(x);
int p = 0, res = 0;
for(int i = 30; i >= 0; i--)
{
int u = x>>i&1;
if(tr[p][u^1]) //也可寫!u
res |= (1<<i), p = tr[p][u^1];
else
p = tr[p][u];
}
ans = max(ans, res);
}
cout << ans;
return 0;
}
並查集
836.合併集合
837.連通塊中點的數量
維護每棵樹的點數的並查集
240.食物鏈(真妙啊)
維護到祖宗距離(這題是距離%3)的並查集
#include <bits/stdc++.h>
using namespace std;
const signed N = 50010;
int p[N], d[N];
int find(int x)
{
if(p[x] != x)
{
int t = find(p[x]);
d[x] += d[p[x]];
p[x] = t;
}
return p[x];
}
signed main()
{
int n, m, ans = 0; cin >> n >> m;
for(int i = 1; i <= n; i++) p[i] = i;
while(m--)
{
int t, x, y; cin >> t >> x >> y;
if(x > n || y > n) ans++;
else
{
int px = find(x), py = find(y);
if(t == 1)
{
if(px == py && (d[x] - d[y]) % 3) ans++;
else if(px != py)
{
p[px] = p[y];
d[px] = d[y] - d[x];
}
}
else
{
if(px == py && (d[x] - d[y] - 1) % 3) ans++;
else if(px != py)
{
p[px] = p[y];
d[px] = d[y] + 1 - d[x];
}
}
}
}
cout << ans;
return 0;
}
堆
\(O(n)\) 建堆:對 i=n/2
到 i=1
進行 down(i)
操作
838.堆排序
從小到大輸出 m 個數
#include <bits/stdc++.h>
using namespace std;
const signed N = 1e5+10;
int h[N], n;
void down(int u)
{
int t = u;
if (u * 2 <= n && h[u * 2] < h[t]) t = u * 2;
if (u * 2 + 1 <= n && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
if (u != t)
{
swap(h[u], h[t]);
down(t);
}
}
signed main()
{
int m; cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> h[i];
for(int i = n/2; i; i--) down(i);
while(m--)
{
cout << h[1] << ' ';
h[1] = h[n--];
down(1);
}
return 0;
}
839.模擬堆
雜湊表
N 一般取hash陣列長度的第一個質數如100003、200003
//找質數
for(int i = N; i++)
{
bool flag = 1;
for(int j = 2; j*j<=i;j++) if(i%j==0)
{
flag = 0; break;
}
if(flag)
{
cout << i; break;
}
}
開放定址法hash陣列的長度要開題目資料範圍的兩到三倍
840.模擬散列表
//題目範圍100000,開放定址法
const int N = 200003, null = 0x3f3f3f3f;
int h[N];
int find(int x) //找x的位置
{
int k = (x % N + N) % N;
while(h[k] != null && h[k] != x)
{
k++;
if(k == N) k = 0;
}
return k;
}
int main()
{
memset(h, 0x3f, sizeof h);
h[find(x)] = x; //插入x
h[find(x)] != null; //存在x
}
841.字串雜湊
把字串看成一個 p 進位制數(左邊是高位),轉成10進位制並 mod Q
經驗上 p 取 131 或 13331,Q 取 2e64(用 ull
自然溢位即可)
注意不能對映成 0, 'A' 要看成 1 而不是 0,否則會誤判 "A" == "AA"。所以直接用 ascii 碼算就行了,不用減去 'A' 之類的
h[i] = h[i-1]*p + str[i]
[L,R]
的雜湊值為 h[R]-h[L]*p^(R-L+1)