链表学习笔记
题面传送门
链表,顾名思义,就是一根链,由许多节点构成,每个节点分为两个部分:数据域和指针域,其中数据域可以包含很多数据,指针域只能包含一个指向对象,不然就成图了。链表可以\(O(1)\)插入和删除数据,但不能随机访问元素。
链表实现就不再乱说了。毕竟是个人都会。主要讲一些链表的应用。
这道题很简单,只要顺着链表找下去,找到第m个点就删掉就好了。
代码实现:
#include<cstdio>
using namespace std;
int n,m,f[100039],head,cnt=1,h[100039];
int main(){
register int i,j;
scanf("%d%d",&n,&m);
for(i=2;i<=n;i++) f[i]=i-1;
f[1]=n;
for(i=1;i<n;i++) h[i]=i+1;
h[n]=1;
for(i=1;i<n;i++){
for(j=1;j<=m;j++)cnt=h[cnt];
f[cnt]=f[f[cnt]];
h[f[cnt]]=cnt;
}
printf("%d",cnt);
}
题面传送门
这道题也不难看出是链表,可是如果用单链表的话,我们来模拟一下:
原来是\(1->2->3->4->5\)
第一次插队把\(1\)插到\(2\)的后面。
\(2->1->3->4->5\)
第二次把\(2\)插到\(4\)的后面:
\(1->3->4->2->5\)
第三次把\(3\)插到\(5\)的后面:
\(1->3\)
\(3->2->5->3\)
这就不是链表了,而且也没有我们想要的目的。那么\(3\)怎么找到\(1\)前面的那个呢?那就用双向链表呗!
代码实现:
#include<cstdio>
using namespace std;
int n,m,x,y,f[100039],h[100039],head=1;
int main() {
register int i;
scanf("%d%d",&n,&m);
for(i=2; i<=n; i++) f[i]=i-1;
for(i=1; i<n; i++) h[i]=i+1;
for(i=1; i<=m; i++) {
scanf("%d%d",&x,&y);
if(!f[x]) {
head=h[x];
f[h[x]]=0;
h[x]=0;
} else if(!h[x]) {
h[f[x]]=0;
f[x]=0;
} else {
h[f[x]]=h[x];
f[h[x]]=f[x];
f[x]=h[x]=0;
}
if(!h[y]) {
f[x]=y;
h[y]=x;
} else {
f[x]=y;
h[x]=h[y];
h[y]=f[h[x]]=x;
}
/*int tail=head;
while(tail) {
printf("%d ",tail);
tail=h[tail];
}
printf("\n");*/
}
while(head) {
printf("%d ",head);
head=h[head];
}
return 0;
}
注意考虑特殊情况(好像这道题不考虑也没事)
题面传送门
这道题用开放寻址法被卡了,只能用挂链表法。
代码实现:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int n,ans,h[1000007],head,z[1000007],tot;
string a,f[1000007];
inline void get(string a){
register int i,tmp,longge=a.size();ans=0;
for(i=0;i<longge;i++) ans=(ans*259+a[i])%1000039;
head++;
f[head]=a;
z[head]=h[ans];
h[ans]=head;
}
inline int find(string a){
register int i,tmp,now=0,longge=a.size();ans=0;
for(i=0;i<longge;i++) ans=(ans*259+a[i])%1000039;
tmp=h[ans];
while(tmp>=0){
if(f[tmp]==a) now++;
tmp=z[tmp];
}
return now;
}
int main(){
register int i;
memset(h,-1,sizeof(h));
scanf("%d",&n);
for(i=1;i<=n;i++){
cin>>a;
if(!find(a)) tot++,get(a);
}
printf("%d",tot);
}
题面传送门
这道题要用邻接表来存图。
我们给每一个点做一个链表,只不过这些链表放在一个数组里。每个链表有一个头,我们每次加入都要从头加入。这样遍历时顺着头一路找回去就可以了。
代码实现:
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,x[39],y[39],head,v[39],h[39];
struct yyy{
int to,z;
}f[39];
inline void add(int x,int y){
head++;
f[head].to=y;
f[head].z=h[x];
h[x]=head;
}
inline void dfs(int x){
if(v[x]) return;
v[x]=1;
printf("%d ",x);
for(register int i=h[x];i!=-1;i=f[i].z) dfs(f[i].to);
return ;
}
int main(){
memset(h,-1,sizeof(h));
register int i;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++)scanf("%d%d",&x[i],&y[i]);
for(i=m;i>=1;i--) add(x[i],y[i]),add(y[i],x[i]);
dfs(1);
return 0;
}
链表可以应用在很多地方,但是链表只是指针的一个应用,所以我们还是学好指针吧!