【C语言】双链表 双向链表 增删改查 实现 V1.0
前言:链表 双链表是常见的一种数据结构,而C语言是一门古老的语言,我个人觉得是一个比较好理解的形式,但真的要使用,想拿一个现成的代码来用一用。我却发现居然信息是零碎了,并没能很简单的获取一个我预想中的完整可用的工程。或许是我的信息检索能力有问题。我索性自己写一个,只说实现不聊概念,水平有限抛砖引玉,请大家多多指教。
工具:VS2013
正文:
1)函数介绍
//创建一个链表,返回一个指向链表的指针 (申请空间 并且清零 malloc memset)
creatList(void);
//创建一个节点,返回一个指向节点的指针 (申请空间 并且清零 malloc memset)
creatNode(void);
//向指定的链表尾部追加一个节点
addNodePend (pHead, pNode);
//根据索引idx项双向链表插入一个节点
insertNodeIdx(pHead, pNode, idx);
//在链表头部追加一个节点
addNodeInhead(pHead, pNode);
//根据idx获取指定的节点指针 获取后随你查看 修改 删除
getNode(pHead, idx);
//通过idx删除链表指定索引位置的节点
delNodeIdx(pHead, idx);
//通过节点的指针来删除该指针
delNode(pHead, pNode);
//查看链表的长度 可以用来计数 可以用来判断链表是否为空
getLen(pHead);
//关于遍历查找
main.c文件中有应用示例,就是打印整个链表部分
2)实验效果
测试开始.....
定义一个链表 pMylist.....
创建链表头SUCCESS
...........测试 addNodePend函数 .............
添加第一个节点 “damao”.....
添加第二个节点 “ermao”.....
添加第三个节点 “sanmao”.....
添加第四个节点 “xiaomao”.....
正向打印出链表中的信息
the total len is: 4
the idx 0 is: damao
the idx 1 is: ermao
the idx 2 is: sanmao
the idx 3 is: xiaomao
...........测试 addNodeInhead函数 .............
在链表的开头插入 “xiaomei_young” ☆
正向打印出链表中的信息
the total len is: 5
the idx 0 is: xiaomei_young
the idx 1 is: damao
the idx 2 is: ermao
the idx 3 is: sanmao
the idx 4 is: xiaomao
...........测试 insertNodeIdx函数 .............
在“ermao”位置插入 “keren” ☆
正向打印出链表中的信息
the total len is: 6
the idx 0 is: xiaomei_young
the idx 1 is: damao
the idx 2 is: keren
the idx 3 is: ermao
the idx 4 is: sanmao
the idx 5 is: xiaomao
...........测试 getNode函数 .............
在获取索引2上的信息 ☆
the message you get is: keren
正向打印出链表中的信息
the total len is: 6
the idx 0 is: xiaomei_young
the idx 1 is: damao
the idx 2 is: keren
the idx 3 is: ermao
the idx 4 is: sanmao
the idx 5 is: xiaomao
...........测试 delNodeIdx函数 .............
在获取索引0上的信息
the message you get is: xiaomei_young
删除索引0上的节点 ☆
正向打印出链表中的信息
the total len is: 5
the idx 0 is: damao
the idx 1 is: keren
the idx 2 is: ermao
the idx 3 is: sanmao
the idx 4 is: xiaomao
...........测试 delNode函数 .............
在获取索引1上的信息
the message you get is: keren
通过指针地址删除查找到的节点 ☆
正向打印出链表中的信息
the total len is: 4
the idx 0 is: damao
the idx 1 is: ermao
the idx 2 is: sanmao
the idx 3 is: xiaomao
...............测试一下双向链表是否完整,反向打印.......................
the total len is: 4
the idx 3 is: xiaomao
the idx 2 is: sanmao
the idx 1 is: ermao
the idx 0 is: damao
3)源码
源码分为3个部分 main.c:应用测试 mylist.h:相关数据结构的声明和函数声明 mylist.c:函数实现 。
相关的程序贴在下面了,自己对哪个函数有兴趣,自行查找,个人建议复制出来用好用的代码查看工具来阅读。如果发现程序中有错误,希望能反馈给我,大家一起学习进步。
//main.c
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "mylist.h"
int main(int argc, char* argv[])
{
pList_Head pMylist = NULL;
pList_Node pMynode = NULL;
unsigned int i;
printf("测试开始.....\r\n");
/* 定义一个链表的头 */
printf("定义一个链表 pMylist.....\r\n");
pMylist = creatList();
if (pMylist){
printf("创建链表头SUCCESS\r\n");
}
else{
printf("创建链表头失败\r\n");
return 0;
}
printf("\r\n...........测试 addNodePend函数 .............\r\n");
/* 添加节点 */
printf("添加第一个节点 “damao”.....\r\n");
pMynode = creatNode(); /* 创建节点 */
if (pMynode){
pMynode->dataBuf = "damao";
addNodePend(pMylist, pMynode);
}
else{
printf("添加节点“damao”失败\r\n");
}
printf("添加第二个节点 “ermao”.....\r\n");
pMynode = creatNode();
if (pMynode){
pMynode->dataBuf = "ermao";
addNodePend(pMylist, pMynode);
}
else{
printf("添加节点“ermao”失败\r\n");
}
printf("添加第三个节点 “sanmao”.....\r\n");
pMynode = creatNode();
if (pMynode){
pMynode->dataBuf = "sanmao";
addNodePend(pMylist, pMynode);
}
else{
printf("添加节点“sanmao”失败\r\n");
}
printf("添加第四个节点 “xiaomao”.....\r\n");
pMynode = creatNode();
if (pMynode){
pMynode->dataBuf = "xiaomao";
addNodePend(pMylist, pMynode);
}
else{
printf("添加节点“xiaomao”失败\r\n");
}
/* 将信息打印出来 */
printf("\r\n正向打印出链表中的信息\r\n");
printf("the total len is: %d \r\n", getLen(pMylist));
for (i = 0; i < pMylist->len; i++){
if (0 == i)
pMynode = pMylist->pFirstNode;
else
pMynode = pMynode->pNextNode;
printf("the idx %d is: %s \r\n", i, pMynode->dataBuf);
}
printf("\r\n...........测试 addNodeInhead函数 .............\r\n");
printf("在链表的开头插入 “xiaomei_young” ☆\r\n");
pMynode = creatNode();
if (pMynode){
pMynode->dataBuf = "xiaomei_young";
addNodeInhead(pMylist, pMynode);
}
else{
printf("添加节点“xiaomei_young”失败\r\n");
}
/* 将信息打印出来 */
printf("\r\n正向打印出链表中的信息\r\n");
printf("the total len is: %d \r\n", getLen(pMylist));
for (i = 0; i < pMylist->len; i++){
if (0 == i)
pMynode = pMylist->pFirstNode;
else
pMynode = pMynode->pNextNode;
printf("the idx %d is: %s \r\n", i, pMynode->dataBuf);
}
printf("\r\n...........测试 insertNodeIdx函数 .............\r\n");
printf("在“ermao”位置插入 “keren” ☆\r\n");
pMynode = creatNode();
if (pMynode){
pMynode->dataBuf = "keren";
insertNodeIdx(pMylist, pMynode, 2);
}
else{
printf("添加节点“keren”失败\r\n");
}
/* 将信息打印出来 */
printf("\r\n正向打印出链表中的信息\r\n");
printf("the total len is: %d \r\n", getLen(pMylist));
for (i = 0; i < pMylist->len; i++){
if (0 == i)
pMynode = pMylist->pFirstNode;
else
pMynode = pMynode->pNextNode;
printf("the idx %d is: %s \r\n", i, pMynode->dataBuf);
}
printf("\r\n...........测试 getNode函数 .............\r\n");
printf("在获取索引2上的信息 ☆\r\n");
pMynode = getNode(pMylist, 2);
if (pMynode){
printf("the message you get is: %s \r\n", pMynode->dataBuf);
}
else{
printf("获取节点2失败了\r\n");
}
/* 将信息打印出来 */
printf("\r\n正向打印出链表中的信息\r\n");
printf("the total len is: %d \r\n", getLen(pMylist));
for (i = 0; i < pMylist->len; i++){
if (0 == i)
pMynode = pMylist->pFirstNode;
else
pMynode = pMynode->pNextNode;
printf("the idx %d is: %s \r\n", i, pMynode->dataBuf);
}
printf("\r\n...........测试 delNodeIdx函数 .............\r\n");
printf("在获取索引0上的信息\r\n");
pMynode = getNode(pMylist, 0);
if (pMynode){
printf("the message you get is: %s \r\n", pMynode->dataBuf);
}
else{
printf("获取节点0失败了\r\n");
}
printf("删除索引0上的节点 ☆\r\n");
delNodeIdx(pMylist, 0);
/* 将信息打印出来 */
printf("\r\n正向打印出链表中的信息\r\n");
printf("the total len is: %d \r\n", getLen(pMylist));
for (i = 0; i < pMylist->len; i++){
if (0 == i)
pMynode = pMylist->pFirstNode;
else
pMynode = pMynode->pNextNode;
printf("the idx %d is: %s \r\n", i, pMynode->dataBuf);
}
printf("\r\n...........测试 delNode函数 .............\r\n");
printf("在获取索引1上的信息\r\n");
pMynode = getNode(pMylist, 1);
if (pMynode){
printf("the message you get is: %s \r\n", pMynode->dataBuf);
}
else{
printf("获取节点1失败了\r\n");
}
printf("通过指针地址删除查找到的节点 ☆\r\n");
delNode(pMylist, pMynode);
/* 将信息打印出来 */
printf("\r\n正向打印出链表中的信息\r\n");
printf("the total len is: %d \r\n", getLen(pMylist));
for (i = 0; i < pMylist->len; i++){
if (0 == i)
pMynode = pMylist->pFirstNode;
else
pMynode = pMynode->pNextNode;
printf("the idx %d is: %s \r\n", i, pMynode->dataBuf);
}
printf("\r\n...............测试一下双向链表是否完整,反向打印.......................\r\n");
printf("the total len is: %d \r\n", pMylist->len);
for (i = 0; i < pMylist->len; i++){
if (0 == i)
pMynode = pMylist->pLastNode;
else
pMynode = pMynode->pPrevNode;
printf("the idx %d is: %s \r\n", pMylist->len - 1 - i, pMynode->dataBuf);
}
while(1);
return 0;
}
//lish.h中的代码
/**************************************************
描述:c语言 双向链表功能
时间:2020.12.27
版本:V1.0
作者:什么mac
版权:无
**************************************************/
#ifndef _MYLIST_H
#define _MYLIST_H
/* 设备节点的结构体 NODE */
typedef struct stNode{
/* 节点中的私有定义数据部分 start 我这里没有实际*/
char* dataBuf;
/* 节点中的私有定义数据部分 end*/
struct stNode* pNextNode; /* 下一个节点地址 */
struct stNode* pPrevNode; /* 上一个节点地址 */
} *pList_Node, List_Node;
/* 链表头的结构体 HEAD*/
typedef struct stHead{
unsigned int len; /* 链表中的节点的个数 */
pList_Node pFirstNode; /* 链表起始节点 */
pList_Node pLastNode; /* 链表中最新的一个节点 */
} *pList_Head, List_Head;
/* 定义与实现的函数 */
extern pList_Head creatList(void);
extern pList_Head creatNode(void);
extern int insertNodeIdx(pList_Head pHead, pList_Node pNode, unsigned int idx);
extern int addNodeInhead(pList_Head pHead, pList_Node pNode);
extern int addNodePend(pList_Head pHead, pList_Node pNode);
extern pList_Node getNode(pList_Head pHead, unsigned int idx);
extern int delNodeIdx(pList_Head pHead, unsigned int idx);
extern int delNode(pList_Head pHead, pList_Node pNode);
extern unsigned int getLen(pList_Head pHead);
#endif
//list.c中的代码
#include <malloc.h>
#include <string.h>
#include "mylist.h"
/***********************************************************
@ 函数名:creatList
@ 返回值:链表的头的地址 NULL:创建链表失败
@ 参数:无
@ 作者:什么mac
@ 时间:2020.12.27
@ 功能:创建一个链表头并返回地址
***********************************************************/
pList_Head creatList(void)
{
pList_Head phead = NULL;
phead = (pList_Head)malloc(sizeof(List_Head)); /* 申请空间 */
if(phead)
memset(phead, 0, sizeof(List_Head)); /* 清零*/
return phead;
}
/***********************************************************
@ 函数名:creatNode
@ 返回值:申请到并且清零的节点地址 NULL:创建节点失败
@ 参数:无
@ 作者:什么mac
@ 时间:2020.12.27
@ 功能:申请一个节点空间并且初始化
***********************************************************/
pList_Head creatNode(void)
{
pList_Node pNode = NULL;
pNode = (pList_Node)malloc(sizeof(List_Node)); /* 申请节点空间 */
if (pNode)
memset(pNode, 0, sizeof(List_Node)); /* 清零*/
return pNode;
}
/***********************************************************
@ 函数名:insertNodeIdx
@ 返回值:0插入成功 负值 插入节点失败了
@ 参数:pHead要插入的链表 pNode要插入的节点 idx要插入的位置
@ 作者:什么mac
@ 时间:2020.12.27
@ 功能:在双头链表指定位置插入一个节点 idx范围 0 到(len-1)
***********************************************************/
int insertNodeIdx(pList_Head pHead, pList_Node pNode, unsigned int idx)
{
unsigned int temp = 0, i;
pList_Node pNodeTemp = NULL;
if(!pNode || (idx >= pHead->len)) /* 插入的节点为空或者大于了现有节点范围 */
return -1;
if(idx){
/* 开始查找对应的插入点*/
temp = (pHead->len)/2; /* 不插入到开头 */
if(idx < temp){ /* 从链头开始查询 */
pNodeTemp = pHead->pFirstNode;
for(i = 0; i < pHead->len; i++){
if(idx == i)
break;
pNodeTemp = pNodeTemp->pNextNode;
}
}
else{ /* 从链尾开始查询 */
pNodeTemp = pHead->pLastNode;
for(i = 0; i < pHead->len; i++ ){
if(idx == (pHead->len - i - 1))
break;
pNodeTemp = pNodeTemp->pPrevNode;
}
}
/* 处理对应的插入 */
pNode->pPrevNode = pNodeTemp->pPrevNode;
pNode->pNextNode = pNodeTemp;
pNodeTemp->pPrevNode->pNextNode = pNode;
pNodeTemp->pPrevNode = pNode;
}
else{ /* 插入到0 即开头位置 */
if(pHead->pFirstNode){
pNode->pNextNode = pHead->pFirstNode; /* 不为空 */
pHead->pFirstNode->pPrevNode = pNode;
}
else{
pHead->pLastNode = pNode; /* 为空 */
}
pHead->pFirstNode = pNode;
}
pHead->len += 1; /* 长度加以 */
return 0;
}
/***********************************************************
@ 函数名:addNodeInhead
@ 返回值:0添加成功 负值 添加节点失败了
@ 参数:pHead要插入的链表 pNode要插入的节点
@ 作者:什么mac
@ 时间:2020.12.27
@ 功能:在双头链表的开头位置添加一个节点
***********************************************************/
int addNodeInhead(pList_Head pHead, pList_Node pNode)
{
return insertNodeIdx(pHead, pNode, 0);
}
/***********************************************************
@ 函数名:addNodePend
@ 返回值:0添加成功 负值 添加节点失败了
@ 参数:pHead要追加的链表 pNode要追加的节点
@ 作者:什么mac
@ 时间:2020.12.27
@ 功能:在双头链的末尾添加一个节点
***********************************************************/
int addNodePend(pList_Head pHead, pList_Node pNode)
{
if(!pNode)
return -1;
/* 判断最新的节点是否为空 */
if(pHead->pLastNode){
pHead->pLastNode->pNextNode = pNode; /* 链表不为空 */
pNode->pPrevNode = pHead->pLastNode;
}
else{
pHead->pFirstNode = pNode; /* 链表是空的 */
}
pHead->pLastNode = pNode; /* 刷新最新并且计数加一 */
pHead->len += 1;
return 0;
}
/***********************************************************
@ 函数名:getNode
@ 返回值:NULL查找节点失败 非空 查找到节点的的起始地址
@ 参数:pHead要查找的链表 idx要查找的节点位置
@ 作者:什么mac
@ 时间:2020.12.27
@ 功能:通过索引位置数值获取对应节点的信息
***********************************************************/
pList_Node getNode(pList_Head pHead, unsigned int idx)
{
unsigned int temp = 0, i;
pList_Node pNodeTemp = NULL;
/* 判断是否是合法 */
if(idx >= pHead->len ) /* 查找的范围超出了现有 */
return NULL;
/* 根据情况分情况从两头趋近指定的索引节点 */
temp = (pHead->len)/2;
if(idx < temp){ /* 从链头开始查询 */
pNodeTemp = pHead->pFirstNode;
for(i = 0; i < pHead->len; i++){
if(idx == i)
break;
pNodeTemp = pNodeTemp->pNextNode;
}
}
else{ /* 从链尾开始查询 */
pNodeTemp = pHead->pLastNode;
for(i = 0; i < pHead->len; i++){
if(idx == (pHead->len - i - 1))
break;
pNodeTemp = pNodeTemp->pPrevNode;
}
}
/* 返回查询到的节点指针 */
return pNodeTemp;
}
/***********************************************************
@ 函数名:delNodeIdx
@ 返回值:负值 删除失败了 0 删除成功了
@ 参数:pHead要删除节点所在链表 idx要删除的节点位置
@ 作者:什么mac
@ 时间:2020.12.27
@ 功能:通过索引删除指定的节点
***********************************************************/
int delNodeIdx(pList_Head pHead, unsigned int idx)
{
pList_Node pNodeTemp = NULL;
/* 查找对应的节点并且 判断是否为空 */
pNodeTemp = getNode(pHead, idx);
if(!pNodeTemp)
return -1;
/* 此节点的上一个节点是否为空 */
if(pNodeTemp->pPrevNode){
pNodeTemp->pPrevNode->pNextNode = pNodeTemp->pNextNode;
}
else{
pHead->pFirstNode = pNodeTemp->pNextNode; /* 上一个为空,说明此节点是firstNode */
}
/* 此节点的下一个节点是否为空 */
if(pNodeTemp->pNextNode){
pNodeTemp->pNextNode->pPrevNode = pNodeTemp->pPrevNode;
}
else{
pHead->pLastNode = pNodeTemp->pPrevNode;
}
/* 释放空间 */
free(pNodeTemp);
/* 计数减一 */
if(pHead->len)
pHead->len -= 1;
return 0;
}
/***********************************************************
@ 函数名:delNode
@ 返回值:负值 删除失败了 0 删除成功了
@ 参数:pHead要删除节点所在链表 pNode要删除的节点指针地址
@ 作者:什么mac
@ 时间:2020.12.27
@ 功能:通过指针删除指定的节点
***********************************************************/
int delNode(pList_Head pHead, pList_Node pNode)
{
pList_Node pNodeTemp = NULL;
/* 上面那个函数改的 指针接过来 判断是否为空 */
pNodeTemp = pNode;
if (!pNodeTemp)
return -1;
/* 此节点的上一个节点是否为空 */
if (pNodeTemp->pPrevNode){
pNodeTemp->pPrevNode->pNextNode = pNodeTemp->pNextNode;
}
else{
pHead->pFirstNode = pNodeTemp->pNextNode; /* 上一个为空,说明此节点是firstNode */
}
/* 此节点的下一个节点是否为空 */
if (pNodeTemp->pNextNode){
pNodeTemp->pNextNode->pPrevNode = pNodeTemp->pPrevNode;
}
else{
pHead->pLastNode = pNodeTemp->pPrevNode;
}
/* 释放空间 */
free(pNodeTemp);
/* 计数减一 */
if (pHead->len)
pHead->len -= 1;
return 0;
}
/***********************************************************
@ 函数名:getLen
@ 返回值:节点数目
@ 参数:pHead所要查看的链表
@ 作者:什么mac
@ 时间:2020.12.27
@ 功能:获取指定链表的长度
***********************************************************/
unsigned int getLen(pList_Head pHead)
{
return pHead->len;
}