• 单链表(详解)


    一.链表的介绍

    链表在物理结构上不一定是连续的
    逻辑结构上一定是连续的(可以通过前一个节点的指针找到下一个节点)

    链表是由一个一个的节点组成的,
    一个节点存储:指向下一个节点的指针和一个任意类型的数据
    由此可以知道链表可以通过前一个节点的指针找到下一个节点,
    各个节点就串联起来了

    也可以把链表类比成一列火车,火车(链表)有一列列车厢(节点)组成

    二.链表的各种方法

    链表也是一种顺序结构(是线性表的一种),和顺序表(底层是数组)一样,
    有许多的相似的方法

    test.c --测试链表方法的文件
    SList.h – 声明链表的实现方法和创建链表的结构
    SList.c – 实现链表的方法

    单链表的结构

    typedef int DataType;
    //将链表中的数据重命名为DataType,方便下次修改数据类型
    typedef struct SListNode
    {
    	DataType data;//单链表中的数据
    	struct SListNode* next;//指向下一个节点的指针
    }SListNode;
    //将链表的名字重命名,方便后续使用
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    初始化链表

    //初始化 
    //单链表的初始化没有节点
    //所以单链表也可以不用初始化
    //双向链表的初始化只有一个哨兵位
    SListNode* SLInit(SListNode* phead)
    {
    	phead = (SListNode*)malloc(sizeof(SListNode));
    	if (phead == NULL)
    	{
    		perror("malloc");
    		return NULL;
    	}
    	//申请成功
    	phead->next = NULL;
    
        return phead;
        //返回申请申请成功的节点
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    为链表开辟新节点

    //为链表开辟新节点
    SListNode* SLByNode(DataType x)
    {
    	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
    	if (newnode == NULL)
    	{
    		perror("malloc");
    		return NULL;
    	}
    	//开辟成功
    	newnode->next = NULL;
    	newnode->data = x;
    
    	return newnode;
    	//把开辟的新节点返回
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    打印链表

    //打印链表
    void SLPrint(SListNode* phead)
    {
    	SListNode* pcur = phead;
    	while (pcur)
    	// 节点不为空,打印
    	{
    		printf("%d->", pcur->data);
    		//打印当前节点
    		pcur = pcur->next;
            //指针往下走
    	}
    	printf("NULL\n");
    	//打印一行就换行
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    尾插

    //尾插
    void SLPushBack(SListNode** pphead,DataType x)
    {
    	assert(pphead);
    	// *pphead指向第一个头结点
    	//分为链表为空的尾插和不为空的尾插
    	SListNode* newnode = SLByNode(x);
    	if (*pphead == NULL)
    	{
    		*pphead = newnode;
    		//链表为空,头结点就是新节点的尾插
    	}
    	//链表不为空
    	SListNode* pcur = *pphead;
    	while (pcur->next)
    	{
    		pcur = pcur->next;
    	}
    	//现在pcur的next指针为NULL, pcur newnode 链接上
    	pcur->next = newnode;
    	newnode->next = NULL;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    头插

    //头插
    void SLPushFront(SListNode** pphead, DataType x)
    {
    	assert(pphead);
    	//链表不为空
    	SListNode* newnode = SLByNode(x);
    
        //链接 newnode *pphead
    	newnode->next = *pphead;
    	*pphead = newnode;
    	//newnode(*pphead)就是新的头节点
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    尾删

    //尾删
    void SLPopBack(SListNode** pphead)
    {
    	assert(pphead && *pphead);
    	//链表不能为空,节点不能没有
    	SListNode* pcur = *pphead;
    	SListNode* prev = *pphead;
    	if (pcur->next == NULL)
    	{
    		//只有一个节点
    		free(*pphead);
    		*pphead = NULL;
    	}
    	else 
    	{
    		//有二个及以上节点
    		while (pcur->next!=NULL)
    		{
    			prev = pcur;
    			pcur = pcur->next;
    		}
    		//prev指向了最后一个节点的前一个节点
    		//pcur指向了最后一个节点
    		prev->next = NULL;
    		free(pcur);
    		pcur = NULL;
    	}
    	
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    头删

    //头删
    void SLPopFront(SListNode** pphead)
    {
    	assert(pphead&&*pphead);
    	//空链表和空节点不能删
    	SListNode* pcur = (*pphead)->next;//->的优先级比*高,所以打括号
    	//先把头节点的下一个节点存起来
    	free(*pphead);
    	//释放头节点
    	*pphead = pcur;
        //头节点的下一个节点是新的头结点
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    查找

    //查找
    SListNode* SLCheck(SListNode* phead,DataType x)
    {
    	assert(phead);
    	SListNode* pcur = phead;
    	while (pcur)
    	{
    		if (pcur->data == x)
    		{
    			//找到了
    			return pcur;
    			//返回存储x的节点
    		}
    		pcur = pcur->next;
    	}
    	//没找到
    	return NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    指定位置之前插入

    //在指定位置之前插入数据
    void SLInsertF(SListNode** pphead,SListNode* pos,DataType x)
    {
    	assert(pphead&&*pphead);
    	//因为有指定位置,链表不能为空
    	SListNode* newnode = SLByNode(x);
    	//为插入的数据开辟一个节点
    	SListNode* pcur = *pphead;
    	while (pcur->next != pos)
    	{
    		pcur = pcur->next;
    	}
    	//找到pos之前的数据,在pos前插入数据
    	newnode->next = pcur->next;
    	//右边赋值给左边,pcur->next不会被改变
    	pcur->next = newnode;
    	//pcur还是指向它的下一个节点
    	//这两句不能交换
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    指定位置之后插入

    //在指定位置之后插入数据
    void SLInsertB(SListNode* pos, DataType x)
    {
    	assert(pos);
    	//不能没有节点
    	SListNode* newnode = SLByNode(x);
    	//为新节点申请一个节点
    	newnode->next = pos->next;
    	//右边赋值给左边,pcur->next不会被改变
    	pos->next = newnode;
    	//pcur还是指向它的下一个节点
    	//这两句不能交换
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    删除(pos)节点

    //删除pos节点
    void SLErase(SListNode** pphead, SListNode* pos)
    {
    	assert(pphead && pos);
    	assert(*pphead);
    	//链表为空和没有节点不能删除,指定删除的节点没有,不能删除
    	//可以分为只有一个节点和两个及以上节点两种情况
    	if ((*pphead)->next == NULL)
    	{
    		//只有一个节点
    		free(*pphead);
    		*pphead = NULL;
    	}
    	else
    	{
    		//有两个及以上节点
    		//先找到pos节点的前一个节点
    		SListNode* pcur = *pphead;
    		SListNode* plist = pos->next;
    		while (pcur->next != pos)
    		{
    			pcur = pcur->next;
    		}
    		//找到pos之前的节点
    		pcur->next = pos->next;
    		//  pcur  pos  pos->next 链接
    		//pcur指向pos的下一个节点,把pos释放
    		free(pos);
    		pos = NULL;
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    删除节点(pos)之后的节点

    等价于删除pos的下一个节点

    //删除pos之后的节点
    void SLEraseB(SListNode* pos)
    {
    	assert(pos);
    	//不能没有节点
    	SListNode* Del = pos->next->next;
    	// pos pcur Del
    	SListNode* pcur = pos->next;
    	//pos的下一个节点pcur
    	pos->next = Del;
    	free(pcur);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    链表的销毁(节点被一个一个地销毁)

    //销毁链表
    void SLDestroy(SListNode** pphead)
    {
       //一个节点一个节点地释放
    	assert(*pphead&&pphead);
    	//(没有节点)空链表不用释放,pphead为NULL
    	SListNode* pcur = *pphead;
    	SListNode* prev = *pphead;
    	while (pcur)
    	{
    		prev = prev->next;
    		//保存下一个节点
    		free(pcur);
    		pcur = prev;
    		//pcur就指向下一个节点
    	}
    	*pphead = NULL;
    	//临时变量pcur和*pphead指向同一块空间,把临时变量释放掉,还要把*pphead置为NULL
    	//防止野指针*pphead不用的时候
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    C++字符串字面量初始化““s的使用方法与示例
    <图像处理> Shi-Tomasi角点检测
    java 企业工程管理系统软件源码+Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis
    【云原生】Docker的初步认识,安装与基本操作
    onnxruntime C++推理
    3 基于采样的路径规划 —— RRT算法
    Qt版本的冷知识
    易观分析:以用户为中心,提升手机银行用户体验,助力用户价值增长
    【汇编】寄存器(学习笔记)
    JVM上篇之类加载子系统
  • 原文地址:https://blog.csdn.net/2301_79722622/article/details/137677462