数据结构:单链表的实现(C语言)

在这里插入图片描述

个人主页 : 水月梦镜花
个人专栏 : 《C语言》 《数据结构》

文章目录

  • 前言
  • 一、单链表实现思路和图解
    • 1.节点的定义(SListNode)
    • 2.申请一个节点(BuySListNode)
    • 3.单链表打印(SListPrint)
    • 4.单链表尾插(SListPushBack)
    • 5.单链表的头插(SListPushFront)
    • 6.单链表的尾删(SListPopBack)
    • 7.单链表头删(SListPopFront)
    • 8.单链表的查找(SListFind)
    • 9.单链表在pos位置之后插入x(SListInsertAfter)
    • 10.单链表删除在pos位置之后的值(SListEraseAfter)
    • 11.单链表的销毁(SListDestroy)
  • 二、代码实现
  • 总结


前言

本博客将要实现的无头单向不循环链表。


一、单链表实现思路和图解

1.节点的定义(SListNode)

我们将节点定义为如下结构:
在这里插入图片描述
其成员变量有data,next。

将int重命名为STLDataType,方便我们以后修改数据域的内容。

//无头单向不循环链表
typedef int SLTDataType;typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SListNode;

2.申请一个节点(BuySListNode)

动态申明一个空间,来放置数据。如下:
在这里插入图片描述
将data的内容置成形参x,next置NULL。

//申请一个节点
SListNode* BuySListNode(SLTDataType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc");exit(-1);}newnode->data = x;newnode->next = NULL;retur

3.单链表打印(SListPrint)

循环遍历链表,直到尾节点。创建一个结构体指针变量cur,循环cur = cur->next,并打印cur->data的内容,直到cur == NULL。
在这里插入图片描述

//单链表打印
void SListPrint(SListNode* plist)
{SListNode* cur = plist;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}

4.单链表尾插(SListPushBack)

  • 如果链表不为NULL(链表中有元素),要先遍历链表找到尾节点,在让尾节点指向新节点,完成尾插。
  • 如果链表为NULL(链表中没有元素),此时应该直接让新节点等于头结点,完成尾插。(本链表是无哨兵位的)
  • 如果传入的头结点无效,直接判错。

在这里插入图片描述

在这里插入图片描述
当链表为NULL,我们就要修改头结点本身的内容,所以我们需要头结点的指针,而本文中头结点本身就是结构体指针,所以尾插函数参数我们需要二级指针。

//单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{assert(pplist);SListNode* newnode =  BuySListNode(x);if (*pplist != NULL){SListNode* cur = *pplist;while (cur->next != NULL){cur = cur->next;}cur->next = newnode;}else{*pplist = newnode;}
}

5.单链表的头插(SListPushFront)

对于头插而言,链表是否有元素并不重要,我们只需要让新节点的指向头结点,并将头结点等于新节点。

在这里插入图片描述
因为头插链表,一定会改变头结点的内容,所以我们头插函数的形参也是二级指针。

//单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x)
{assert(pplist);SListNode* newnode = BuySListNode(x);newnode->next = *pplist;*pplist = newnode;
}

6.单链表的尾删(SListPopBack)

  • 如果链表元素有两个及两以上,我们需要两个指针变量prev,next来找尾节点,循环prev = cur,cur = cur->next,使next指向尾节点,prev指向尾节点的前一个,free尾节点,prev指向的节点指向NULL。
  • 如果链表元素只有一个,直接free头结点,使头结点置NULL。
  • 如果链表没有元素,直接判错。

在这里插入图片描述

在这里插入图片描述
当链表元素只有一个时,此时尾删链表,要修改头结点的内容,尾删函数的形参需要二级指针。

//单链表的尾删
void SListPopBack(SListNode** pplist)
{assert(pplist);//链表为NULLassert(*pplist);if ((*pplist)->next == NULL){free(*pplist);*pplist = NULL;}else{SListNode* cur = *pplist;SListNode* prev = NULL;while (cur->next != NULL){prev = cur;cur = cur->next;}prev->next = NULL;free(cur);}
}

7.单链表头删(SListPopFront)

我们需要一个结构体指针变量next,来保存头结点的下一个节点,然后free头结点,使头结点 = 指针变量next。

  • 如果链表没有元素,直接判错。

在这里插入图片描述

//单链表头删
void SListPopFront(SListNode** pplist)
{assert(pplist);assert(*pplist);SListNode* next = (*pplist)->next;free(*pplist);*pplist = next;
}

8.单链表的查找(SListFind)

我们需要一个结构体指针变量cur,来遍历链表比较cur->data == x,如果相等,放回此时cur的内容(该节点的地址)。如果遍历完链表,并没有相等元素,放回NULL。

//单链表的查找
SListNode* SListFind(SListNode* plist, SLTDataType x)
{SListNode* cur = plist;while (cur != NULL){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}

9.单链表在pos位置之后插入x(SListInsertAfter)

我们让newnode指向pos下一个的节点,pos指向newnode。

  • 如果我们先让pos指向newnode,newnode在指向pos的下一个节点,就会造成newnode指向自己,导致链表成环。
  • 该函数不会影响头结点的内容,所以函数的形参不用二级指针。

在这里插入图片描述


//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}

10.单链表删除在pos位置之后的值(SListEraseAfter)

我们需要一个结构体指针变量next,记录pos下一个节点的地址,然后是pos指向next的下一个节点,接着free(next)。

  • 如果链表只有一个元素,我们不能调用该函数,否则会导致NULL指针的解引用。
  • 该函数不会改变头结点的内容,所以形参我们不用二级指针。

在这里插入图片描述


//单链表删除在pos位置之后的值
void SListEraseAfter(SListNode* pos)
{assert(pos && pos->next);SListNode* next = pos->next;pos->next = next->next;free(next);
}

11.单链表的销毁(SListDestroy)

我们需要两个结构体指针prev,cur。先让prev = cur ,cur再指向下一个节点,free(prev),重复上述操作,直到cur指向NULL。

在这里插入图片描述
需要我们在函数调用完后,自己对头结点置NULL。

//单链表的销毁
void SListDestroy(SListNode* plist)
{assert(plist);SListNode* cur = plist;while (cur != NULL){SListNode* prev = cur;cur = cur->next;free(prev);}
}

二、代码实现

//slist.h  文件#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>
#include <stdlib.h>
#include <assert.h>//无头单向不循环链表
typedef int SLTDataType;typedef struct SListNode
{SLTDataType data;struct SListNode* next;
}SListNode;//申请一个节点
SListNode* BuySListNode(SLTDataType x);//单链表打印
void SListPrint(SListNode* plist);//单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x);//单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x);//单链表的尾删
void SListPopBack(SListNode** pplist);//单链表头删
void SListPopFront(SListNode** pplist);//单链表的查找
SListNode* SListFind(SListNode* plist, SLTDataType x);//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x);//单链表删除在pos位置之后的值
void SListEraseAfter(SListNode* pos);//单链表的销毁
void SListDestroy(SListNode* plist);
//slist.c    文件#include "slist.h"//申请一个节点
SListNode* BuySListNode(SLTDataType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc");exit(-1);}newnode->data = x;newnode->next = NULL;return newnode;
}//单链表打印
void SListPrint(SListNode* plist)
{SListNode* cur = plist;while (cur != NULL){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}//单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{assert(pplist);SListNode* newnode =  BuySListNode(x);if (*pplist != NULL){SListNode* cur = *pplist;while (cur->next != NULL){cur = cur->next;}cur->next = newnode;}else{*pplist = newnode;}
}//单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x)
{assert(pplist);SListNode* newnode = BuySListNode(x);newnode->next = *pplist;*pplist = newnode;
}//单链表的尾删
void SListPopBack(SListNode** pplist)
{assert(pplist);//链表为NULLassert(*pplist);if ((*pplist)->next == NULL){free(*pplist);*pplist = NULL;}else{SListNode* cur = *pplist;SListNode* prev = NULL;while (cur->next != NULL){prev = cur;cur = cur->next;}prev->next = NULL;free(cur);}
}//单链表头删
void SListPopFront(SListNode** pplist)
{assert(pplist);assert(*pplist);SListNode* next = (*pplist)->next;free(*pplist);*pplist = next;
}//单链表的查找
SListNode* SListFind(SListNode* plist, SLTDataType x)
{SListNode* cur = plist;while (cur != NULL){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{assert(pos);SListNode* newnode = BuySListNode(x);newnode->next = pos->next;pos->next = newnode;
}//单链表删除在pos位置之后的值
void SListEraseAfter(SListNode* pos)
{assert(pos && pos->next);SListNode* next = pos->next;pos->next = next->next;free(next);
}//单链表的销毁
void SListDestroy(SListNode* plist)
{assert(plist);SListNode* cur = plist;while (cur != NULL){SListNode* prev = cur;cur = cur->next;free(prev);}
}

总结

以上就是我对于无头单向不循环链表的实现!!!

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/52869.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[迁移学习]领域泛化

一、概念 相较于领域适应&#xff0c;领域泛化(Domain generalization)最显著的区别在于训练过程中不能访问测试集。 领域泛化的损失函数一般可以描述为以下形式&#xff1a; 该式分为三项&#xff1a;第一项表示各训练集权重的线性组合&#xff0c;其中π为使该项最小的系数&a…

ChatGPT已打破图灵测试,新的测试方法在路上

生信麻瓜的 ChatGPT 4.0 初体验 偷个懒&#xff0c;用ChatGPT 帮我写段生物信息代码 代码看不懂&#xff1f;ChatGPT 帮你解释&#xff0c;详细到爆&#xff01; 如果 ChatGPT 给出的的代码不太完善&#xff0c;如何请他一步步改好&#xff1f; 全球最佳的人工智能系统可以通过…

TCP的三次握手四次挥手

TCP的三次握手和四次挥手实质就是TCP通信的连接和断开。 三次握手&#xff1a;为了对每次发送的数据量进行跟踪与协商&#xff0c;确保数据段的发送和接收同步&#xff0c;根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系&#xff0c;并建立虚连接。 四次挥手&a…

如何把pdf转成cad版本?这种转换方法非常简单

将PDF转换成CAD格式的优势在于&#xff0c;CAD格式通常是用于工程设计和绘图的标准格式。这种格式的文件可以在计算机上进行编辑和修改&#xff0c;而不需要纸质副本。此外&#xff0c;CAD文件通常可以与其他CAD软件进行交互&#xff0c;从而使得工程设计和绘图过程更加高效和精…

Camunda BPM Run下载(7.20)

官网地址: https://camunda.com/ 中文站点:http://camunda-cn.shaochenfeng.com https://downloads.camunda.cloud/release/camunda-bpm/run/7.20/https://downloads.camunda.cloud/release/camunda-bpm/run/7.20/camunda-bpm-run-7.20.0-alpha3.ziphttps://downloads.camunda…

Stephen Wolfram:嵌入的概念

The Concept of Embeddings 嵌入的概念 Neural nets—at least as they’re currently set up—are fundamentally based on numbers. So if we’re going to to use them to work on something like text we’ll need a way to represent our text with numbers. And certain…

华秋亮相2023世界汽车制造技术暨智能装备博览会,推动汽车产业快速发展

洞悉全球汽车产业格局&#xff0c;前瞻业界未来趋势。2023年7月27日-30日&#xff0c;时隔三年&#xff0c;重聚武汉国际博览中心&#xff0c;2023世界汽车制造技术暨智能装备博览会盛大开幕。深耕汽车行业多年的世界汽车制造技术暨智能装备博览会&#xff0c;掀起行业热点新高…

Web3.0:重新定义互联网的未来

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Web3.0&#xff1a;重新定义互联网的未来 Web3.0是指下一代互联网&#xff0c;也称为“分布式互联网”。相比于Web1.0和Web2.0&#xff0c;Web3.0具有更强的去中心化、…

Python零基础入门(十一)——异常处理

系列文章目录 个人简介&#xff1a;机电专业在读研究生&#xff0c;CSDN内容合伙人&#xff0c;博主个人首页 Python入门专栏&#xff1a;《Python入门》欢迎阅读&#xff0c;一起进步&#xff01;&#x1f31f;&#x1f31f;&#x1f31f; 码字不易&#xff0c;如果觉得文章不…

LeetCode 626. 换座位

题目链接&#xff1a;LeetCode 626. 换座位 题目描述 表名&#xff1a;Seat 编写SQL查询来交换每两个连续的学生的座位号。如果学生的数量是奇数&#xff0c;则最后一个学生的id不交换。 按 id 升序 返回结果表。 查询结果格式如下所示。 示例1&#xff1a; 题目分析 如…

【编程】典型题目:寻找数组第K大数(四种方法对比)

【编程】典型题目&#xff1a;寻找数组第K大数&#xff08;四种方法对比&#xff09; 文章目录 【编程】典型题目&#xff1a;寻找数组第K大数&#xff08;四种方法对比&#xff09;1. 题目2. 题解2.1 方法一&#xff1a;全局排序&#xff08;粗暴&#xff09;2.2 方法二&#…

《golang设计模式》第一部分·创建型模式-04-抽象工厂模式(Abstract Factory)

文章目录 1. 概述1.1 角色1.2 类图 2. 代码示例2.1 设计2.2 代码2.3 类图 1. 概述 1.1 角色 AbstractFactory&#xff08;抽象工厂&#xff09;&#xff1a;它声明了一组用于创建产品的方法&#xff0c;每一个方法对应一种产品。ConcreteFactory&#xff08;具体工厂&#xf…