【C语言】C语言实现双链表

头文件

#pragma once
#ifndef  _L_LIST_H
#define _L_LIST_H


/* 双向链表节点结构体声明 */
typedef struct node
{
	struct node* nextNode, * prevNode;	//指向上一个节点和下一个节点的地址
	void* nodeData;						//指向节点的数据地址
}node_t, * pNode_t;

/* 双向链表结构体声明 */
typedef struct list
{
	struct  node* headNode;		//指向头节点的地址
	int nodeCount;				//节点数量 包括头节点
}list_t, * pList_t;


pList_t CreateList(unsigned int nodeCount);
void DeleteList(pList_t list);
pList_t LinkList(pList_t list1, pList_t list2);
void PrintfList(pList_t list);

pList_t AddNode(pList_t list, pNode_t nodeToAdd);
pList_t DeleteNode(pList_t list, pNode_t nodeToDelete);

pNode_t FindNodeByIndex(pList_t list, unsigned int index);
pList_t InsertNodeByIndex(pList_t list, pNode_t nodeToInsert, unsigned int indexToInsert);
int GetNodeIndex(pList_t list, pNode_t node);

#endif // ! _L_LIST_H

源代码

/* 需要用到的C库文件 */
#include <stdlib.h>
#include <stdio.h>
#include "lList.h"


/** \brief 	删除双链表
 *
 * \param   list 双链表的地址
 * \return  无
 *
 */
void DeleteList(pList_t list)
{
	pNode_t currentNode = NULL;
	pNode_t nextNode = NULL;

	if (list == NULL)
	{
		return;
	}

	currentNode = list->headNode;
	while (list->nodeCount--)
	{
		nextNode = currentNode->nextNode;//获取下一个节点
		printf("节点:%p空间已释放\n", currentNode);
		free(currentNode);//释放当前节点
		currentNode = nextNode;//移动到下一个节点
	}
	printf("链表:%p空间已释放\n", list);
	free(list);//释放链表
}


/** \brief 	创建双链表
 *
 * \param   nodeCount 节点数量
 * \return  成功则返回链表地址
 *				失败则返回NULL
 *
 */
pList_t CreateList(unsigned int nodeCount)
{
	pNode_t currentNode = NULL;
	pNode_t prevNode = NULL;
	pList_t list = NULL;

	/* 剔除节点数量为0的情况 */
	if (!nodeCount)
	{
		return NULL;
	}

	/* 为链表分配空间并初始化为0 */
	list = calloc(1, sizeof(list_t));
	if (list == NULL)//空间分配失败则返回
	{
		//free(list);
		return NULL;
	}

	/* 为节点分配空间并建立链表 */
	while (nodeCount--)
	{
		currentNode = calloc(1, sizeof(node_t));//为节点分配空间
		if (currentNode == NULL)
		{
			/* 删除链表 */
			DeleteList(list);
			return NULL;
		}
		if (list->headNode == NULL)//头节点为空,则将当前节点视为头节点
		{
			list->headNode = currentNode;
			prevNode = currentNode;
		}
		prevNode->nextNode = currentNode;//上一个节点的下一个节点指向当前节点
		currentNode->prevNode = prevNode;//当前的节点的上一个节点指向上一个节点
		prevNode = currentNode;//保存当前节点
		list->nodeCount++;//节点数目加一
	}
	currentNode->nextNode = list->headNode;//最后一个节点的下一个节点指向首节点
	list->headNode->prevNode = currentNode;//首节点的上一个节点指向最后一个节点

	return list;
}


/** \brief 	将 list2 连接到 list1 后面
 *
 * \param   list1 双链表1的地址
 * \param   list2 双链表2的地址
 * \return  返回新的双链表的地址
 *
 */
pList_t LinkList(pList_t list1, pList_t list2)
{
	list1->headNode->prevNode->nextNode = list2->headNode;//list1的末节点的下一个节点连接list2的头节点
	list2->headNode->prevNode->nextNode = list1->headNode;//list2的末节点的下一个节点连接list1的头节点

	list1->headNode->prevNode = list2->headNode->prevNode;//list1的头节点的上一个节点连接list2的末节点
	list2->headNode->prevNode = list1->headNode->prevNode;//list2的头节点的上一个节点连接list1的末节点
	list1->nodeCount += list2->nodeCount;
	free(list2);

	return list1;
}


/** \brief 	打印链表
 *
 * \param   list 双链表的地址
 * \return  无
 *
 */
void PrintfList(pList_t list)
{
	pNode_t currentNode = NULL;
	int nodeCount = list->nodeCount;

	printf("双链表链表的地址:%p,  头节点的地址:%p,节点数目:%-4d\n", list, list->headNode, list->nodeCount);
	currentNode = list->headNode;

	while (nodeCount--)
	{
		printf("上一个节点的地址:%p,当前节点的地址:%p,下一个节点的地址:%p\n", currentNode->prevNode, currentNode, currentNode->nextNode);
		currentNode = currentNode->nextNode;
	}
}


/** \brief 	添加一个新节点到链表的末尾
 *
 * \param   list 双链表的地址
 * \param   nodeToAdd 要添加的节点的地址
 * \return  成功则返回链表的地址
 *			失败则返回NULL
 */
pList_t AddNode(pList_t list, pNode_t nodeToAdd)
{
	pNode_t node = NULL;

	/* 剔除空链表和空节点情况 */
	if (list == NULL || nodeToAdd == NULL)
	{
		return NULL;
	}

	/* 为新节点分配空间并复制数据 */
	node = calloc(1, sizeof(node_t));
	if (node == NULL)//空间分配失败
	{
		//free(node);
		return NULL;
	}
	node->nodeData = nodeToAdd->nodeData;

	/* 将新节点添加到链表末尾 */
	node->nextNode = list->headNode;//要添加的节点下一个节点是链表的头节点
	node->prevNode = list->headNode->prevNode;//要添加的节点上一个节点是链表的末尾节点

	list->headNode->prevNode->nextNode = node;//链表的末尾节点的下一个节点是要添加的节点
	list->headNode->prevNode = node;//链表的头节点的上一个节点是要添加的节点
	list->nodeCount++;//节点数量加一

	return list;
}


/** \brief 	通过下标索引获取节点的地址
 *
 * \param   list 双链表的地址
 * \param   index 节点的下标索引(下标从0开始,头节点的下标是0)
 * \return  成功则返回节点的地址
 *			失败则返回NULL
 */
pNode_t FindNodeByIndex(pList_t list, unsigned int index)
{
	pNode_t node = NULL;

	/* 剔除空链表的情况 */
	if (list == NULL)
	{
		return NULL;
	}

	/* 寻找节点 */
	node = list->headNode;
	while (index--)
	{
		node = node->nextNode;
	}

	return node;
}



/** \brief 	插入节点到指定的位置
 *
 * \param   list 双链表的地址
 * \param   nodeToInsert 要插入的节点的地址
 * \param   index 节点的下标索引(下标从0开始,头节点的下标是0)
 * \return  成功则返回链表的地址
 *			失败则返回NULL
 */
pList_t InsertNodeByIndex(pList_t list, pNode_t nodeToInsert, unsigned int indexToInsert)
{
	pNode_t node = NULL;

	/* 剔除空链表和空节点的情况 */
	if (list == NULL || nodeToInsert == NULL)
	{
		return NULL;
	}

	/* 为新节点分配空间并复制数据 */
	node = calloc(1, sizeof(node_t));
	if (node == NULL)//空间分配失败
	{
		//free(node);
		return NULL;
	}
	node->nodeData = nodeToInsert->nodeData;//复制数据
	nodeToInsert = node;//保存地址

	/* 找到下标索引位置的节点 */
	node = FindNodeByIndex(list, indexToInsert);

	/* 插入节点 */
	nodeToInsert->nextNode = node;//要插入的节点的下一个节点是找到的节点
	nodeToInsert->prevNode = node->prevNode;//要插入的节点的上一个节点是找到的节点的上一个节点

	node->prevNode->nextNode = nodeToInsert;//找到的节点的上一个节点的下一个节点是要插入的节点
	node->prevNode = nodeToInsert;//找到的节点的上一个节点是要插入的节点

	/* 判断要插入的节点是不是链表的头节点 */
	if (indexToInsert % list->nodeCount == 0)
	{
		list->headNode = nodeToInsert;
	}
	list->nodeCount++;//节点数量加一

	return list;
}


/** \brief 	删除节点
 *
 * \param   list 双链表的地址
 * \param   nodeToDelete 要添加的节点的地址
 * \return  成功则返回链表的地址
 *			失败则返回NULL
 */
pList_t DeleteNode(pList_t list, pNode_t nodeToDelete)
{
	/* 剔除空链表和空节点的情况 */
	if (list == NULL || nodeToDelete == NULL)
	{
		return NULL;
	}

	/* 判断要删除的节点是不是链表的头节点 */
	if (nodeToDelete == list->headNode)
	{
		list->headNode = list->headNode->nextNode;
	}

	/* 删除节点 */
	nodeToDelete->prevNode->nextNode = nodeToDelete->nextNode;//要删除的节点的上一个节点的下一个节点是要删除的节点的下一个节点
	nodeToDelete->nextNode->prevNode = nodeToDelete->prevNode;//要删除的节点的下一个节点的上一个节点是要删除的节点的上一个节点

	free(nodeToDelete);//释放空间
	list->nodeCount--;//节点数量减一

	return list;
}


/** \brief 	获取节点在链表中的下标索引
 *				满足节点的地址或者节点的数据相等则返回
 * \param   list 双链表的地址
 * \param   node 节点的地址
 * \return  成功则返回节点索引
 *			失败则返回-1
 */
int GetNodeIndex(pList_t list, pNode_t node)
{
	int nodeCount = 0;
	int index = 0;
	pNode_t currentNode = NULL;

	/* 剔除空链表和空节点的情况 */
	if (list == NULL || node == NULL)
	{
		return -1;
	}

	currentNode = list->headNode;
	nodeCount = list->nodeCount;

	/* 寻找节点的位置 */
	while (nodeCount--)
	{
		//满足节点的地址或者节点的数据条件则返回
		if (currentNode == node || currentNode->nodeData == node->nodeData)
		{
			return index;
		}
		currentNode = currentNode->nextNode;
		index++;
	}

	return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值