一、单链表
1、单链表的存储结构
在操作单链表时加入头结点方便增删查改,本文章使用二级指针操作头结点实现相关功能。
存储结构如下
typedef int NodeDataType;
typedef struct Node
{
NodeDataType _data; //数据
struct Node* next; //下一个节点
}Node;
初始化时在定义一个头结点即可
Node* plist = NULL;
后面使用二级指针去操作
2、头文件声明需要实现的函数接口
//销毁以及打印
void NodeDestory(Node** pphead);
void Print(Node **pphead);
//相关增删查改
void NodePushBack(Node **pphead,NodeDataType x);
void NodePopBack(Node **pphead);
void NodePushFront(Node **pphead,NodeDataType x);
void NodePopFront(Node **pphead);
//一些指定功能
Node* NodeFind(Node* phead,NodeDataType x);
void NodeInsertAfter(Node** pphead,int pos,NodeDataType x);
void NodeEraseAfter(Node* phead,int pos);
void NodeDelete(Node* phead,NodeDataType x);
3、Node.c文件逐一实现相关功能
①准备工作
增加新节点时需要开辟空间,定义一个开辟新节点的函数为BuynewNode,代码如下
Node* buyNode(NodeDataType x)
{
Node* newNode = (Node*) malloc(sizeof(Node));
if(newNode == NULL)
{
printf("申请失败\n");
exit(-1);
}
newNode->_data = x;
newNode->next = NULL;
return newNode;
}
在对每个函数测试时需要进行打印操作方便查看
void Print(Node **pphead)
{
Node* cur = *pphead;
while(cur)
{
printf("%d->",cur->_data);
cur = cur->next;
}
printf("NULL\n");
}
②尾插尾删
//尾插
void NodePushBack(Node **pphead,NodeDataType x)
{
Node* newNode = buyNode(x);
//判断链表是否为空,为空则直接改变*phead
if(*pphead == NULL )
{
*pphead = newNode;
}
else
{
//非空则需要遍历至链表尾端
Node* tail = *pphead;
while(tail->next != NULL)
{
tail = tail->next;
}
tail->next = newNode;
}
}
//尾删
void NodePopBack(Node **pphead)
{
//尾删较为复杂,需要判断空,只有一个节点,两个及以上节点三个情况,每个情况操作不同
//空则直接返回
if(*pphead == NULL)
{
return;
}
//一个节点直接释放*pphead
else if((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
//两个及以上需要遍历至末尾
Node* prev = NULL;
Node* tail = *pphead;
while(tail->next != NULL)
{
prev=tail;
tail=tail->next;
}
free(tail);
prev->next = NULL;
}
}
③头插 头删
//头插直接插在*pphead前面
void NodePushFront(Node **pphead,NodeDataType x)
{
Node *newNode = buyNode(x);
newNode->next = *pphead;
*pphead = newNode;
}
//头删
//需要判断为空以及其他情况
void NodePopFront(Node **pphead)
{
if(*pphead == NULL)
{
return;
}
else
{
Node* next = (*pphead)->next;
free(*pphead);
*pphead = next;
}
}
④其他功能
//查找指定数字,该函数可以用于其他函数的实现
Node* NodeFind(Node* phead,NodeDataType x)
{
Node* cur = phead;
while(cur)
{
if(cur->_data == x)
return cur;
cur = cur->next;
}
}
//指定位置后插入
//由于单链表向后指,所以一般实现后插
void NodeInsertAfter(Node** pphead,int pos,NodeDataType x)
{
Node* cur = *pphead;
while(--pos)
{
cur = cur->next;
}
Node* newNode = buyNode(x);
newNode->next = cur->next;
cur->next = newNode;
}
//删除指定位置后数据
void NodeEraseAfter(Node* phead,int pos)
{
Node* cur = phead;
while(--pos)
{
cur = cur->next;
}
Node* next = cur->next;
Node* nextnext = next->next;
cur->next = nextnext;
free(next);
next = NULL;
}
//删除指定数字,该函数考虑的细节比较多
void NodeDelete(Node* phead,NodeDataType x)
{
Node* prev = NULL;
Node* cur = phead;
while(cur->next != NULL)
{
if(cur->_data == x)
{
if(cur == phead)
{
phead = cur->next;
free(cur);
cur = phead;
return;
}
else
{
prev->next = cur->next;
free(cur);
return;
}
}
prev = cur;
cur = cur->next;
}
}
二、双向循环链表
1、双向链表的数据存储结构
双向循环链表顾名思义需要头尾相连,因此需要两个指针,一个指向前,一个指向后。
双向链表头结点不进行实际赋值操作
typedef int DNDataType;
typedef struct Double_Node
{
DNDataType _data;
struct Double_Node * next;
struct Double_Node* prev;
}DbNode;
2、头文件声明需要实现的函数
DbNode *DbNodeInit();
DbNode *BuyNode();
void DbNodePrint(DbNode* phead);
void DbNodePushBack(DbNode *phead,DNDataType x);
void DbNodePopBack(DbNode* phead);
void DbNodePushFront(DbNode *phead,DNDataType x);
void DbNodePopFront(DbNode* phead);
DbNode *DbNodeFind(DbNode *phead,DNDataType x);
void DbNodeInsertAfter(DbNode *phead,DNDataType _a,DNDataType x);
void DbNodeEraseAfter(DbNode *phead,DNDataType _a);
void Insert(DbNode* pos,DNDataType x);
void NewPushFront(DbNode* phead,DNDataType x);
void clear(DbNode* phead);
void Destroy(DbNode* phead);
3、Double_Node.c文件逐一实现
①初始化以及销毁
//初始化
DbNode *DbNodeInit()
{
DbNode *phead = BuyNode();
phead->next = phead;
phead->prev = phead; //首尾相连
return phead;
}
//销毁
void Destroy(DbNode* phead)
{
DbNode *cur = phead->next;
while(cur != phead)
{
DbNode *next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;
}
②打印以及开辟新节点
//遍历打印
void DbNodePrint(DbNode* phead)
{
DbNode* cur = phead->next; //注意实际节点从头结点下一个开始
while(cur != phead) //终止条件
{
printf("%d ",cur->data);
cur = cur->next;
}
printf("\n");
}
//开辟新节点
DbNode *BuyNode(DNDataType x)
{
DbNode* newNode = (DbNode*) malloc(sizeof(DbNode));
if(newNode == NULL)
{
printf("申请失败\n");
exit(-1);
}
newNode->next = NULL;
newNode->prev = NULL;
newNode->data = x;
return newNode;
}
③尾插尾删
//尾插
void DbNodePushBack(DbNode *phead,DNDataType x)
{
DbNode *tail = phead->prev;
DbNode* newNode = BuyNode(x);
tail->next = newNode;
newNode->prev = tail;
newNode ->next = phead;
phead->prev = newNode;
}
//尾删
void DbNodePopBack(DbNode* phead)
{
DbNode* tail = phead->prev;
DbNode* tailPrev = tail->prev;
phead->prev = tailPrev;
tailPrev->next = phead;
free(tail);
tail = NULL;
}
④头插头删
//头插
void DbNodePushFront(DbNode *phead,DNDataType x)
{
DbNode* next = phead->next;
DbNode *newNode = BuyNode(x);
phead->next = newNode;
newNode->prev = phead;
newNode->next = next;
next->prev = newNode;
}
//头删
void DbNodePopFront(DbNode* phead)
{
DbNode *next = phead->next;
DbNode* nextnext = next->next;
phead->next = nextnext;
next->prev = phead;
free(next);
next = NULL;
}
⑤其他功能
//查找指定数字的节点位置
DbNode *DbNodeFind(DbNode *phead,DNDataType x)
{
DbNode *cur = phead->next;
while(cur != phead)
{
if(cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
//指定数字后插入新数字
void DbNodeInsertAfter(DbNode *phead,DNDataType _a,DNDataType x)
{
DbNode* pos = DbNodeFind(phead,_a);
DbNode *next = pos->next;
DbNode* newNode = BuyNode(x);
pos->next = newNode;
newNode->prev = pos;
newNode->next = next;
next->prev = newNode;
}
//删除指定数字后数据
void DbNodeEraseAfter(DbNode *phead,DNDataType _a)
{
DbNode *pos = DbNodeFind(phead,_a);
DbNode *next = pos->next;
DbNode *nextnext = next->next;
pos->next = nextnext;
nextnext->prev = pos;
free(next);
next = NULL;
}
//指定位置后插入
void Insert(DbNode* pos,DNDataType x)
{
DbNode* posPrev = pos->prev;
DbNode* newNode = BuyNode(x);
posPrev->next = newNode;
newNode->prev = posPrev;
newNode->next = pos;
pos->prev = newNode;
}
三、结语
双向链表相对于单链表更易于操作,功能实现也更为简单。
本文为个人学习记录,写的简单粗糙,如有错误,欢迎指正。