【C语言】双链表 双向链表 增删改查 实现 V1.0

【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;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值