目錄
- 連結串列和鄰接表
- 單連結串列
- 雙連結串列
- 棧和佇列
- 棧
- 佇列
- Kmp
- 1.對於next陣列的理解
- 2.主串與模式串的匹配過程
- Tire
- 並查集
- 堆
- Harsh表
- 一般雜湊
- 拉鍊法
- 開放定址法
- 字串雜湊
- 一般雜湊
連結串列和鄰接表
單連結串列
//單連結串列
const int N=10010;
int h,e[N],en[N],idx;
初始化
void init(){
h=-1;
idx=0;
}
插入
//頭插法
void insert(int x){
e[idx]=x;
en[idx]=en[h];
h=idx++;
}
//任意位置插入
void insert_every(int k,int x){
e[idx]=x;
en[idx]=en[k];
k=idx++;
}
k的前一個位置-1,後一個位置+1;
刪除
void delete(int k){
en[k]=en[en[k]];
}
int main(){
//輸出
for(int i=h;i!=-1;i=en[i]){
cout<<e[i]<<" ";
}
}
雙連結串列
using namespace std;
const int N=100010;
int l[N],r[N],e[N];
int k,x,idx;
/*雙連結串列
*l[i]:表示i位置左邊的點
*r[i]:表示i位置右邊的點
*e[i]:表示i位置的值
*1.初始化
*2.插入
*3.刪除
*/
void init() {
//0表示左端點,1表示右端點
r[0]=1;l[1]=0;
idx=2;
}
void insert(int k,int x) {
e[idx]=x;
r[idx]=r[k];
l[idx]=k;
r[k]=idx++;
}
void remove(int k) {
r[l[k]]=r[k];
l[r[k]]=l[k];
}
//輸出i=1說明回到了起點
for (int i = r[0]; i!= 1; i = r[i]) {
cout << e[i] << " ";
}
int main() {
init();
// 插入一些節點
insert(0, 10); // 在左端點後插入值為 10 的節點
insert(2, 20); // 在新插入的節點後插入值為 20 的節點
insert(3, 30);
cout << "插入節點後的雙連結串列:";
for (int i = r[0]; i!= 1; i = r[i]) {
cout << e[i] << " ";
}
cout << endl;
// 刪除中間節點
remove(2);
cout << "刪除節點後的雙連結串列:";
for (int i = r[0]; i!= 1; i = r[i]) {
cout << e[i] << " ";
}
cout << endl;
return 0;
}
案例模擬
1.初始
+---+ +---+
| 0 |<-->| 1 |
+---+ +---+
2.插入一個值
+---+ +---+ +---+
| 0 |<-->| 2 |<-->| 1 |
+---+ +---+ +---+
3.
+---+ +---+ +---+ +---+
| 0 |<-->| 2 |<-->| 3 |<-->| 1 |
+---+ +---+ +---+ +---+
4.
+---+ +---+ +---+ +---+ +---+
| 0 |<-->| 2 |<-->| 3 |<-->| 4 |<-->| 1 |
+---+ +---+ +---+ +---+ +---+
5.刪除一個值
+---+ +---+ +---+ +---+
| 0 |<-->| 3 |<-->| 4 |<-->| 1 |
+---+ +---+ +---+ +---+
棧和佇列
棧
陣列模擬棧
const int N=10010;
int stk[N];棧陣列
int top;//棧頂指標
//判斷棧空
if(top<0);
//入棧
stk[top++]=e;
//出棧
top--;
佇列
陣列模擬佇列
const int N=10010;
int que;佇列陣列
int hh,tt;對頭,隊尾
//判斷佇列是否為空
if(tt<hh)
//入佇列
que[++tt]=e;
//出佇列
hh++;
記錄;x=que[hh++];
Kmp
kmp演算法:是一種用於在一個文字字串中查詢一個模式字串的高效演算法。
const int N=100010,M=10010;
char S[M],P[N];
int Ne[N];
int m,n;
//用於:匹配問題
int main(){
cin >> n >> P+1 >> m >> S+1 ;
//求next的過程P為小的
for(int i=2,j=0;i<=n;i++){
while(j&&P[i]!=P[j+1]) j=Ne[j];//快速回溯 ,不斷回溯直到找到相等的,或者是j=0
if(P[i]==P[j+1])j++;//更新匹配長度
Ne[i]=j;//將當前的匹配長度 j 儲存在 next 陣列的對應位置 i,表示在模式字串中位置 i 之前的最長相同字首和字尾的長度。
}
//kmp的匹配過程S為大的
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-j+1);
j=Ne[j];//目的是利用已經計算好的模式字串的next陣列來快速確定下一次匹配的起始狀態。
}
}
1.對於next陣列的理解
求出部分模式串的next值:其實就是求最長公共前字尾
拿ABABC舉例
主要找最後一個字元
第一部:A 只有A自並沒有公共前字尾 故next[0]=0
第二部:AB 字首為A,字尾為B,並不相等 next[1]=0
第三部:ABA 最長相等的公共前字尾,前字尾為A,next[2]=1
第四部:ABAB 最長相等的公共前字尾,前字尾為AB,next[3]=2
第五部:ABABC 並沒有公共前字尾,next[4]=0 ;
2.主串與模式串的匹配過程
在匹配過程中,KMP演算法並沒有出現和BF演算法一樣匹配過程,主串沒有和模式串一起移動,而是採用了只動模式串的做法
1.在建立了next回溯陣列後,與主串進行匹配。
2.一旦匹配出現了問題後,根據匹配出現問題的地方,結合next陣列,進行跳轉。
3.模式串全部匹配後,則結束匹配。
Tire
Tire也可以稱它為字首樹
1.用於快速儲存和查詢一個單詞
那空間換時間
const int N=10010;
int son[N][26],idx;//用數字儲存字母
bool cnt[N];//記錄末尾
char str[N];//需要進行操作的字串
void insert(char Str[]){
int p=0;//p為根節點
for(int i=0;Str[i];i++){
char u=Str[i]-'a';//將其轉化為數字,進行儲存
if(!son[p][u]) son[p][u]=++idx;//idx為記錄它的深度
p=son[p][u];//不斷的往下跑
}
cnt[p]=true;//最後記錄最後的一個單詞,表明有這個單詞
}
bool search(char Str[]){
int p=0;//p為根節點
for(int i=0;Str[i];i++){
char u=Str[i]-'a';//將其轉化為數字,進行儲存
if(!son[p][u]) return false;
p=son[p][u];//不斷的往下跑
}
return true;
}
並查集
/*並查集的作用:
* 1.將兩個集合合併
* 2.查詢兩個元素是否在同一個集合當中
*3.統計集合中的個數,只看根節點的size
*
*
* 並查集的基本原理:每個集合有一個樹表示。樹根的編號就是集合的編號。每個節點儲存他的父節點。p[x]表示他的父節點
*
* 問題:
* 1.如何判斷根節點:if(p[x]=x)
* 2.如何求X的集合編號:while(p[x]!=x)x=p[x];
* 3.如何將兩個集合合併:p[x]=y;
*
* */
const int N=10010;
int p[N],x;
int size[N];
//路徑壓縮
int find(x){
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
int main(){
//1~n的編號
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
p[i]=i;//初始化父節點,使每個都指向自己
size[i]=1;//初始化時都為1
}
while(m--){
char op[10];
int a,b;
cin>>op;
if(op[0]=='M'){
cin>>a>>b;
size[find(b)]+=size[find(a)];//先加
p[find(a)]=find[b];//再合併
}else if(op[0]=='Q'){
if(op[1]=='a'){
cin>>a>>b;
if(find(a)==find(b)){
puts("在一個連通塊中");
}
else{
puts("不在");
}
}else{
cin>>a;
cout<<size(find(a))<<endl;
}
}
else{
}
}
}
堆
/*
* heap陣列,up(size)
* 下標:從一開始
* 堆:完全二叉樹
* 小根堆:他並未是完全從小到大的,他的儲存結構是數,不能夠直接用迴圈輸出
* 1.插入一個數:heap[++size]=x;up(size);
* 2.求集合當中的最小值:heap[1];
* 3.刪除最小元素:heap[1]=heap[size];size--;down(1);
* 4.刪除任意一個值:heap[k]=heap[size];size--;down(k);up(k);
* 5.修改任意一個元素:heap[k]=x;up(k),down(k)
* */
const int N=10010;
int h[N],size;
//用遞迴來實現
void down(int t){
int u=t;//開始位置
//先從左邊開始
if(2*u<=size&&h[2*u]<h[t])t=2*u
//再從右邊開始
if(2*u+1<=size&&h[2*u+1]<h[t])t=2*u+1;
if(t!=u){
swap(h[u],h[t]);
down(t);
}
}
//用迴圈實現
void up(int t){
while(2/t&&h[2/t]>h[t]){
swap(h[2/t],h[t]);
t=2/t;
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){scanf("%d",&h[i]);}
//建堆
for(int i=n/2;i;i--){down(i)}
while(m--){
char op[10];
int c,k;
scanf("%s",op);
//1.插入
if(op[0]=='I'){
cin>>c;
//新增原理:先將size++,讓後up(size)一下
h[++size]=c;
up(size);
}
//2.查詢最小的數
else if(op[0]=='Q'){
if(op[1]=='a'){
cout<<h[1]<<endl;
}
}
//3.刪除
else if(op[0]=='D'){
//刪除第一個元素
if(op[1]=='a'){
h[1]=h[size];
size--;
down(1);
}else if(op[1]=='b'){
//刪除任意位置的數
cin>>k;
h[k]=h[size];
size--;
up(k);down(k);
}
}
//修改任意位置的數
else{
cin>>c>>k;
h[k]=c;
down(k);up(k);
}
}
}
Harsh表
一般雜湊
拉鍊法
1.高效的資料儲存與檢索
2.資料去重
可以將10的九次方的數轉換為10的五次方
const int N=10010;
int h[N],e[N],ne[N],idx;
//拉鍊法實現
void insert(int x){
int k=(x%N+N)%N;//將k對映為0到N-1的範圍
e[idx]=x;
ne[idx]=h[k]
h[k]=idx;
idx++;
}
bool search(int x){
int k=(x%N+N)%N;//將k對映為0到N-1的範圍
for(int i=h[k];i!=-1;i=ne[i]){
if(e[i]==x)return true;
}
return false;
}
int main(){
memset(h,-1,sizeof h);
}
開放定址法
//開放定址發
int h[N];
// 如果x在雜湊表中,返回x的下標;如果x不在雜湊表中,返回x應該插入的位置
int find(int x)
{
int t = (x % N + N) % N;
while (h[t] != null && h[t] != x)
{
t ++ ;
if (t == N) t = 0;
}
return t;
}
字串雜湊
用於快速檢索兩端字串是否相等
str="ABCIBNUOCTYULJ"
h[0]=0;
h[1]="A"的雜湊值
h[2]="AB"的雜湊值
.
.
.
.
以此類推
(A B C D)
(1,2,3,4)p
(1*p的三次方+...)modQ
注意點: 1.不能對映為0
2.Rps是足夠好
用一個P進位制來表示
P最好為131或13331數學中證明過:99.99%沒有衝突
最好再模上個2的64次方:可以用unsigned long long 溢位的部分就是取模的數
typedef unsigned long long ULL;
const int N=10010,P=131;
ULL h[N],p[N];
int n,m;
char str[N];
ULL get(int l,int r){
return h[r]-h[l-1]*p[l-r+1];
}
int main(){
scanf("%d%d%s",&n,&m,str+1);
p[0]=1;
for(int i=1;i<=n;i++){
p[i]=p[i-1]*P;
h[i]=h[i-1]*P+str[i];//預處理字首雜湊值
}
while(m--){
int l1,r1,l2,r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(get(l1,r1)==get(l2,r2))puts("Yes");
else{puts("No")}
}
return 0;
}