C语言之 - 双向链表

9 篇文章 3 订阅
3 篇文章 2 订阅

一、前言

之前,写过一篇《C语言之 - 单向链表》的文章,有兴趣的朋友可以看一下。

双向链表再某些特定的场合结合着单向链表能够非常简便、快速的对数据进行操作,比如有锁队列等。

可以看一下双向链表的百科:百科-双向链表

二、实现过程

我们结合着注释,一起来学习一下双向链表。文章底部有源码供大家下载。

首先我们定义一个结构体节点:

typedef struct s_msg
{
	int  count;    // 数据域:整型 count
	char str[20];  // 数据域:字符串数组,分配20个字节

	struct s_msg *prev; // 指针域:链接上一个节点
	struct s_msg *next; // 指针域:链接下一个节点
} s_msg_t;

1.双向链表的创建

/************************************************************************* 
* Function:    void double_link_creat(s_msg_t **head, s_msg_t *new)
* Description: 创建/插入双向链表
* Input:   
*              s_msg_t **head 链表头
*              s_msg_t *new   链表节点
* Return:      NULL
* Others:      NULL
**************************************************************************/
void double_link_creat(s_msg_t **head, s_msg_t *pnew)
{
	s_msg_t *tmp = *head;
	if (NULL == *head) // 头部为空,新来的节点就是第一个
	{
		*head = pnew;
		pnew->prev = NULL;
		pnew->next = NULL;
	}
	else // 第二次及以后追加节点
	{
		while (NULL != tmp->next)
		{
			tmp = tmp->next;// 循环找到最后一个节点
		}
		tmp->next  = pnew;  // 新申请的节点加入链表
		pnew->prev = tmp;
		pnew->next = NULL;
	}
}

2.双向链表的打印

/************************************************************************* 
* Function:    void double_link_print(s_msg_t * head)
* Description: 打印链表节点
* Input:   
*              s_msg_t *head 链表头
* Return:      NULL
* Others:      NULL
**************************************************************************/
void double_link_print(s_msg_t * head)
{
	if (NULL == head)
	{
		LOGD("double link is null, return.");
		return ;
	}
	
	s_msg_t *tmp = head;
	while (NULL != tmp)
	{
		LOGD("show link nodes: count = %d, str = %s\n", tmp->count, tmp->str);
		tmp = tmp->next;
	}
}

3.双向链表删除节点

/************************************************************************* 
* Function:    void double_link_delete_nodes_for_num(s_msg_t **head, int num)
* Description: 删除链表节点
* Input:   
*              s_msg_t *head 链表头
*  int num     根据链表的数据删除的链表
* Return:      NULL
* Others:      NULL
**************************************************************************/
void double_link_delete_nodes_for_num(s_msg_t **head, int num)
{
	if (NULL == *head)
	{
		LOGD("double link is null, return.");
		return ;
	}
	s_msg_t *tmp = *head, *pf;
	while ((num != tmp->count) && (NULL != tmp->next))
	{
		tmp = tmp->next;
	}
	if (num == tmp->count)
	{
		if (tmp == (*head)) // 要删除的是头节点
		{
			if (NULL == (*head)->next)  // 如果只有头节点一个数据
			{
				(*head) = tmp->next;
			}
			else// 如果有两个以上的节点
			{
				(*head) = tmp->next;
				(*head)->prev = NULL;
			}
		}
		else // 要删除的是其它节点
		{
			if (NULL != tmp->next) // 删除的是中间节点
			{
				pf = tmp->prev;
				pf->next = tmp->next;
				(tmp->next)->prev = pf;
			}
			else // 删除尾节点
			{
				pf = tmp->prev;
				pf->next = NULL;
			}
		}
		free(tmp); // 释放节点
	}
	else
	{
		LOGD("not find the num, can't delete.");
	}
}

4.双向链表插入节点

/************************************************************************* 
* Function:    void double_link_insert_for_num(s_msg_t **head, s_msg_t *pnew)
* Description: 按数据大小插入链表节点
* Input:   
*              s_msg_t **head 链表头
*              s_msg_t *pnew  要插入的节点
* Return:      NULL
* Others:      NULL
**************************************************************************/
void double_link_insert_for_num(s_msg_t **head, s_msg_t *pnew)
{
	s_msg_t *pf, *tmp = (*head);
	if (NULL == tmp) // 链表为空,新来的就是头节点
	{
		(*head)= pnew;
		pnew->prev = NULL;
		pnew->next = NULL;
		return ;
	}
	while ((pnew->count >= tmp->count) && (NULL != tmp->next))
	{
		tmp = tmp->next;
	}
	if (pnew->count < tmp->count) // 找到一个tmp的count比pnew的count大,插到tmp前面
	{
		if (tmp == (*head)) // 找到的是头节点,查到头节点前面
		{
			pnew->next = *head;
			(*head)->prev = pnew;
			pnew->prev = NULL;
			(*head) = pnew;
		}
		else // 插到中间位置
		{
			pf = tmp->prev; // pf指向找到节点的前一个位置

			pnew->next = tmp;
			pnew->prev = pf;

			pf->next   = pnew;
			tmp->prev  = pnew;
		}
	}
	else // tmp中的count都比pnew中的count小,插到最后面
	{
		tmp->next  = pnew;
		pnew->prev = tmp;
		pnew->next = NULL;
	}
}

5.双向链表查找节点

/************************************************************************* 
* Function:    s_msg_t *double_link_search_for_num(s_msg_t *head, int num)
* Description: 查找链表节点
* Input:   
*              s_msg_t **head 链表头
*              int num要搜寻的节点的num
* Return:      NULL
* Others:      NULL
**************************************************************************/
s_msg_t *double_link_search_for_num(s_msg_t *head, int num)
{
	if (NULL == head)
	{
		LOGD("double link is null, return.");
		return ;
	}

	s_msg_t *tmp = head;
	while (NULL != tmp)
	{
		if (num == tmp->count)
		{
			LOGD("find nodes: count = %d, str = %s", tmp->count, tmp->str);
			//return NULL;
		}
		tmp = tmp->next;
	}

	return NULL;
	
}

6.双向链表的释放

注:释放方式可能还不完善

/************************************************************************* 
* Function:    s_msg_t *double_link_search_for_num(s_msg_t *head, int num)
* Description: 双向链表释放
* Input:   
*              s_msg_t **head 链表头
* Return:      NULL
* Others:      NULL
**************************************************************************/
void double_link_free(s_msg_t **head)
{
	if (NULL == (*head)) // 若为空,直接返回
	{
		LOGD("double link is null, return.");
		return ;
	}
	LOGD("free double link.");
	s_msg_t *tmp = NULL;
	while (NULL != (*head)) // 若头部不为空
	{
		tmp = (*head);  // 临时变量赋值
		(*head) = tmp->next;// head后移一位
		tmp->next = NULL;   // 临时变量下一个指向空
		if (NULL != *head)  // 判断head是否为空
		{
			(*head)->prev = NULL; // 不为空则前一个指向空
		}
		free(tmp);
		tmp = NULL;
	}
}

demo源码:点击下载双向链表demo 提取码:vfer

下载源码放到linux系统下直接make编译即可。

三、结束语

大家通过上面的例子灵活运用,根据自己的需求去修改代码。就介绍到这里吧,通过多写代码加深对链表的使用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值