深度理解单链表

目录

一:什么是链表:

二:单链表的特点:

三:单链表头节点的初始化:

初始化单链表:

元素得增加:

输出单链表中所有的元素

单链表元素的删除:

单链表元素的查找


以自行车的链条来比喻我们的单链表,或者双链表


单链表是线性表的一种,因为从逻辑上来看单链表是一条线
节点由指针域和数据域组成,所以一个节点含有两个不同类型的元素,那么我们学过的知识中,那种数据类型可以
保存不同类型的元素呢,那就是结构体,所以我们可以用结构体来定义一个节点

struct node
{
    int id;//数据域,节点要保存的数据
    struct node*next://指针成员,用于指向下一节点
};


单链表只要知道一个节点,那么就可以知道之后的任意节点位置,直接通过指针域就可以查找到,所以对于
单链表来说,指针域是灵魂,顺序表的内存地址都是连续的,那么增加删除数据就非常的麻烦,所以我们用
单链表,不需要元素都相邻,用一个指针域来找到其他节点,这样即使不相邻,也可以找到单链表中所有的元素

一:什么是链表:


链表是数据结构中线性表的一种,其中的每个元素实际上是一个单独的结构体对象,而所有对象都通过每个
元素中的指针连接在一起,它是以结构体为节点,将一个结构体看成数据域和指针域两个部分,数据域用于
存储数据,指针域用于链接下一个节点,链表中每个结构体对象叫做节点,其中第一个数据节点叫做链表的
首元节点,如果第一个节点不用于存储数据,只用于代表链表的起始点,则这个节点称为链表的头结点
也就是说头节点和首元节点的区别就在于,是否存储数据,而头结点并不是单链表的必需,可以有也可以没有
不保存数据,仅仅是作为单链表的开头,指向单链表,我们叫它头节点


二:单链表的特点:


1:单链表没有固定的长度,可以自由增加节点
2:单链表能够实现快速的插入删除数据
3:与数组类似,单链表也是一种线性数据结构
4:单链表的尾节点的后继必定指向空
单链表和数组的区别就在于:数组是顺序存储的,这就是为什么可以用数组表示一个顺序表,而单链表是链式存储的


三:单链表头节点的初始化:


头指针变量:保存单链表节点的首地址,因为知道了单链表节点的首地址,也就是第一个元素的地址,那么根据节点的指针域,依次寻找就可以找到我们想要的任意元素
struct node*head = NULL;


初始化单链表:


申请一个首元节点:让head指向这个节点
定义了头指针变量,就是为了让这个头指针变量保存首元节点的内存地址,那么我们现在还没有一个首元节点
所以我们要创建一个首元节点,让这个头指针指向这个首元节点,那么我们就先申请一个首元节点呗,那就定
义一个函数,用来申请首元节点呗,然后把这个首元节点的地址返回出来,交给头节点指针保存

struct node*init()
{
    struct node*temp = (struct node*)malloc(sizeof(struct node))//temp是一个临时变量,用来保存从
        //堆区中申请出来的内存的首地址
        temp->id = 0;
    temp->next = NULL;
    return temp;

}
head = init()//通过调用inti函数,将申请的首元节点的地址temp交给头指针节点head保存,这样我们就有了
//一个首元节点*temp,需要注意的是虽然我们单独定义了一个函数用来申请首元节点,但是仅仅是申请了这样
//一个地址,并不代表我们就拥有了一个首元节点,首元节点只有是属于单链表中的时候才有意义,所以要把
//首元节点的地址交给头指针变量head保存,这样和head共同构成一个含有两个节点:头节点,首元节点的单
//链表

元素得增加:


头插法思想:从前面开始插入,可以理解为排队,大家都想排第一,那么来插队的也肯定插队首的位置,
头插法三步骤:以单链表中只有两个节点:头节点和首元节点为例子,插入的元素先和后面的元素
相连,插入的元素变为首元节点,原本的首元节点和头结点的连接断开,头结点改为连接新来的首元节点,而
首元节点此时连着原本的首元节点
具体表现为:头节点连接新首元节点连接旧首元节点

头插法三步骤:
1;申请节点,把数据放进这个新节点s
2:新节点s指针域保存头结点的下一个节点的地址
3:头结点指针保存新节点s的地址

代码体现:
 

void insert(struct node*head, int date)//head 用来接收首元节点的地址,date用来接收要增加的元素
{
    struct node*s = (struct node*)malloc(sizeof(struct node));
    s->id = date;//保存要增加的元素值
    s->next = head->next;//新节点保存申请的头结点
    head ->next= s;
}

尾插法三步骤:


1:定义一个指针变量P,P用来保存当前单链表中的最后一个节点的地址
2:定义一个指针变量s,申请一个新的节点(保存要增加的数据,生成新节点s)
3:新节点s指针为空,节点P的指针域指向新节点s


 

void inserEnd(struct node*head, int date)//date用来接收新增加的数据
{
    struct node*p = head;
    while (p->next != NULL)//p不是单链表中最后一个节点
    {
        //p++;不可以的,因为内存不连续
        p = p->next;//P指向下一个节点
    }
    //循环执行完之后,P指向单链表中的最后一个节点
    //定义一个指针变量S,申请一个新的节点(保存要增加的数据),生成新的节点S
    struct node*s = (struct node*)malloc(sizeof(struct node));
    s->id = date;//新节点保存要增加的元素值
    //新节点S指针为空,节点P的指针域指向新节点S
    s->next = p->next;
    p->next = s;
}

输出单链表中所有的元素

void print(struct node*head)
{
    struct node*p = head->next;
    while (p->next != NULL)
    {
        printf("%d -->", p->id);
        p = p->next;
    }
    printf("NULL\n\n");
}

单链表元素的删除:


1:定义两个指针变量p1 p2  p1指向头节点,p2指向头结点的下一个节点
2:使用循环,使p1,p2同时往后移,当P2指向要删除的这个节点时,结束循环
3:P1指向的节点里面的指针,保存P2下一个节点的地址
4:释放P2指向的节点
代码:
 

void del(struct node*head, int val)//val表示要被删除的值
{
    //P1指向删除节点的前驱节点
    //P2指向删除节点
    struct node*P1 = head;
    struct node*P2 = head->next;
    while (p2 != NULL)//p2不为NULL,正在遍历单链表中的数据
    {
        if (p2->id == val)//当P2指向要被删除的节点时
        {
            p1->next = p2->next;
            free(p2);
            p2 = p1->next;
        }
        else//两个指针变量同时往后移
        {
            p1 = p1->next;
            p2 = p2->next;
        }
    }
}


单链表元素的查找


 

int find(struct node*head, int val)//val表示要查找的值
{
    int flag = 0;//标记是第几个节点
    struct node*p = head->next;
    while (p != NULL)
    {
        flag++;
        if (p->id = val)//找到要查找的值
        {
            printf("这个值在第%个位置\n\n", flag);
            return p->id;
        }
        p = p->next;
    }
}

单链表节点的释放:不用考虑链表断没断,只要全部释放就好了。

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

翘指

创作不易,一分也是爱

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

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

打赏作者

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

抵扣说明:

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

余额充值