在上两篇文章中,我们介绍了基于静态数组和动态数组的顺序表,顺序表频繁查询而插入删除动作少的情况下,顺序表也适用于进行尾插的时候,因为相对于链表而言,顺序表在进行尾插时,顺序表不需要通过遍历来找到最后一个插入点,比较而言,顺序表尾插效率高。
但是,在进行头插和中插时,顺序表需要将插入元素位置之后的元素整体后移才能插入数据,这样做在有大量插入删除场景下即为麻烦且效率低,因此,提出了链表的思想。而链表在头插或者中插时,只需要创建一个新节点,然后将节点链入所插入位置即可。
链表概述
链表是一种常见的数据结构。数组可以存放数据,但是数组存放数据时必须提前指定数组包含元素个数,即数组长度。但是数组保存数据的缺点在于如果要保存元素大于数组大小时,则不能将所有内容保存入数组,而当要保存元素远小于数组大小时,又造成了空间的浪费。而链表其存储元素个数不受限定,当进行添加元素时,存储个数随之增加。
链表是一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称存储单元为一个节点。
typedef int DataType;
typedef int DataType;
typedef struct LinkNode
{
DataType _data;//当前节点保存数据
struct LinkNode* _pNext;//指向链表中下一结点的指针
}Node;
如下图,为链表结构示意图:
在链表中有一个头指针变量,图中head表示的就是头指针,这个指针变量保存一个地址。从图中的箭头可以看到该地址为一个变量的地址,也就是说头指针指向一个变量。这个变量称为元素,在链表中每一个元素包括两个部分:数据部分和指针部分。数据部分用来存放元素所包含的数据,而指针部分用来指向下一个元素。最后一个元素指针指向NULL,表示指向的地址为空。
从图可以看到,head头结点指向第一个元素,第一个元素中的指针又指向第二个元素,第二个元素的指针又指向第三个元素的地址,第三个元素的指针指向第四个元素,第四个元素的指针就指向为空。
链表的分类
- 单链表
- 双向链表
- 双向循环链表
需要注意的是,上述三种链表都有两种形式,分别为带头结点和不带头结点。
C语言实现不带头节点的单链表
具体代码实现如下:
#pragma once
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
/***************************************/
//链表的定义
typedef int DataType;
typedef struct LinkNode
{
DataType _data;//当前节点保存数据
struct LinkNode* _pNext;//指向链表中下一结点的指针
}Node;
/***************************************/
//链表的初始化
void LinkListInit(Node** pHead);
//创建新结点
Node* LinkListCreatNode(DataType data);
//销毁结点
void LinkListIDestroyNode(Node** pHead);
//遍历打印链表
void LinkListPrint(Node** pHead);
//尾插元素
void LinkListPushBack(Node** pHead, DataType data);
//尾删元素
void LinkListPopBack(Node** pHead);
//头插元素
void LinkListPushFront(Node** pHead, DataType data);
//头删元素
v