C语言数据结构单链表链表
数据结构–单链表
学习了顺序表,我们发现顺序表在向里面存放数据的时候很麻烦,比如我们要使用头插法存放一个数据到顺序表的时候,我们要将整个表都向后挪一位,这个操作就让人很难受。那么有没有一种结构可以让我们存放数据的操作变得简单一些呢?这就要用到线性结构的另一种–链表
链表的概念及结构
链表是一种物理存储结构上非连续、非顺序的的存储结构,数据元素的国际顺序是通过链表中的指针链接次序实现的。如下图:
注意:
- 从上图可以看出,链式结构在逻辑上是连续的,但是在物理上不一定连续
- 现实中的结点一般都是从堆上申请出来的
- 从对上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续
链表的分类
实际中链表的结构非常多,单链表、双向链表、带头单链表、带头双向链表、循环链表、不循环链表等等,虽然结构很多,但是我们实际最常用的还是无头单向非循环链表和带头双向循环链表
单向或者双向链表
带头链表
循环链表
链表的一些特点
**1.无头单向非循环链表:**结构简单,一般不会单独用来存数据。实际中更是多为其他数据结构的子结构。
**带头双向循环链表:**机构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。
链表的理解
链表的代码结是这样定义的:
struct Node{int data; // 用来存放数据struct Node* next; // 存放下一个节点的地址,用于找到下一个节点
}
我在学习链表的时候对于next的理解一直很困惑,后面突然顿悟,next就是指向下一个节点的地址。我们知道,指针是存放地址的,通过这个地址我们能访问到那个位置的数据。而这个next就是存放下一个节点地址的,通过这个地址,我们就能找到下一个节点。
单链表的实现
链表的操作和顺序表的操作是差不多的,无非是增删改查,这一系列操作。先看看我们要实现的功能
typedef int SLDataType;// 定义链表的结构
typedef struct SListNode {SLDataType data;struct SListNode* next;}SListNode;// 创建一个新节点
SListNode* BuyNewNode(SLDataType x);// 尾插
void SListPushBack(SListNode** pphead, SLDataType x);// 头插
void SListPushFront(SListNode** pphead, SLDataType x);// 尾删
void SListPopBack(SListNode** pphead);// 头删
void SListPopFront(SListNode** pphead);// 打印链表
void SListPrint(SListNode* pphead);// 单链表查找
SListNode* SListFind(SListNode* plist, SLDataType x);// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLDataType x);// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);// 单链表的销毁
void SListDestory(SListNode* plist);
首先我们得先把链表的结构给定义出来,就是用结构,定义一个链表结点的结构。如下图,我们要把一个结点划分为两个部分,即data和next部分。data部分用来存储数据,next用来存放下一个结点的位置,也就是用来将结点“链接起来”。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xasr70N8-1636910284535)(D:\blogs\结点.png)]
// 链表结点的定义
typedef struct SListNode {SLDataType data;struct SListNode* next; // 因为结点是SListNode类型,所以next得是SListNode*}SListNode;
在定义好结点的结构之后,就得创建结点,我们可以将它封装到一个函数中
SListNode* BuyNewNode(SLDataType x) {SListNode* tmp = (SListNode*)malloc(sizeof(SListNode)); // 为结点申请一块空间// 给成员变量赋值tmp->data = x;tmp->next = NULL;return tmp;
}
// 使用创建结点的函数
int main(){SListNode* node = BuyNewNode(2);return 0;
}
这样我们就的到了单链表的第一个结点,接着就是不断地在这个结点后面插入数据 ,就形成的单链表。下面我们看看几种插入数据的方法。
// 尾插法
void SListPushBack(SListNode** pphead, SLDataType x) {if (*pphead == NULL) {*pphead = BuyNewNode(x);}else {SListNode* tail = *pphead;while (tail->next) {tail = tail->next;};tail->next = BuyNewNode(x);}
}
尾插法,故名思意就是每次都将数据插入到链表的尾部。所以,我们定义了一个tail指针,用它来找到链表的尾部。找的方式就是通过循环来遍历链表tail = tail->next;
,这段代码就是让tail指针指向tail的下一个结点,实现遍历。
头插法
// 头插
void SListPushFront(SListNode** pphead, SLDataType x) {if (*pphead == NULL) {*pphead = BuyNewNode(x);}else {SListNode* tmp = BuyNewNode(x);tmp->next = *pphead;*pphead = tmp;}
}
头插法,正好和尾插相反,头插是将新结点插入到链表的最前面,然后返回新节点的地址
尾删
// 尾删
void SListPopBack(SListNode** pphead) {if (*pphead == NULL) {return;}if ((*pphead)->next == NULL) {free(*pphead);*pphead = NULL;}else {SListNode* tail = *pphead;while (tail->next->next) {tail = tail->next;}free(tail->next);tail->next = NULL;}
}
尾删和尾插一样,都需要一个指针来遍历链表,让tail指向最后一个结点的前一个位置,先把最后一个结点free掉,然后再将tail的next指向空。
// 头删
void SListPopFront(SListNode** pphead) {if (*pphead == NULL) {return;}SListNode* tmp = *pphead;*pphead = (*pphead)->next;free(tmp);tmp = NULL;
}
头删就是将最前面的一个结点删掉,所以我们需要先将头结点的地址保存起来,然后让头结点指向它的下一个结点,最后在把结点给free掉
// 单链表查找
SListNode* SListFind(SListNode* plist, SLDataType x) {if (plist == NULL) {return NULL;}SListNode* tmp = plist;while (tmp) {if (tmp->data == x) {break;}else {tmp = tmp->next;}}return tmp;
}
单链表的查找就很简单了,就是把单链表遍历一下,看看有没有哪个结点的data部分和x是相等的,如果有就返回这个结点的地址,否则返回空。
接下来就是单链表的随机插入和删除,这里的随机不是说,听天由命的让电脑选一个位置插入到链表中,而是,我们自己想把它插到哪就插到哪。
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLDataType x) {assert(pos);SListNode* newnode = BuyNewNode(x);newnode->next = pos->next;pos->next = newnode;
}
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos) {assert(pos);if (pos->next == NULL) {return;}SListNode* tmp = pos->next;pos->next = tmp->next;free(tmp);tmp = NULL;}
当我们不用单链表的时候一定要将它销毁,把申请的空间给释放掉
// 单链表的销毁
void SListDestory(SListNode* plist) {assert(plist);SListNode* prev = plist;SListNode* cur = plist->next;while (cur) {free(prev);prev = cur;cur = cur->next;}free(prev);prev = NULL;
}
下次分享双向带头环形链表,那是一个很棒的结构。
C语言数据结构单链表链表相关推荐
- C语言中用链表构建栈讲解,C语言数据结构之使用链表模拟栈的实例
C语言数据结构之使用链表模拟栈的实例 以下是"使用链表模拟栈"的简单示例: 1. 用C语言实现的版本 #include #include typedef char datatype ...
- C语言数据结构 单链表的建立、遍历、查找、插入和删除操作
参考文献 本博文为半摘记性质 -- 声明:全文主干部分摘自 [1] 杨智明. 数据结构(C语言版)[M]. 第一版. 北京:北京理工大学出版社, 2016. [2] 严蔚敏, 李冬梅, 吴伟民. 数据 ...
- C语言-数据结构-单链表的初始化,插入和输出
[问题描述] 设有头结点单链表,实现单链表的初始化.插入和输出算法. [输入形式] 第一行输入一个N(N大于等于1,小于1000),一个M(N大于等于1,小于1000): 第二行输入N个整数,以空格作 ...
- c语言数据结构单链表
#include<stdio.h> #include<stdlib.h> typedef int elemtype; typedef struct LNode { el ...
- c语言单链表功能,[数据结构]单链表(C语言)的各种功能
06-03阅读200,000 + 链表是一种常见的基本数据结构,在此充分利用了结构指针. 链表可以动态存储和分配,即链表是一个功能非常强大的数组. 他可以在节点中定义多种数据类型,并可以根据需要随意添 ...
- 数据结构-单链表基本操作(C语言实现)
参考书:王道考研数据结构 (此贴为博主学习408的笔记,因博主也是学习者,个人总结如有错误欢迎指正.如有侵权请告知,马上删除致歉) 单链表定义 单链表是线性表的链式存储,通过一组任意的存储单元来存 ...
- c语言将一个已知头结点的单链表逆序_C语言数据结构实现链表逆序并输出
C语言数据结构实现链表逆序并输出 将一个链表逆序并输出.我用了两种方法来实现,第一种是借助了一个新的空链表:第二种是在原来链表的基础上直接实现逆序. 实例代码: 头文件: #include #incl ...
- 用c语言实现单链表的初始化,建表,查找,求长度,插入,删除等操作,【YTU+2430+C语言习题+链表建立+插入+删除+输(5)...
的打印.判断链表是否为空.计算链表长度.插入节点.删除节点.删除整个链表.(2) 线性表adt顺序存储实现中的创建.查找.插入和删除等基本操作及相关算法,线性表adt链式存储实现中单链表.循环链表和双 ...
- php链表和联表的区别,PHP_浅谈PHP链表数据结构(单链表),链表:是一个有序的列表,但 - phpStudy...
浅谈PHP链表数据结构(单链表) 链表:是一个有序的列表,但是它在内存中是分散存储的,使用链表可以解决类似约瑟夫问题,排序问题,搜索问题,广义表 单向链表,双向链表,环形链表 PHP的底层是C,当一个 ...
最新文章
- Hadoop-2.2.0学习之一Hadoop-2.2.0变化简介
- Qt安装和QML HelloWord程序
- 推荐Datawhale整理的秋招求职攻略
- Centos下MySql用户管理
- 一张图解决Android Studio 项目运行按钮灰色
- PROTEUS元件库元件称呼 .
- php数字补零的两种方法
- raspberry pi_如何使用Raspberry Pi构建数字针Kong相机
- android搭建客户端,用Android搭建客户端 手机和服务器交互开发实例
- bzoj1041 [HAOI2008]圆上的整点 gcd
- React学习笔记—表单
- NP、OSPF 故障排除
- tcp 重发 应用层重传
- JavaWeb-Web请求过程
- 常用webserver 比较
- android什么叫服务器,Android系统中神秘的Bootloader究竟是什么
- linux系统FW升降级步骤,使用fwupd为Ubuntu 18.04系统更新固件的方法
- 【软件推荐】使用手机和平板作电脑副屏扩展
- 思科交换机配置试题_cisco交换机配置简单教程.doc
- php 中margin-top,margin-top是什么意思?