1. 首先是对单链表代码的实现
SList.h
c
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);
// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x);
// 删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos);
void SLTDestroy(SListNode** pphead);
SList.c
c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newdata = (SListNode*)malloc(sizeof(SListNode));
if (newdata==NULL)
{
perror("malloc fail");
exit(-1);
}
newdata->data = x;
newdata->next = NULL;
return newdata;
}
void SListPrint(SListNode* plist)
{
SListNode* tem = plist;
while (tem!=NULL)
{
printf("%d-> ",tem->data);
tem = tem->next;
}
printf("NULL");
printf("\n");
}
void SListPushBack(SListNode** pplist, SLTDateType x)
{
SListNode* newdata = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newdata;
}
else
{
SListNode* tail = *pplist;
while (tail->next !=NULL)
{
tail = tail->next;
}
tail->next = newdata;
}
}
void SLTDestroy(SListNode** pphead)
{
assert(*pphead);
SListNode* cur=*pphead;
while (cur!=NULL)
{
SListNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
//头插 对于头插没有必要判断*pplist是否为空
void SListPushFront(SListNode** pplist, SLTDateType x)
{
SListNode* newdata = BuySListNode(x);
newdata->next = *pplist;
*pplist = newdata;
}
//单链表尾删
void SListPopBack(SListNode** pplist)
{
assert(*pplist);
assert(pplist);
SListNode* cur = *pplist;
if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
while (cur->next->next)
{
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
//单链表头删
void SListPopFront(SListNode** pplist)
{
assert(*pplist);
if (*pplist == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* cur = *pplist;
SListNode* tail = (*pplist)->next;
*pplist = tail;
free(cur);
cur = NULL;
}
}
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* tail = plist;
while (tail)
{
if (tail->data == x)
{
return tail;
}
else
{
tail = tail->next;
}
}
return NULL;
}
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* newdata = BuySListNode(x);
if (pos->next == NULL)
{
pos->next = newdata;
}
else
{
newdata->next = pos->next;
pos->next = newdata;
}
}
void SListEraseAfter(SListNode* pos)
{
assert(pos);
assert(pos->next);
SListNode* tem = pos->next;
pos->next = pos->next->next;
free(tem);
tem = NULL;
}
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{
assert(pphead);
assert(pos);
assert(*pphead);
assert((*pphead)->next);
SListNode* newdata = BuySListNode(x);
if (*pphead == pos)
{
SListPushFront(pphead,x);
}
SListNode* tem = *pphead;
while (tem->next != pos)
{
tem = tem->next;
}
tem->next = newdata;
newdata->next = pos;
}
void SLTErase(SListNode** pphead, SListNode* pos)
{
assert(pos);
assert(pphead);
assert(*pphead);
if (*pphead == pos)
{
SListPopFront(pphead);
}
SListNode* tem = *pphead;
while (tem->next != pos)
{
tem = tem->next;
}
tem->next = pos->next;
free(pos);
pos = NULL;
}
2.链表的概念和结构
概念:链表是一种物理存储结构上非连续,非顺序的储存结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
下面通过一个图来稍微介绍一下链表 :这里以没有哨兵位的一个链表为例
(哨兵位:第一个节点只储存下一个节点的地址,其他值为空,比没有哨兵的链表多一个节点)
这里每一个框框代表一个节点,每一个节点相互之间是无关的,是随机的,位置实际是东一个,西一个的。每一个节点包含了两个主要组成,一个是下一个节点的地址,二是每一个节点包含一个自己定义的值,像图中的数就是每一个链表代表的值。一般将第一个节点叫做phead。最后一个链表指向的地址为空。
可以从下面图中更加好的看出链表的结构 :上面的红色字符代表这个节点的地址,节点中的蓝色字符的位置实质是一个指针,储存着下一个节点的地址。而黑色字体的数字则代表这个节点存的数据。
注意:
1.现实中的节点一般是从堆上申请过来的。
2.从堆上申请的空间,是按照一定策略来申请的两次申请的空间可能连续也 可能不连续。
3.链表的分类
实际上链表的类型也有很多,有8种。由下面几种情况组合而来。
4.单链表的主要实现过程
1.创建单链表的单个节点的结构体
c
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
这是一个节点的结构体,data为存储数据的地方,next则是存的下一个地址的指针。
2.单链表的功能
c
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);
// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x);
// 删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos);
void SLTDestroy(SListNode** pphead);
要注意这里许多地方都用了二级指针,像数据的插入和删除这样的操作就必须要二级指针,因为如果不传二级指针那么传来的指针就不会被改变(形参的改变不会影响实参)。
3,功能的实现
创造一个新节点
c
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newdata = (SListNode*)malloc(sizeof(SListNode));
if (newdata==NULL)//判断节点是否创造成功
{
perror("malloc fail");
exit(-1);
}
newdata->data = x;
newdata->next = NULL;
return newdata;
}
打印单链表
c
void SListPrint(SListNode* plist)
{
SListNode* tem = plist;
while (tem!=NULL)//到最后一个节点即next为NULL的节点停止。
{
printf("%d-> ",tem->data);
tem = tem->next;
}
printf("NULL");
printf("\n");
}
尾插
c
void SListPushBack(SListNode** pplist, SLTDateType x)
{
SListNode* newdata = BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newdata;
}
else
{
SListNode* tail = *pplist;
while (tail->next !=NULL)
{
tail = tail->next;
}
tail->next = newdata;
}
}
对于链表的插入与删除数据最主要的操作就是改变链表的next的指向。
而尾插就是找到最后一个节点,再将最后一个节点的next指向新创建的节点。
链表的释放
c
void SLTDestroy(SListNode** pphead)
{
assert(*pphead); //检查链表是否存在
SListNode* cur=*pphead;
while (cur!=NULL) //利用循环将每一个节点都删除
{
SListNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
头插
c
void SListPushFront(SListNode** pplist, SLTDateType x)
{
SListNode* newdata = BuySListNode(x);
newdata->next = *pplist;
*pplist = newdata;
}
头插没必要判断*pplist是否为空
尾删
c
void SListPopBack(SListNode** pplist)
{
assert(*pplist);
assert(pplist);
SListNode* cur = *pplist;
if ((*pplist)->next == NULL) //只有一个节点的情况
{
free(*pplist);
*pplist = NULL;
}
while (cur->next->next) //当有多个节点得到时候,先找到最后一个节点,然后释放
{
cur = cur->next;
}
free(cur->next);
cur->next = NULL;
}
头删
c
void SListPopFront(SListNode** pplist)
{
assert(*pplist);
if (*pplist == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* cur = *pplist;
SListNode* tail = (*pplist)->next;
*pplist = tail;
free(cur);
cur = NULL;
}
}
单链表的查找
c
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
SListNode* tail = plist;
while (tail)
{
if (tail->data == x)
{
return tail; //找到所需查找的值
}
else
{
tail = tail->next;
}
}
return NULL; //没有找到,返回空
}
在pos位置前插入
c
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
assert(pos);
SListNode* newdata = BuySListNode(x);
if (pos->next == NULL) //pos位置就是最后一个位置,相当于尾插
{
pos->next = newdata;
}
else
{
newdata->next = pos->next;
pos->next = newdata;
}
}
删除pos位置后的节点
c
void SListEraseAfter(SListNode* pos)
{
assert(pos);
assert(pos->next);
SListNode* tem = pos->next;
pos->next = pos->next->next;
free(tem);
tem = NULL;
}
在pos前插入
c
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x)
{
assert(pphead);
assert(pos);
assert(*pphead);
assert((*pphead)->next);
SListNode* newdata = BuySListNode(x);
if (*pphead == pos) //当只有一个节点时,相当于头插
{
SListPushFront(pphead,x);
}
SListNode* tem = *pphead;
while (tem->next != pos)
{
tem = tem->next;
}
tem->next = newdata;
newdata->next = pos;
}
删除pos位置节点
c
void SLTErase(SListNode** pphead, SListNode* pos)
{
assert(pos);
assert(pphead);
assert(*pphead);
if (*pphead == pos)
{
SListPopFront(pphead);
}
SListNode* tem = *pphead;
while (tem->next != pos)
{
tem = tem->next;
}
tem->next = pos->next;
free(pos);
pos = NULL;
}