《数据结构》第一章:线性表 第二节:单链表 【代码纯手打、每行注释(草履虫可学版)】

目录

第二节:单链表

链式存储结构

单链表结点结构

一、创建单链表

二、 单链表初始化

三、头插法

 四、尾插法

五、单链表遍历 

六、指定位置插入新节点

七、删除指定位置的节点

八、获取表长的方法

九、调用演示示例


第二节:单链表

链式存储结构

逻辑上相邻接的数据元素的存储地址不一定相邻接, 相邻关系通过指针来描述, 这种存储结构称为链式存储结构。

单链表结点结构

单链表每个节点包含两部分:数据域和指针域。

数据域用于存储实际的数据,指针域用于指向下一个节点,通过这种方式将各个节点连接成一个线性序列。

一、创建单链表

代码示例:

//创建链表
typedef struct Node
{
	//数据域
	ElementType data;
	//指针域
	struct Node* next;
}Node;

二、 单链表初始化

在单链表中,头节点的数据域通常不存储实际的数据。

代码示例:

//链表初始化
Node* InitList()
{
	//头节点堆内存分配
	Node* head = (Node*)malloc(sizeof(Node));
	//头节点数据域初始化为0
	head->data = 0;
	//头节点指针域初始化为空
	head->next = NULL;
	//返回头节点
	return head;
}

    (Node*) 是强制类型转换,将 malloc 返回的 void* 类型指针转换为 Node* 类型指针。

    三、头插法

    头插法是指在链表的头节点之后插入一个新节点,使得新节点成为链表的第一个有效节点。

    代码示例:

    /头插法
    int insertHead(Node* head, ElementType e)
    {
    	//新节点申请堆内存分配
    	Node* p = (Node*)malloc(sizeof(Node));
    	//新节点数据域初始化为e
    	p->data = e;
    	//新节点指针域初始化为头节点的下一个节点
        p->next = head->next;
    	//头节点指向新节点
        head->next = p;
    	return 1;
    }

     四、尾插法

    尾插法是指将新节点插入到链表的尾部。与头插法不同,尾插法插入节点后,链表中节点的顺序与插入顺序一致。在进行尾插操作时,需要先找到链表的尾节点,然后将新节点连接到尾节点之后,并更新尾节点为新插入的节点。

    代码示例:

    //尾插法
    int insertTail(Node* head, ElementType e)
    {
    	//遍历链表,找到最后一个节点
        Node* p = head;
        while (p->next != NULL)
        {
            p = p->next;
        }
    	//新节点申请堆内存分配
        Node* p_new = (Node*)malloc(sizeof(Node));
    	//新节点数据域初始化为e
    	p_new->data = e;
    	//新节点指针域初始化为空
    	p_new->next = NULL;
    	//当前最后一个节点指向新节点
    	p->next = p_new;
        return 1;
    }

    五、单链表遍历 

    代码功能概述:接收一个指向单链表头节点的指针作为参数,然后根据链表的状态进行不同处理,如果链表为空,会输出提示信息,如果链表不为空,则依次输出链表中每个节点的数据域。

    代码示例:

    //单链表遍历
    void TraverseList(Node* head)
    {
    	//头节点的下一个节点为空,则链表为空
    	if (head->next == NULL)
    	{
    		printf("链表为空\n");
    		return;
    	}
    	//头节点的下一个节点不为空,则链表不为空
    	else
    	{
    		//当前节点为头节点的下一个节点
    		Node* p = head->next;
    		//遍历链表
    		while (p != NULL)
    		{
    			//输出当前节点的数据域
    			printf("%d ", p->data);
    			//当前节点指向下一个节点
    			p = p->next;
    		}
    		printf("\n");
    	}
    }

    六、指定位置插入新节点

    遍历链表,找到指定位置的前一个节点。将数据存入新节点中,上一节点指向新节点,新节点指向上一节点指向的下一节点。

    过程示意:

    代码示例:

    //指定位置插入节点
    int insertNode(Node* head, int pos, ElementType e)
    {
        if (pos < 1)
        {
            printf("插入位置错误\n");
            return 0;
        }
        else if (pos == 1)
        {
            //头插法
            insertHead(head, e);
            return 1;
        }
        else
        {
            //遍历链表,找到指定位置的前一个节点
            Node* p = head;
            for (int i = 1; i < pos - 1; i++)
            {
                if (p->next == NULL)
                {
                    printf("插入位置错误\n");
                    return 0;
                }
                p = p->next;
            }
            //新节点申请堆内存分配
            Node* p_new = (Node*)malloc(sizeof(Node));
            //新节点数据域初始化为e
            p_new->data = e;
    		//新节点指针域初始化为当前位置的下一个节点
            p_new->next = p->next;
    		//当前位置的下一个节点指向新节点
            p->next = p_new;
            return 1;
        }
    }

    七、删除指定位置的节点

    思想:第一步,找到指定位置的前一个节点:从链表头节点开始遍历,找到要删除节点的前一个节点。第二步,将前一个节点的 next 指针指向要删除节点的下一个节点,然后释放要删除节点的内存。

    代码示例:

    //删除指定位置节点
    int deleteNode(Node* head, int pos)
    {
        if (pos < 1)
        {
            printf("删除位置错误\n");
            return 0;
        }
        else if (pos == 1)
        {
            //头节点指向下一个节点
            head->next = head->next->next;
            return 1;
        }
        else
        {
            //遍历链表,找到指定位置的前一个节点
            Node* p = head;
            for (int i = 1; i < pos - 1; i++)
            {
                if (p->next == NULL)
                {
                    printf("删除位置错误\n");
                    return 0;
                }
                p = p->next;
            }
            if (p->next == NULL)
            {
                printf("删除位置错误\n");
                return 0;
            }
            p->next = p->next->next;
            return 1;
            printf("删除成功\n");
            return 1;
        }
    }
    

    八、获取表长的方法

    遍历到尾节点即可

    代码示例:

    //获取单链表的长度
    int getLength(Node* head)
    {
        //头节点的下一个节点为空,则链表为空
        if (head->next == NULL)
        {
            return 0;
        }
        //头节点的下一个节点不为空,则链表不为空
        else
        {
            //当前节点为头节点的下一个节点
            Node* p = head->next;
            //链表长度初始化为0
            int length = 0;
            //遍历链表
            while (p != NULL)
            {
                //链表长度加1
                length++;
                //当前节点指向下一个节点
                p = p->next;
            }
            return length;
        }
    }

    九、释放链表

    代码示例:

    //释放链表
    void FreeList(Node* head)
    {
    	//头节点的下一个节点为空,则链表为空
    	if (head->next == NULL)
    		printf("链表为空\n");
    	//头节点的下一个节点不为空,则链表不为空
    	else
    	{
    		//当前节点为头节点的下一个节点
    		Node* p = head->next;
    		//遍历链表
    		while (p != NULL)
    		{
    			//当前节点指向下一个节点
    			Node* q = p->next;
    			//释放当前节点
    			free(p);
    			//当前节点指向下一个节点
    			p = q;
    		}
    		//头节点指向空
            head->next = NULL;
            printf("链表释放成功\n");
    	}
    }

    十、调用演示示例

    代码示例:

    int main()
    {
    	//初始化链表
    	Node* list = InitList();
        printf("链表初始化成功\n");
        printf("链表长度为%d\n", getLength(list));
        //头插法
        insertHead(list, 1);
        insertHead(list, 2);
        insertHead(list, 3);
        //遍历链表
        TraverseList(list);
        printf("链表长度为%d\n", getLength(list));
        //尾插法
        insertTail(list, 5);
        insertTail(list, 6);
        insertTail(list, 7);
        //遍历链表
        TraverseList(list);
        printf("链表长度为%d\n", getLength(list));
        //指定位置插入节点
        insertNode(list, 2, 3);
        //遍历链表
        TraverseList(list);
        printf("链表长度为%d\n", getLength(list));
        //删除指定位置节点
        deleteNode(list, 2);
        //遍历链表
        TraverseList(list);
        printf("链表长度为%d\n", getLength(list));
    	return 0;
    }

    运行结果:

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    打赏作者

    陌北、深度学习

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

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

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

    打赏作者

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

    抵扣说明:

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

    余额充值