今日学习目标:
学习算法-数据结构-链表
✅创作者:贤鱼
⏰预计时间:30分钟
个人主页:贤鱼的个人主页
专栏系列:算法
贤鱼的个人社区,欢迎你的加入 贤鱼摆烂团
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。
每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
每一个单元都指向下一位
和单链表差不多,只不过第一位和最后一位相连,变成了循环链表
头文件
#include
插入
前方插入push_front(数字)
在链表最前面插入一个数字
后方插入push_back(数字)
链表最后面插入一个数字
中间插入insert(迭代器,数字)
在迭代器指向的链表元素前面插入一个数字
删除
erase(迭代器)
删除迭代器指向的链表元素
查找
begin()
链表头
end()
链表末尾
需要迭代器储存
输出
需要迭代器,后文会讲
结构体模拟部分看例题解法2
list::iterator 名字
迭代器是一种检查容器内元素并遍历元素的数据类型
可以理解为数组的下标
举个例子
list<int>::iterator pos[10005];
list<int> m;
pos[5]//迭代器代表的内容就是链表中元素为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
在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。
第 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 个空格隔开的正整数,表示了队列从左到右所有同学的编号,行末换行且无空格。
4
1 0
2 1
1 0
2
3
3
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就是当前数字的左边
这样子也可以模拟链表
其他具体内容下列题解见
第一种是用链表的做法,来巩固一下上文学习的知识
#include
#include
#include
#include
#include
using namespace std;
typedef list<int>::iterator iter;//这个的意思是iter代表前面迭代器部分,不然手打好麻烦(你问我为啥不用下头两个??我觉得颜色放一起好看~)
//using Iner =;
//const int maxn=xxx;常量
iter next(iter it){//这里手写一个next函数,意思就是指向传入参数迭代器的下一位
it++;
return it;
}
iter pos[100005];//定义迭代器
list<int>m;//定义链表
int f[100005];
int n;
void build(){
m.push_front(1);//链表第一位插入1
pos[1]=m.begin();//储存
for(int i=2;i<=n;i++){
int k,p;
cin>>k>>p;
if(p==0){//这个是在左边插入,直接用insert就可以了
pos[i]=m.insert(pos[k],i);//意思是在数组k的左边插入数字i
}else{
iter nextx=next(pos[k]);//这里右边插入需要处理一下,利用手写next函数指向下一位迭代器(老版本无法直接用next函数,所以手打)
pos[i]=m.insert(nextx,i);//在数字k的下一位的前面插入i
}
}
int mm;
cin>>mm;
for(int i=1;i<=mm;i++){
int x;
cin>>x;
if(!f[x]) m.erase(pos[x]);//删除链表中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;//意思是输出ib迭代器指向的数字
f1=0;
}
cout<<endl;
return 0;
}
#include
#include
#include
#include
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
左边添加数字部分
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;
}
所以结构体也是可以模拟链表的速度比链表还要快
(上链表,下结构体)
今天链表的学习就到这里了,如果大家有什么问题或者有意见可以在评论区中提出,相信在贤某的努力和大家的监督下一定会创作出更加优质的文章
后序会讲解更多关于算法的知识,感兴趣的各位可以订阅一下专栏呀