基礎資料結構

2022-07-24 06:01:01

基礎資料結構介紹

\(luoguB3614\)

概念

一種先進後出的資料結構

實現方法

手寫棧(用陣列模擬)

int st[N];//模擬棧 
int idx;//棧中元素數量 

st[++idx]=x;//壓棧 

return st[idx];//取棧頂元素 

if(idx) idx--;//彈出棧頂元素

idx=0;//清空棧 

STL庫

#include <stack>//棧所需的標頭檔案

stack<int> st;

st.top();//返回棧頂元素 

st.push(x);//壓棧 

st.pop();//彈出棧頂元素 

st.empty();//判斷棧是否為空 

st.size();//返回棧中元素數量 

單調棧 \(luogu5788\)

概念

具有單調性的棧。

維護一個單調棧(\(st\)

插入

在插入時,將不滿足單調性的元素彈出。

//手寫
int st[N],idx=0;

inline void add(int x)
{
	while(idx!=0&&st[idx]<x) idx--;
	st[++idx]=x;
}

//STL
stack<int> st;

while(!st.empty()&&st.top()<x) st.pop();
st.push(x);

其餘操作(讀取棧頂元素,返回棧頂數量等)與棧操作相同

佇列 \(luoguB3616\)

概念

一種先進先出的資料結構

實現方法

手寫(用陣列模擬)

int q[N],h=1,t;//q為佇列,h為隊頭,t為隊尾

q[++t]=x;//將一個元素x加入佇列

h++;//隊首元素出隊

q[h];//隊頭元素

q[t];//隊尾元素

h=1;//清空佇列 
t=0;//同時也是初始化 

STL庫

#include <queue>//佇列所需標頭檔案

queue<int> q;//佇列

q.front();//隊首元素

q.back();//隊尾元素

q.push(x);//將元素x新增到佇列中

q.pop();//彈出隊首元素

q.empty();//判斷佇列是否為空

q.size();//返回佇列元素數量 

雙端佇列

概念

與普通佇列的不同之處在於雙端佇列可以在隊頭/隊尾新增(刪除)元素。

實現方法

手寫雙端佇列(用陣列模擬)

int q[N],h=1,t;//q為佇列,h為隊頭,t為隊尾

q[++t]=x;//將一個元素x加入隊首

q[--h]=x;//將一個元素x加入隊尾

h++;//隊首元素出隊

t--;//隊尾元素出隊

q[h];//隊頭元素

q[t];//隊尾元素

h=1;//清空佇列 
t=0;//同時也是初始化 

STL庫

#include <deque>//雙端佇列所需標頭檔案

deque<int> q;

q.front();//查詢隊頭元素 

q.back();//查詢隊尾元素 

q.push_back(x);//在隊尾插入元素x 

q.push_front(x);//在隊首插入元素x 

q.pop_back();//在隊尾彈出元素 

q.pop_front();//在隊首彈出元素 

q.empty();//判斷佇列是否為空 

q.size();//返回佇列元素數量 

單調佇列 \(luogu1886\)

概念

具有單調性的佇列。

注意:單調佇列與雙端佇列相似,均可以在隊頭(隊尾)進行插入或刪除操作。

維護一個單調佇列(\(q\)

插入

int q[N],h=1,t;

inline void add(int x)
{
	while(h<=t&&q[t]<=x) t--;
	q[++t]=x;
} 

其餘操作(查詢元素,判斷是否為空等)與雙端佇列相同

優先佇列(堆)

概念

堆其實是一棵完全二元樹,而優先佇列是一個大根堆。

因此可以用一維陣列來進行模擬堆。

堆的實現方法

手寫(用一維陣列模擬)

int h[N],idx;//h模擬堆,idx為堆內元素數量

void down(int x)//向下調整堆 
{
	int t=x;
	if(x*2<=idx&&h[x*2]<h[t]) t=x*2;
	if(x*2+1<=idx&&h[x*2+1]<h[t]) t=x*2+1;
	if(x!=t)
	{
		int tmp=h[x];
		h[x]=h[t];
		h[t]=tmp;
		down(t);
	}
} 

void up(int x)//向上調整堆 
{
	while(x/2&&h[x/2]>h[x])
	{
		int t=h[x/2];
		h[x/2]=h[x];
		h[x]=t;
		x/=2;
	}
}

inline void add(int x)//將元素x插入堆 
{
	h[++idx]=x;
	up(x);
}

inline int minn()//返回堆內最小值 
{
	return h[1];
}

inline void delete_minn()//刪除堆內最小值
{
	h[1]=h[idx];
	idx--;
	down(1);
} 

inline void _delete(int x)//刪除堆內第x個元素
{
	h[x]=h[idx];
	idx--;
	up(x);
	down(x);
} 

inline void write(int x,int y)//將第x個元素修改為第y個元素
{
	h[x]=h[y];
	up(x);
	down(x);
} 

STL庫(優先佇列)

#include <queue>

priority_queue<int> q;

q.top();//查詢堆頂元素 

q.empty();//判斷是否為空

q.size();//堆內元素數量

q.push(x);//將元素x加入堆中

q.pop();//刪除堆頂 

單向連結串列 \(luoguB3631\)

概念

一種鏈式資料結構。

與陣列的不同點在於陣列是連續儲存的,而連結串列可以是非連續的。

連結串列相關操作

單向連結串列包括資料域和指標域,資料域用來儲存當前位置所儲存的值,指標域用來儲存下一個資料的位置。

int e[N],ne[N],idx,h=-1;

inline void add_front(int x)//在連結串列頭插入元素x 
{
	e[idx]=e;
	ne[idx]=h;
	h=idx++;
}

inline void delete_front()//刪除連結串列頭元素 
{
	h=ne[h];
}

inline void add(int x,int i)//在第i個元素後面插入元素x 
{
	e[idx]=x;
	ne[idx]=ne[i];
	ne[i]=idx++;
}

inline void _delete(int i)//將第i個元素刪除
{
	ne[i]=ne[ne[i]];
} 


雙向連結串列

概念

與單連結串列的不同之處在於指標域有左右之分。

程式碼實現

int e[N],l[N],r[N],idx;

inline void init()//初始化 
{
	r[0]=1;
	l[1]=0;
	idx=2;
}

inline void add(int x,int i)//在第i個資料的右邊插入資料x 
{
	e[idx]=x;
	l[idx]=i;
	r[idx]=r[i];
	l[r[i]]=idx;
	r[i]=idx++;
}

inline void _delete(int i)//刪除第i個結點 
{
	l[r[i]]=l[i];
	r[l[i]]=r[i];
}

並查集 \(luogu3367\)

概念

並查集是一種類似於樹的資料結構,支援合併和查詢兩種操作。

程式碼實現

int fa[N];

inline void init(int n)//初始化 
{
	for(register int i=1;i<=n;i++)
	{
		fa[i]=i;
	}
}

int f(int x)//查詢操作 
{
	if(fa[x]!=x) fa[x]=f(fa[x]);//路徑壓縮
	return fa[x]; 
}

inline void unionSet(int x,int y)//合併操作
{
	fa[f(x)]=f(y);
} 

資料結構實際運用

  • 佇列 廣搜

  • 優先佇列 dijkstra堆優化(最短路演演算法)

  • 連結串列 鄰接表存圖(也就是若干個單連結串列)

  • 並查集 Kruskal(最小生成樹演演算法)

  • 單調佇列/單調棧 優化dp

一些建議

  • 在手動模擬資料結構時,若操作較多,可用 \(\text {struct}\) 將其封裝,既方便使用,又提高了程式碼的整潔度和可觀性。