目录
1.链式存储结构与顺序存储结构的比较
2.单链表、双链表与循环链表
3.单链表的实现
顺序存储的特点:已物理相邻表示逻辑关系,任一元素均可随机存取。
链式存储的特点:用一组物理位置任意的存储单元来存放线性表的数据元素,这组存储单元可以是连续的也可以是不连续的,甚至可以分布在内存中的任意位置上,链表中元素的逻辑次序和物理次序不一定相同。
两种存储方式可以用下图来辅助理解。
顺序存取:查找第i个位置的数据,需要通过从第一个到第i个,顺序查找,所以存取的时候都是需要按顺序进行,所以叫顺序存取。
随机存取:随便一个i位置的数据,可以通过查找数组第 (i-1)个来查找,存取的时候同样可以存第i个位置或者取第i个位置的元素,所以叫随机存取。
可以用以下图来辅助理解:
单链表,指结点只有一个指针域的链表,也可以称为线性链表。每个单链表都有一个头指针,也就是链表的首地址,其结点分为两个部分,一个是指针域一个是数据域(如下图所示),其中指针域存下一个结点的地址,而数据域为该结点存放的元素。
用单链表的方式来表示线性表,有两种方式,带头结点跟不带头结点。不带头结点,顾名思义就是链表的头指针指向的下一个即为首元素结点;而带头结点的链表则头指针指向头结点,而头结点再指向首元素结点。如下图所示:
带头结点的好处:
便于首元素结点的处理:首元素结点的地址保存在头结点的指针域中,所以在链表中的第一个位置上的操作和其他位置的一致,无需进行特殊处理。
便于空表和非空表的统一处理:无论链表是否为空,头指针都只想头结点的非空指针,因此空表和非空表的处理也就一致了。
单链表定义:用结构体的方式定义,定义一个数据域跟一个指针域。这里实现的是不带头结点的单链表。
这段代码的ElenType为data的类型(相当于结构体的自定义类型);
typedef int ElemType; //定义数据类型
typedef struct LNode //声明结点的类型和指向结点的指针类型
{
ElemType data; //结点的数据域
struct LNode* next; //结点的指针域
}LNode,*LinkList; //LinkList为指向结构体Lnode的指针
注:定义的结构体LNode的类型名称为LNode其指针的为LinkList。即 *LNode = LinkList ,因为后面的函数多数都是对链表的指针域进行操作,所以定义指针类型LinkList方便后面的操作。
定义函数的操作:
#define TRUE 1
#define FALSE 0
#define VETERY 1
#define DEFEAT 0
#define NOFINE 0
typedef int Status;
Status InitLinkList(LinkList L); //初始化单链表
LinkList List_HeadInsert(LinkList L); //头插入法
LinkList List_TailInsert(LinkList L); //尾插入法
LNode* GetElem(LinkList L, int i); //按序号查找结点值
LNode* LocateElem(LinkList L, ElemType e); //按照结点值查找结点
void Delete_Node(LinkList L, int i); //按序号删除结点
void Print_Node(LinkList L); //打印单链表
int Linklength(LinkList L); //计算结点个数
int ListEmpty(LinkList L); //判断单链表是否为空
Status ClearList(LinkList L); //清空单链表
Status DestroyList(LinkList L); //销毁单链表
单链表的基本操作:对单链表进行初始化、建立、判断空值、取值、查找、删除、打印、求表长、清空、销毁十一个操作进行编写代码。
代码块粘贴在最后
初始化,即开辟一个空间,创建单链表的头指针;
Status InitLinkList(LinkList L) //初始化单链表
{
L = (LinkList)malloc(sizeof(LNode)); //开辟一个指针
L->next = NULL; //单链表没有下一个指针
return VETERY;
}
注:(强制转换的类型)malloc(sizeof(类型空间)),malloc是C语言进行开辟空间的函数,如果还不理解的话,可以去看看官方文档的解释。
判断单链表是否为空,只要头指针指向的下一个地址不为空,则还有元素结点在。
int ListEmpty(LinkList L) //判断单链表是否为空
{
if (L->next)
{
printf("链表不为空!\n");
return FALSE;
}
else
{
printf("链表为空!\n");
return TRUE;
}
}
清空单链表
清空单链表采用两个指针进行操作,通过定义p指针为L->next,则为首元素结点,然后用另外一个q指针指向p->next则为第二个元素结点,然后释放p指针free(p),在将q指针赋予给p指针,然后通过不断释放p指针从而实则释放全部的元素结点。下图辅助理解:
Status ClearList(LinkList L) //清空单链表
{
LinkList p, q;
p = L->next;
while (p)
{
q = p->next;
free(p);
p = q;
}
L->next = NULL;
return VETERY;
}
销毁单链表
销毁单链表将整个单链表的空间都释放掉,因此是从头指针开始往后释放每一个结点。
Status DestroyList(LinkList L) //销毁单链表
{
LinkList p;
while (L)
{
p = L;
L = L->next;
free(p);
}
return VETERY;
}
求单链表长度
int Linklength(LinkList L) //求单链表长度
{
int k = 0;
while (L->next != NULL)
{
k++;
L = L->next;
}
return k;
}
打印单链表
void Print_Node(LinkList L) //打印单链表
{
while (L->next != NULL)
{
L = L->next;
printf("%d ", L->data);
}
printf("\n");
}
头插法建立单链表
通过从第一个元素结点进行插入结点,可以由下图辅助理解:
LinkList List_HeadInsert(LinkList L)//头插入法
{
LNode *new;
int i = 10;
for (i = 0; i < 10; i++)
{
new = (LNode*)malloc(sizeof(LNode)); //开辟一个新指针空间
new->data = i; //给指针的数据域赋值
new->next = L->next; //将新结点的下一个指针指向首元素结点,则代替首元素结点
L->next = new; //头指针指向新结点,即新节点为首元素结点
}
return L;
}
尾插法建立单链表
顾名思义,就是从尾部插入元素,通过下图进行辅助理解:
LinkList List_TailInsert(LinkList L) //尾插入法
{
LNode* new;
LNode* tail = L;
for (int i = 0; i < 10; i++)
{
new = (LinkList)malloc(sizeof(LNode));
new->data = i;
tail->next = new; //将最后一个结点下一个指向new指针
tail = new; //最后一个指针为new指针
}
tail->next = NULL; //最后一个指针指向NULL
return L;
}
按序号查找结点值
通过序号 i 来查找相对应的结点值,通过第一个到第 i 个结点,从而返回结点地址,通过结点地址可以获取到对应的结点值。
LinkList GetElem(LinkList L, int i) //按序号查找结点值
{
int j = 1;
LNode* p = L->next; //定义辅助指针p
if (i == 0) //如果为0,则返回头指针地址
return L;
if (i < 1) //如果小于1则返回NULL空指针
return NULL;
while (p && j < i) //在j>=i和p不为NULL之前,循环继续
{
p = p->next; //p指下一个指针
j++; //j+1
} //查找得到的话为j=i时返回,查找不到,则一直到p为NULL
return p;
}
按值查找表结点
通过第一个结点的data开始与目标值e进行对比,直到查找到,则返回结点值。若果没有则返回NULL。
LinkList LocateElem(LinkList L, ElemType e) //按照结点值查找结点
{
LNode* p = L->next;
while (p->data != e && p != NULL)
{
p = p->next;
}
return p;
}
按序号删除结点
通过GetElem函数查找结点的前驱指针,然后将前驱指针 p 指向待删除结点 q 的后继指针,从而释放待删除结点free(q)。通过下图进行辅助理解:
void Delete_Node(LinkList L, int i) //按序号删除结点
{
LinkList p = GetElem(L, i - 1); //查找前驱结点
LinkList q;
q = p->next; //令q等于待删除结点
p->next = q->next; //将p的下一个指向q的下一个
free(q); //删除q
}
test.c
#include "LinkList.h"
int main()
{
LNode L; //定义链表L
InitLinkList(&L); //初始化链表
//List_HeadInsert(&L); //头插法创建链表
//Print_Node(&L); //打印链表
List_TailInsert(&L); //尾插法创建链表
Print_Node(&L); //打印链表
LNode* p = GetElem(&L, 3); //查找i = 3 位置的指针
printf("查找成功3的数据为%d \n", p->data); //打印3位置的元素数据
LNode* q = LocateElem(&L, 5); //查找值为5的元素的指针
printf("查找成功5的位置为%p \n", q); //打印查找的指针值
int l = LinkLength(&L); //链表长度
printf("%d\n", l); //打印链表长度
Delete_Node(&L, 4); //删除i = 4的结点
printf("删除成功!\n");
Print_Node(&L); //打印链表
int i = LinkLength(&L);
printf("%d\n", i); //查看链表长度
ClearList(&L); //清空链表
if(!ListEmpty(&L)) //判断链表是否为空
Print_Node(&L); //不为空则打印链表
return 0;
}
LinkList.,h
#pragma once
#include
#include
#define TRUE 1
#define FALSE 0
#define VETERY 1
#define DEFEAT 0
#define NOFINE 0
typedef int ElemType; //定义数据类型
typedef int Status;
typedef struct LNode
{
ElemType data; //结点的数据域
struct LNode* next; //结点的指针域
}LNode,*LinkList; //LinkList为指向结构体Lnode的指针
Status InitLinkList(LinkList L); //初始化单链表
LinkList List_HeadInsert(LinkList L); //头插入法
LinkList List_TailInsert(LinkList L); //尾插入法
LinkList GetElem(LinkList L, int i); //按序号查找结点值
LinkList LocateElem(LinkList L, ElemType e); //按照结点值查找结点
void Delete_Node(LinkList L, int i); //按序号删除结点
void Print_Node(LinkList L); //打印单链表
int LinkLength(LinkList L); //计算结点个数
int ListEmpty(LinkList L); //判断单链表是否为空
Status ClearList(LinkList L); //清空单链表
Status DestroyList(LinkList L); //销毁单链表
LinkList.c
#include "LinkList.h"
Status InitLinkList(LinkList L) //初始化单链表
{
L = (LinkList)malloc(sizeof(LNode));
L->next = NULL;
return VETERY;
}
LinkList List_HeadInsert(LinkList L)//头插入法
{
LNode *new;
int i = 10;
for (i = 0; i < 10; i++)
{
new = (LNode*)malloc(sizeof(LNode));
new->data = i;
new->next = L->next;
L->next = new;
}
return L;
}
LinkList List_TailInsert(LinkList L) //尾插入法
{
LNode* new;
LNode* tail = L;
for (int i = 0; i < 10; i++)
{
new = (LinkList)malloc(sizeof(LNode));
new->data = i;
tail->next = new;
tail = new;
}
tail->next = NULL;
return L;
}
LinkList GetElem(LinkList L, int i) //按序号查找结点值
{
int j = 1;
LNode* p = L->next;
if (i == 0)
return L;
if (i < 1)
return NULL;
while (p && j < i)
{
p = p->next;
j++;
}
return p;
}
LinkList LocateElem(LinkList L, ElemType e) //按照结点值查找结点
{
LNode* p = L->next;
while (p->data != e && p != NULL)
{
p = p->next;
}
return p;
}
void Delete_Node(LinkList L, int i) //按序号删除结点
{
LNode* p = GetElem(L, i - 1);
LNode* q;
q = p->next;
p->next = q->next;
free(q);
}
void Print_Node(LinkList L) //打印单链表
{
while (L->next != NULL)
{
L = L->next;
printf("%d ", L->data);
}
printf("\n");
}
int LinkLength(LinkList L) //求单链表长度
{
int k = 0;
while (L->next != NULL)
{
k++;
L = L->next;
}
return k;
}
int ListEmpty(LinkList L) //判断单链表是否为空
{
if (L->next)
{
printf("链表不为空!\n");
return FALSE;
}
else
{
printf("链表为空!\n");
return TRUE;
}
}
Status ClearList(LinkList L) //清空单链表
{
LinkList p, q;
p = L->next;
while (p)
{
q = p->next;
free(p);
p = q;
}
L->next = NULL;
return VETERY;
}
Status DestroyList(LinkList L) //销毁单链表
{
LinkList p;
while (L)
{
p = L;
L = L->next;
free(p);
}
return VETERY;
}
运行截图: