线性表--链表

       根据前面的学习我们知道了一些数据结构,最基本的是数组,以及栈和队列,都是基本的数据结构,他们统称为线性表,然后是线性表的另一个内容-链表,链表的意义和数组有些类似,我们知道数组实际上是一串连续的内存空间,而链表则是不连续的内存空间,所谓连续实际上就是地址连续,在数组中假设地址1后面就是地址2,后面又是地址3....但是假设将地址1后面穿根线链接地址3,再用线在地址3后穿起地址2这样就组成了不连续的数组也就是链表,可以说是特殊的数组

链表常见的有四种类型:
1)单链表     //只记录后继


2)双链表      //只记录前驱


3)循环单链表       //链尾记录链首


4)循环双链表        //同上


此外,结点有以下属性:
1.指针域。表示前驱和后继两种
2.数据域。存放该结点的数据值

因此根据属性可以定义一个结构体或者类来进行

其一般结构为:

struct node 
{ //一般将结点分为数据域,和指针域
int pre ,next;//指针域,用来指向前驱或后继
int  date; //数据域
//还可以根据题目另外设置其他属性
}

接下来是实例演示:

链接:

https://www.luogu.com.cn/problem/P1160

题目描述
一个学校里老师要将班上N个同学排成一列,同学被编号为1∼N,他采取如下的方法:

先将1号同学安排进队列,这时队列中只有他一个人;

2−N号同学依次入列,编号为i的同学入列方式为:老师指定编号为i的同学站在编号为1∼(i−1)中某位同学(即之前已经入列的同学)的左边或右边;

从队列中去掉M(M<N)个同学,其他同学位置顺序不变。

在所有同学按照上述方法队列排列完毕后,老师想知道从左到右所有同学的编号。

第1行为一个正整数N,表示了有N个同学。

第2−N行,第ii行包含两个整数k,p,其中k为小于i的正整数,p为0或者1。若p为0,则表示将i号同学插入到k号同学的左边,p为1则表示插入到右边

第N+1行为一个正整数M,表示去掉的同学数目。

接下来M行,每行一个正整数x,表示将x号同学从队列中移去,如果x号同学已经不在队列中则忽略这一条指令。

输入样例:
4
1 0
2 1
1 0
2
3
3
输出:
2 4 1

根据题目我们得知编号为一号的同学已经入队,因此只需要对剩下的三名同学进行如题放置,p=0,放左边;p=1,放右边(如上红字)

那么对于结点的插入和删除要先定义好

结点插入:

1.左接,将y接在x左边类似图中所示:

 让每个结点类似一个盒子,每个盒子有左右接口,要让y接入x左边那么首先为了书写方便先让y的左接口L链接z(也就是目前x的左接口)让y的右接口R链接x,先完成对y结点左右接口的设置,然后设置z结点(因为左接,所以左边的结点实际上是没有直接表示的只能用x的左边来表示,所以再设置其右接口为y,然后是x结点,将其左节点设置为y就完成了结点的左插入,

因此有代码:

void insertl(int x,int y)
{
      node[y].pre=node[x].pre;  //y的左接口为x的左接口
   node[y].next=x;           //y的右接口为x
      node[node[x].pre].next=y;   //z结点的右接口,也就是x结点的左接口的右接口
     node [x].pre=y;   //x结点的左接口为y

}

右插入结点同理:

void insertr(int x,int y)
{
   node [y].next=node[x].next;
    node[y].pre=x;
    node[node[x].next].pre=y;
    node[x].next=y;

}

删除结点

方式有很多种,这里在结构体中另设置了开关标志,控制最后链表输出时是否报数,也可以直接对结点进行删除,道理同上面结点的插入,对y结点进行删除让其左接口链接的z的右接口链接x也就是y的右接口链接的结点,让y结点右接口链接的x的左接口链接z,最后让y的左右接口链接-1,就完成删除了,当然还可以用new 指针开辟空间,用delete及时删除空间

因此整合代码如下:

#include<bits/stdc++.h>
using namespace std;
int n;
struct node{
int pre ,next;//这里只是简单的链表结构为了简化没有写date表明数据域,只是用了指针域  
bool flag;  
}node[100010];
/*为了符合题目设置了flag开关,表示该同学是否被删除,而没有对链表进行更改,当然也可用对链表进行修改的方式*/
void insertl(int x,int y)  //左边接入新结点
{
      node[y].pre=node[x].pre;      
   node[y].next=x;
      node[node[x].pre].next=y;    
     node [x].pre=y; 
}
void insertr(int x,int y)  //右边接入新结点
{
   node [y].next=node[x].next;
    node[y].pre=x;
    node[node[x].next].pre=y;
    node[x].next=y;

}
void dele(int x)  //删除结点,这里只是用了开关表示删除
{
    node[x].flag=0;
}
void csh()  //初始化,对链表进行初始化,也是可以在主函数中进行的
{
    for(int i=1;i<=n;i++) {node[i].flag=1; //先打开所有开关


}
    node[0].next=1;  //node[0]表示头节点,让头节点右边链接第一个有效结点,也就是同学1;


}
int main(){
ios::sync_with_stdio(false);
cin>>n;
int k,p;
csh();
for(int i=2;i<=n;i++)
{
    cin>>k>>p;
    if(p==0)  //题目要求 p=0左接,p=1右接
    insertl(k,i);
    else if(p==1)
        insertr(k,i);}
    int m,q;
    cin>>m;
    for(int i=1;i<=m;i++)
    { cin>>q;
    dele(q);
    }
int   h=node[0].next;  //备份头结点右接口
    int g=n;  

    while(g--)
    {
        if(node[h].flag==1)
            cout<<h<<" ";
        h=node[h].next;
    }
/*因为是设置了开关,所以只能循环规定次数,
但当在对链表修改时只需设置被删除的结点两边接口都等于-1,
那么最后一个结点的右接口就等于-1来终止循环即可
,也可用new分配动态空间,然后用delete及时删除也可以*/

return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Assault boy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值