链表是线性表的链式存储方式。链表的内存是不连续的,前一个元素存储地址的下一个地址中存储的不一定是下一个元素。小编今天就将带大家深入了解一下链表,快来学习吧
引言
在存储一大波数的时候,我们通常使用的是数组,但是数组有时候又会显得不够灵活,比如下面这个例子:
有一串已经排序好的数 2,3,5,8,9 ,10
如果我们想要往数组中插入6 这个元素,需要把 8 以后的元素全部往后挪一位
这样操作显然很耗费时间,如果使用链表的话则会快很多。那么什么是链表呢?请看下图:
此时如果需要在8前面加入一个6,那么只需要向下图一样更改一下就可以了,而不用向像最开始那样把每个数向后挪。
链表的相关思考
为了实现链表这样的数据结构,我们需要使用指针和malloc这样的函数。
注意 : malloc 函数的返回值是 void * 类型,我们需要对其进行强制类型转换
使用malloc时需要调用头文件 <stdlib.h>
为什么我们要用这么复杂的办法来储存类型呢?
因为按照之前的方法,我们必须预先准确地知道所需变量的个数,也就是说我们必须我们必须定义出所有的变量。假如说你现在定义了100个变量,而实际上则需要101个变量,那么就不得不对这个程序进行修改。
而有了malloc函数,我们可以在程序运行的过程中根据实际情况来申请空间。
链表结点结构
每一个结点都由两个部分组成。左边的部分用来存放具体的值,那么用一个整型变量就可以;右边的部分则需要储存下一个点的地址,则可以用指针来实现(也称为后继指针)。
这里我们定义一个结构体类型来存储这个结点:
struct node
{
int date;
struct node* next;
};
因为下一个结点的类型也是 struct node ,所以我们指针的类型也必须是 struct node * 类型。
建立链表
首先,我们需要一个头指针 head 指向链表的最开始。当链表还没有建立的时候头指针head为空(也可以理解指向空结点)。
struct node* head;
head = NULL; //头指针初始为空
现在,我们来创立第一个结点,并用临时指针p指向这个结点
struct node* p;
//动态申请一块空间,用来存放一个结点,并用临时指针p指向这个结点
p = (struct node*)malloc(sizeof(struct node));
接下来分别设置新建的结点的左半部分和右半部分
scanf("%d", &a);
p->date = a; //将数据存储到当前结点的date域中
p->next = NULL; //设置当前结点的后继指针为空,也就是当前结点的下一个结点为空
下面来设置头指针并设置新创结点的 *next 指向空 。头指针的作用是方便以后从头遍历整个链表
if (head == NULL)
head = p; //如果这是第一个创建的结点,则将头指针指向这个结点
else
q->next = p; //如果不是第一个创建的结点,则将上一个结点的后继指针指向当前结点
如果是第一个创立的结点,则将头指针指向这个结点
如果不是第一个创建的结点,则将上一个结点的后继节点指向当前结点。
最后要将指针q也指向当前结点,因为待会临时指针p将会指向新创建的结点。
q = p; //指针q也要指向当前结点
#include <stdio.h>
#include <stdlib.h>
//这里创建一个结构体用来表示链表的结点类型
struct node
{
int date;
struct node* next;
};
int main()
{
struct node* head, * p, * q = NULL, * t;
int i, n, a;
scanf("%d", &n);
head = NULL; //头指针初始化为空
for (i = 1; i <= n; i++)
{
scanf("%d", &a);
//动态申请一块空间,用来存放一个结点,并用临时指针p指向这个结点
p = (struct node*)malloc(sizeof(struct node));
p->date = a;
p->next = NULL; //设置当前结点的后继指针为空,也就是下一个结点为空
if (head == NULL)
head = p; //如果这是第一个创建的结点,则将头指针指向这个点
else
q->next = p;//如果不是第一个创建的结点,则将上一个结点的后继节点指向当前结点
q = p; //指针q也指向当前结点
}
//输出链表中的所有数
t = head;
while (t != NULL)
{
printf("%d ", t->date);
t = t->next; //继续下一个结点
}
}
效果图
实现插入操作
首先用一个临时指针t从链表的头部开始遍历
t = head; //从链表的头部开始遍历
等到指针t的下一个结点的值比6大的时候,将6插到中间。
即 t -> next -> date 大于 6 的时候进行插入
代码实现
scanf("%d", &a); //读入待插入的数
t = head; //从链表的头部开始遍历
while (t != NULL)
{
if (t->next->date > a || t->next->next == NULL)
{
//如果当前结点是最后一个结点或者下一个结点的值大于待插入的值的时候插入
p = (struct node*)malloc(sizeof(struct node)); //申请一块空间,来存放新增结点
p->date = a;
p->next = t->next;//新增结点的后继指针指向当前结点的后继指针所指向的结点
t->next = p; //当前结点的后继指针指向新增结点
break; //插入完毕退出循环
}
t = t->next; //继续下一个结点
}
完整代码
效果图:
#include <stdio.h>
#include <stdlib.h>
//这里创建一个结构体用来表示链表的结点类型
struct node
{
int date;
struct node* next;
};
int main()
{
struct node* head, * p, * q = NULL, * t;
int i, n, a;
scanf("%d", &n);
head = NULL; //头指针初始化为空
for (i = 1; i <= n; i++)
{
scanf("%d", &a);
//动态申请一块空间,用来存放一个结点,并用临时指针p指向这个结点
p = (struct node*)malloc(sizeof(struct node));
p->date = a;
p->next = NULL; //设置当前结点的后继指针为空,也就是下一个结点为空
if (head == NULL)
head = p; //如果这是第一个创建的结点,则将头指针指向这个点
else
q->next = p;//如果不是第一个创建的结点,则将上一个结点的后继节点指向当前结点
q = p; //指针q也指向当前结点
}
scanf("%d", &a); //读入待插入的数
t = head; //从链表的头部开始遍历
while (t != NULL)
{
if (t->next->date > a || t->next->next == NULL)
{
//如果当前结点是最后一个结点或者下一个结点的值大于待插入的值的时候插入
p = (struct node*)malloc(sizeof(struct node)); //申请一块空间,来存放新增结点
p->date = a;
p->next = t->next;//新增结点的后继指针指向当前结点的后继指针所指向的结点
t->next = p; //当前结点的后继指针指向新增结点
break; //插入完毕退出循环
}
t = t->next; //继续下一个结点
}
//输出链表中的所有数
t = head;
while (t != NULL)
{
printf("%d ", t->date);
t = t->next; //继续下一个结点
}
}
以上就是C语言数据结构与算法之链表(一)的详细内容,更多关于C语言数据结构 链表的资料请关注编程学习网其它相关文章!
本文标题为:C语言数据结构与算法之链表(一)
基础教程推荐
- C++详细实现完整图书管理功能 2023-04-04
- C语言基础全局变量与局部变量教程详解 2022-12-31
- C利用语言实现数据结构之队列 2022-11-22
- 一文带你了解C++中的字符替换方法 2023-07-20
- C++中的atoi 函数简介 2023-01-05
- C语言 structural body结构体详解用法 2022-12-06
- 如何C++使用模板特化功能 2023-03-05
- C++使用easyX库实现三星环绕效果流程详解 2023-06-26
- C/C++编程中const的使用详解 2023-03-26
- 详解c# Emit技术 2023-03-25