🏆今日学习目标: 🍀学习算法-数据结构-链表 ?创作者:贤鱼 ?预计时间:30分钟 🎉个人主页:贤鱼的个人主页 🔥专栏系列:算法 🍁贤鱼的个人社区,欢迎你的加入 贤鱼摆烂团
🍁链表
🍀介绍🍀
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。
每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
🍀分类🍀
🍉单项链表🍉
每一个单元都指向下一位
🍉循环链表🍉
和单链表差不多,只不过第一位和最后一位相连,变成了循环链表
🍉双向链表🍉
🍁常见操作🍁
🍀链表🍀
头文件
#include
插入
前方插入push_front(数字) 在链表最前面插入一个数字 后方插入push_back(数字) 链表最后面插入一个数字 中间插入insert(迭代器,数字) 在迭代器指向的链表元素前面插入一个数字
删除
erase(迭代器) 删除迭代器指向的链表元素
查找
begin() 链表头 end() 链表末尾 需要迭代器储存
输出
需要迭代器,后文会讲
结构体模拟部分看例题解法2
🍀迭代器🍀
list::iterator 名字
迭代器是一种检查容器内元素并遍历元素的数据类型 可以理解为数组的下标 举个例子
list<int>::iterator pos[10005];
list<int> m;
pos[5]
list<int>::iterator iterbegin;
iterbegin=m.begin();
cout<<*iterbegin;
list<int>::iterator iterend;
iterend=m.end();
for(;iterbegin!=iterend;iterbegin++){
}
循环也很好理解
只有当begin=end的时候循环就会结束
🍁例题🍁
🍀队列安排🍀
🍎题目描述🍎
一个学校里老师要将班上
N
N
N 个同学排成一列,同学被编号为
1
~
N
1\sim N
1~N,他采取如下的方法:
-
先将
1
1
1 号同学安排进队列,这时队列中只有他一个人; -
2
?
N
2-N
2?N 号同学依次入列,编号为
i
i
i 的同学入列方式为:老师指定编号为
i
i
i 的同学站在编号为
1
~
(
i
?
1
)
1\sim(i-1)
1~(i?1) 中某位同学(即之前已经入列的同学)的左边或右边; -
从队列中去掉
M
(
M
<
N
)
M(M<N)
M(M<N) 个同学,其他同学位置顺序不变。
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
🍎输入格式🍎
第
1
1
1 行为一个正整数
N
N
N,表示了有
N
N
N 个同学。
第
2
~
N
2\sim N
2~N行,第
i
i
i 行包含两个整数
k
,
p
k,p
k,p,其中
k
k
k 为小于
i
i
i 的正整数,
p
p
p 为
0
0
0 或者
1
1
1。若
p
p
p 为$ 0$,则表示将
i
i
i 号同学插入到
k
k
k 号同学的左边,
p
p
p 为
1
1
1 则表示插入到右边。
第
N
+
1
N+1
N+1 行为一个正整数
M
M
M,表示去掉的同学数目。
接下来
M
M
M 行,每行一个正整数
x
x
x,表示将
x
x
x 号同学从队列中移去,如果
x
x
x 号同学已经不在队列中则忽略这一条指令。
🍎输出格式🍎
1
1
1 行,包含最多
N
N
N 个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。
🍎样例 #1🍎
🍍样例输入 #1🍍
4
1 0
2 1
1 0
2
3
3
🍍样例输出 #1🍍
2 4 1
🍎提示🍎
样例解释:
将同学
2
2
2 插入至同学
1
1
1 左边,此时队列为:
2 1
将同学
3
3
3 插入至同学
2
2
2 右边,此时队列为:
2 3 1
将同学
4
4
4 插入至同学
1
1
1 左边,此时队列为:
2 3 4 1
将同学
3
3
3 从队列中移出,此时队列为:
2 4 1
同学
3
3
3 已经不在队列中,忽略最后一条指令
最终队列:
2 4 1
数据范围
对于
20
%
20\%
20% 的数据,有
1
≤
N
≤
10
1\leq N\leq 10
1≤N≤10;
对于
40
%
40\%
40% 的数据,有
1
≤
N
≤
1000
1\leq N\leq 1000
1≤N≤1000;
对于
100
%
100\%
100% 的数据,有
1
≤
N
,
M
≤
100000
1\leq N,M\leq100000
1≤N,M≤100000。
🍀思路🍀
这个题很简单,如果不是m数据范围太大,循环直接就可以解决
问题是他数据范围很大
我们可以用一个迭代器pos[]来储存每一位数字 这样子插入就解决了 利用insert pos[k]就是插入数字的位置(找到k数字插入)
然后就是删除 输入x 利用erase 删除pos[x]就好了
接着就是输出 我们用迭代器ib,id(iterbegin和iterend)来储存链表头和链表尾,循环输出就可以了 ib=m.begin() id=m.end()
当然,结构体也可以做这个题 结构体a,内有l,r。表示当前数字的左边和右边 a[].r就是当前数字的右边 a[].l就是当前数字的左边 这样子也可以模拟链表 其他具体内容下列题解见
🍀做法1🍀
第一种是用链表的做法,来巩固一下上文学习的知识
#include<cmath>
#include<iostream>
#include<cstdio>
#include<iostream>
#include<list>
using namespace std;
typedef list<int>::iterator iter;
iter next(iter it){
it++;
return it;
}
iter pos[100005];
list<int>m;
int f[100005];
int n;
void build(){
m.push_front(1);
pos[1]=m.begin();
for(int i=2;i<=n;i++){
int k,p;
cin>>k>>p;
if(p==0){
pos[i]=m.insert(pos[k],i);
}else{
iter nextx=next(pos[k]);
pos[i]=m.insert(nextx,i);
}
}
int mm;
cin>>mm;
for(int i=1;i<=mm;i++){
int x;
cin>>x;
if(!f[x]) m.erase(pos[x]);
f[x]=1;
}
}
int main(){
cin>>n;
build();
iter ib=m.begin();
iter id=m.end();
int f1=1;
for(;ib!=id;ib++){
if(!f1) cout<<" ";
cout<<*ib;
f1=0;
}
cout<<endl;
return 0;
}
🍀做法2🍀
#include<cmath>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct node{
int l,r;
}a[100005];
void del(int x){
if(a[x].l==-1) return ;
a[a[x].l].r=a[x].r;
a[a[x].r].l=a[x].l;
a[x].l=-1;
a[x].r=-1;
}
void adr(int x,int k){
a[x].l=k;
a[a[k].r].l=x;
a[x].r=a[k].r;
a[k].r=x;
}
void adl(int x,int k){
a[x].r=k;
a[a[k].l].r=x;
a[x].l=a[k].l;
a[k].l=x;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
a[i].l=a[i].r=-1;
}
a[1].l=0;
a[0].r=1;
for(int i=2;i<=n;i++){
int k,p;
cin>>k>>p;
if(p==1){
adr(i,k);
}else{
adl(i,k);
}
}
int m;
cin>>m;
for(int i=1;i<=m;i++){
int x;
cin>>x;
del(x);
}
int xx=a[0].r;
while(1){
cout<<xx<<" ";
if(a[xx].r==-1) break;
xx=a[xx].r;
}
cout<<endl;
}
结构体模拟链表
删除部分
void del(int x){
if(a[x].l==-1) return ;
a[a[x].l].r=a[x].r;
a[a[x].r].l=a[x].l;
a[x].l=-1;
a[x].r=-1;
}
这里a[a[x].l].r代表的就是当前数字 a[x].r代表就是当前数字的右边 所以这两个等于的意思就是当前数字左边的右边变成当前数字的右边
第二个的意思就是当前数字右边的左边指向当前数字的左边 这么一看,是不是中间的 b 部分就被孤立出来啦 所以总结一下删除的两个步骤
- 将当前元素的 右边元素 的左边 指向 当前元素 的左边
- 将当权元素的 左边元素 的右边 指向 当前元素 的右边
所以当前数字左右两边互相联系,当前数字就被删除了
右边添加数字部分
void adr(int x,int k){
a[x].l=k;
a[a[k].r].l=x;
a[x].r=a[k].r;
a[k].r=x;
}
第一句话的意思就是插入的数字的左边指向k 第二句话的意思是在k指向右边的数字的左边指向x
第三个的意思就是x的右边指向k的右边,也就是c 最后让k的右边变成x就可以了
- 将要添加的数字的左元素右边,右元素左边指向自己,就做到了添加数据的功能
左边添加数字部分
void adl(int x,int k){
a[x].r=k;
a[a[k].l].r=x;
a[x].l=a[k].l;
a[k].l=x;
}
这里恰好和上文相反,可以参照右边添加数字部分
所以结构体也是可以模拟链表的速度比链表还要快
(上链表,下结构体)
🏆结束语🏆
今天链表的学习就到这里了,如果大家有什么问题或者有意见可以在评论区中提出,相信在贤某的努力和大家的监督下一定会创作出更加优质的文章
后序会讲解更多关于算法的知识,感兴趣的各位可以订阅一下专栏呀
|