【数据结构基础笔记】【链表】

代码参考《妙趣横生的算法.C语言实现》


前言

本章总结:链表的定义、创建、销毁,结点的插入与删除


1、链表基础

链表的物理存储结构是用一组地址任意的存储单元存储数据的。
在链表结构中,每个数据元素记录都存放在链表的一个结点node中,而每个结点之间由指针将其连接到一起。
每个结点由指针域(存放后继结点的位置)、数据域构成。
一个链表通常有一个表头,是一个指针变量,用来存放第一个结点地址。
链表的最后一个结点的的指针域要置空,表示为链表的尾结点。
在这里插入图片描述
链表特点:
1、每个结点包括两个部分:数据域和指针域
数据域用来存放数据元素本身信息,指针域用来存放后继结点的地址
2、链表逻辑上是连续的,但物理上不一定是连续存储结点。
3、只要获取链表的头结点,就可以通过指针遍历整条链表
一个链表结点可以描述为:

typedef struct node{
    
    
    ElemType data;		//数据域
    struct node *next;	//指针域
}LNode,*LinkList;

每个结点的类型是LNode
*LinkList是指向LNode类型数据的指针类型定义。
所以 LNode *L 与 LinkList L; 是等价的。

2、创建一个链表

LinkList GreatLinkList(int n)
{
    
    
    //建立一个长度为n的链表
    LinkList p,r,list=NULL;		//p:相当于每次新建结点的暂存器,r:相当于插入结点的上一个结点,永远指向原先链表的最后一个结点。list链表的头指针
    ElemType elem;				//获取暂存数据
    int i;						//定义累加器
    for(i=0;i<n;i++)
    {
    
    
        scanf("%d",&elem);
        p=(LinkList)malloc(sizeof(LNode));	//分配内存,并将首地址送到p
        p->data=elem;			//置入数据
        p->next =NULL;			//指针指向NULL,暂时不考虑下一个结点
        if(!list)				//如果链表为空,则新创建的结点就是该链表的第一个结点
            list=p;
        else					//如果链表不为空,则将新建立的结点连接到之前链表的尾部
            r->next=p;
        r=p;					//将p结点的数据赋给r
    }
    return list;				//将链表的头指针返回主调函数,通过list就可以访问链表中的每个结点,并进行操作	
}

3、插入结点

步骤描述:
1、创建新节点,用指针p指向该结点
2、将q指向结点的next域的值赋值给p指向结点的next域
3、将p的值赋值给q的next域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ynbLqOhX-1600526323871)(C:\Users\15409\AppData\Roaming\Typora\typora-user-images\1600507457502.png)]
代码描述:

void insertList(LinkList *list,LinkList q,ElemType e)
{
    
    
    //向链表中由指针q指向的结点后面插入结点,结点数据位e
    LinkList p;
    p=(LinkList)malloc(sizeof(LNode));		//生成一个新节点,由p指向它
    p->data=e;
    if(!*list)				//如果链表为空
    {
    
    
        *list=p;
        p->next=NULL;
    }						//当链表为空的时候,q没有意义,只能在头结点后面插入第一个元素
	else
    {
    
    
        //当链表不为空的时候,认为q指向的结点一定存在
        //将q指向的结点的next域的值赋给p指向的结点的next域
        p->next=q->next;
        q->next=p; 
    }
}

通过这个算法同样可以创建一个链表,因为链表为空时,list==NULL,可以自动创建一个结点。在下面创建其他结点时,只要始终将指针q指向链表的最后一个结点,就可以创建出一个链表

4、删除结点

从非空链表中删除q所指的结点。
考虑三个情况:1、q指向的是链表的第一个结点
2、q指向的结点的前驱结点的指针已知
3、q指向的结点的前驱结点的指针未知
步骤:
1:将q所指的结点的指针域next的值赋给头指针list,让list指向第二个结点,再释放掉q所指的结点即可。
2:假设前驱指针为r,将q所指的结点的指针域next的值赋给r的指针域next,释放掉q所指结点
3:当q所指的结点的前驱结点的指针未知,需要通过链表头指针list遍历链表,找到q的前驱结点,并将该指针赋值给变量r,再按照第二种情况去做即可

情况1、2的代码描述:

void delLink(LinkList *list,LinkList r,LinkList q)
{
    
    
    if(q==*list)		//情况1:q指向链表的第一个结点
        *list=q->next;
    else				//情况2:q指向的结点前驱结点的指针已知
        r->next=q->next;
    free(q);
}

情况1、3的代码描述:

void delLink(LinkList *list,LinkList r,LinkList q)
{
    
    
    if(q==*list)//情况1:q指向链表的第一个结点
    {
    
    
        *list=q->next;  
        free(q);
    }
    else				//情况3:q指向的结点前驱结点的指针未知
    {
    
    
       for(r=*list;r->next!=q;r=r->next);	//遍历链表,找到q的前驱结点的指针
       if(r->next!=NULL)
       {
    
    
           r->next=q->next;			//从链表中删除q指向的结点
           free(q);
       }
    } 
}

5、销毁链表

使用链表之后需要销毁它,因为链表本身会占用内存。
code描述:

void destroyLinkList(LinkList *list)
{
    
    
    LinkList p,q;
    p=*list;
    while(p)
    {
    
    
        q=p->next;
        free(p);
        p=q;
    }
    *list=NULL;
}

6、实例分析

要求:
输入一组整数(大于10个数),以0作为结束标志,将这组整数存放到一个链表中(结束标志0不包括在内),打印出该链表中的值。然后删除该链表中的第5个元素,打印出删除后的结果。最后在内存中释放掉该链表。

#include "stdio.h"
#include "malloc.h"
#include "conio.h"

typedef int ElemType;
//指针定义
typedef struct node {
    
    
    ElemType data;		//数据域
    struct node* next;	//指针域
}LNode, * LinkList;

//***********创建链表******************//
//
LinkList GreatLinkList(int n)
{
    
    
    //建立一个长度为n的链表
    LinkList p, r=NULL, list = NULL;		//p:相当于每次新建结点的暂存器,r:相当于插入结点的上一个结点,永远指向原先链表的最后一个结点。list链表的头指针
    ElemType elem;				//获取暂存数据
    int i;						//定义累加器
    for (i = 0;i < n;i++)
    {
    
    
        scanf("%d", &elem);
        p = (LinkList)malloc(sizeof(LNode));	//分配内存,并将首地址送到p
        p->data = elem;			//置入数据
        p->next = NULL;			//指针指向NULL,暂时不考虑下一个结点
        if (!list)				//如果链表为空,则新创建的结点就是该链表的第一个结点
            list = p;
        else					//如果链表不为空,则将新建立的结点连接到之前链表的尾部
            r->next = p;
        r = p;					//将p结点的数据赋给r
    }
    return list;				//将链表的头指针返回主调函数,通过list就可以访问链表中的每个结点,并进行操作	
}

//*************插入结点************//
//
void insertList(LinkList* list, LinkList q, ElemType e)
{
    
    
    //向链表中由指针q指向的结点后面插入结点,结点数据位e
    LinkList p;
    p = (LinkList)malloc(sizeof(LNode));		//生成一个新节点,由p指向它
    p->data = e;
    if (!*list)				//如果链表为空
    {
    
    
        *list = p;
        p->next = NULL;
    }						//当链表为空的时候,q没有意义,只能在头结点后面插入第一个元素
    else
    {
    
    
        //当链表不为空的时候,认为q指向的结点一定存在
        //将q指向的结点的next域的值赋给p指向的结点的next域
        p->next = q->next;
        q->next = p;
    }
}
//通过这个算法同样可以创建一个链表,因为链表为空时,list==NULL,可以自动创建一个结点。在下面创建其他结点时,只要始终将指针q指向链表的最后一个结点,就可以创建出一个链表

//删除结点
void delLink(LinkList* list, LinkList q)
{
    
    
    LinkList r;
    if (q == *list)//情况1:q指向链表的第一个结点
    {
    
    
        *list = q->next;
        free(q);
    }
    else				//情况3:q指向的结点前驱结点的指针未知
    {
    
    
        for (r = *list;r->next != q;r = r->next);	//遍历链表,找到q的前驱结点的指针
        if (r->next != NULL)
        {
    
    
            r->next = q->next;			//从链表中删除q指向的结点
            free(q);
        }
    }
}

//销毁链表
void destroyLinkList(LinkList* list)
{
    
    
    LinkList p, q;
    p = *list;
    while (p)
    {
    
    
        q = p->next;
        free(p);
        p = q;
    }
    *list = NULL;
}


void print_linklist(LinkList show_list)
{
    
    
    while (show_list)
    {
    
    
        printf("%d ",show_list->data);
        show_list = show_list->next;
    }
}
int main()
{
    
    
    int elem = 0;   //定义中间变量数据
    int i = 0;      //定义累加器
    LinkList L, q;  
    q = L = GreatLinkList(1);       //创建1个链表结点,q和L指向该结点
    scanf("%d",&elem);
    while (elem)                    //循环地输入数据,同时插入新生成的结点,结束条件:输入数据为0
    {
    
    
        insertList(&L,q,elem);
        q = q->next;
        scanf("%d", &elem);
    }
    q = L;
    printf("The content of the linklist\n");
    print_linklist(q);
    q = L;
    printf("\n Delete the fifth element\n");
    for (i=0;i<4;i++)           //将指针q指向第五个元素
    {
    
    
        if (q == NULL)     //确保此时链表的长度大于等于5,否则是非法操作
        {
    
    
            printf("The length of the linklist is smaller than 5");
            _getche();
            return 0;
        }
        q = q->next;
    }
    delLink(&L,q);
    q = L;
    print_linklist(q);
    destroyLinkList(&L);
    return 0;
}

result:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42604176/article/details/108686913