数据结构 - 链表(双向链表 、 通用结构)

链表的概念

在这篇博客“数据结构 - 链表(单向链表 、 单一结构)”简单介绍了链表,但是这种形式只能单一数据结构的,但可能在项目中有不同的数据结构都需要用到链表,每个数据都重写一遍链表,那将是很麻烦很冗余的事情。
本文介绍一种是可以通用数据结构的链表,只用一套链表代码就可以实现对不同数据结构的复用。其实基本的思路就是,在数据域中不在具体定义某个数据类型的变量,而是定义为一个空指针,该指针指向的才是真正的数据域。
所以在链表添加元素时候,除了要分配结点空间,还要分配数据域空间,同样在删除元素时候就需要释放数据域空间。那么怎么知道分配数据域空间时要分配多大空间呢,在链表结构体中新增个“data_size”变量用来记录数据域大小,每次分配就分配这个大小的空间就可以了,“data_size”变量在链表初始化时就要完成赋值。
顺便采用双向链表,来对比不同。单向链表时候,只需要一个链表头就能找到链表的位置,而双向链表还需增加个链表尾,以便从尾部开始遍历链表。
链表

C语言代码

头文件

#ifndef __list_H
#define __list_H

/* 节点结构体定义 */
typedef struct _LIST_NODE_
{
	struct _LIST_NODE_ *prev;
	struct _LIST_NODE_ *next;
	void *data;
}LIST_NODE;

/* 链表结构体定义 */
typedef struct _LIST_
{
	LIST_NODE *list_head;
	LIST_NODE *list_tail;
	unsigned int count;
	unsigned char data_size;
}LIST;

unsigned char list_init(LIST *list, unsigned char data_size);
unsigned char list_add(LIST *list, unsigned int position, void *data);
unsigned char list_add_head(LIST *list, void *data);
unsigned char list_add_tail(LIST *list, void *data);
unsigned char list_get(LIST *list, unsigned int position, void *data);
unsigned char list_alter(LIST *list, unsigned int position, void *data);
unsigned char list_delete(LIST *list, unsigned int position);
unsigned char list_delete_head(LIST *list);
unsigned char list_delete_tail(LIST *list);
void list_clean(LIST *list);
void list_free(LIST *list);
unsigned int list_get_count(LIST *list);
unsigned char list_empty(LIST *list);

#endif

看到头文件和原来的对比,结点结构体部分增加指向前一结点的“prev”指针,数据域也由原来的实际数据类型改成了void类型的指针。链表结构体部分,增加了链表尾部,以及记录数据域大小的“data_size”。

源文件

#include "list.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/*********************************************************************************************************
** 函数名称:	list_init
** 功能描述:	链表初始化
** 输 入:		list: 链表
				data_size: 数据大小
** 输 出:		1: 初始化成功
				0: 初始化失败
********************************************************************************************************/
unsigned char list_init(LIST *list, unsigned char data_size)
{
	// 申请空间
	list->list_head = (LIST_NODE *)malloc(sizeof(LIST_NODE));
	if (list->list_head == NULL)
	{
		return 0;
	}
	list->list_tail = (LIST_NODE *)malloc(sizeof(LIST_NODE));
	if (list->list_tail == NULL)
	{
		free(list->list_head); // 释放前面创建好的空间
		return 0;
	}

	list->list_head->data = NULL;
	list->list_head->prev = NULL;
	list->list_head->next = list->list_tail;

	list->list_tail->data = NULL;
	list->list_tail->prev = list->list_head;
	list->list_tail->next = NULL;

	list->count = 0;
	list->data_size = data_size;

	return 1;
}

/*********************************************************************************************************
** 函数名称:	list_add
** 功能描述:	增加元素
** 输 入:		list: 链表
				position: 位置
				data: 数据变量的地址
** 输 出:		1: 增加成功
				0: 增加失败
********************************************************************************************************/
unsigned char list_add(LIST *list, unsigned int position, void *data)
{
	unsigned int index = 0;
	LIST_NODE *temp_node = NULL;
	LIST_NODE *prev_node = NULL, *next_node = NULL;

	/* 检查位置是否超过链表的长度 */
	if (position > list->count)
	{
		return 0;
	}

	/* 找到最短的插入距离 */ 
	if (position <= ((list->count - 1) / 2)) // 从头部开始查找
	{
		/* 循环找到插入位置 */
		prev_node = list->list_head;
		for (index = 0; index < position; index++)
		{
			prev_node = prev_node->next;
		}
		next_node = prev_node->next;
	}
	else // 从尾部开始查找
	{
		/* 循环找到插入位置 */
		next_node = list->list_tail;
		for (index = list->count - 1; index > position; index--)
		{
			next_node = next_node->prev;
		}
		prev_node = next_node->prev;
	}

	/* 创建空间,赋值 */
	temp_node = (LIST_NODE *)malloc(sizeof(LIST_NODE));
	if (temp_node == NULL) 
	{
		return 0;
	}
	temp_node->data = malloc(list->data_size);
	if (temp_node->data == NULL) 
	{
		free(temp_node); // 释放前面申请好的空间
		return 0;
	}
	memcpy(temp_node->data, data, list->data_size);

	/* 连接新节点 */
	temp_node->next = next_node;
	next_node->prev = temp_node;

	temp_node->prev = prev_node;
	prev_node->next = temp_node;

	/* 链表节点数更新 */
	list->count++;

	return 1;
}

/*********************************************************************************************************
** 函数名称:	list_add_head
** 功能描述:	头插
** 输 入:		list: 链表
				data: 数据变量的地址
** 输 出:		1: 增加成功
				0: 增加失败
********************************************************************************************************/
unsigned char list_add_head(LIST *list, void *data)
{
	return list_add(list, 0, data);
}

/*********************************************************************************************************
** 函数名称:	list_add_tail
** 功能描述:	尾插
** 输 入:		list: 链表
				data: 数据变量的地址
** 输 出:		1: 增加成功
				0: 增加失败
********************************************************************************************************/
unsigned char list_add_tail(LIST *list, void *data)
{
	return list_add(list, list->count, data);
}

/*********************************************************************************************************
** 函数名称:	list_get
** 功能描述:	获取元素
** 输 入:		list: 链表
				position: 位置
				data: 数据变量的地址
** 输 出:		1: 获取成功
				0: 获取失败
********************************************************************************************************/
unsigned char list_get(LIST *list, unsigned int position, void *data)
{
	unsigned int index = 0;
	LIST_NODE *temp_node = NULL;

	/* 检查位置是否超过链表 */
	if (position >= list->count)
	{
		return 0;
	}

	if (position <= (list->count - 1) / 2) // 从头部开始查找
	{
		/* 遍历找到节点位置 */
		temp_node = list->list_head->next;
		for (index = 0; index < position; index++)
		{
			temp_node = temp_node->next;
		}
	}
	else // 从尾部开始查找
	{
		/* 遍历找到节点位置 */
		temp_node = list->list_tail->prev;
		for (index = list->count - 1; index > position; index--)
		{
			temp_node = temp_node->prev;
		}
	}

	// 赋值
	memcpy(data, temp_node->data, list->data_size);

	return 1;
}

/*********************************************************************************************************
** 函数名称:	list_alter
** 功能描述:	修改元素
** 输 入:		list: 链表
				position: 位置
				data: 数据变量的地址
** 输 出:		1: 修改成功
				0: 修改失败
********************************************************************************************************/
unsigned char list_alter(LIST *list, unsigned int position, void *data)
{
	unsigned int index = 0;
	LIST_NODE *temp_node = NULL;

	/* 检查位置是否超过链表 */
	if (position >= list->count)
	{
		return 0;
	}

	if (position <= (list->count - 1) / 2) // 从头部开始查找
	{
		/* 遍历找到节点位置 */
		temp_node = list->list_head->next;
		for (index = 0; index < position; index++)
		{
			temp_node = temp_node->next;
		}
	}
	else // 从尾部开始查找
	{
		/* 遍历找到节点位置 */
		temp_node = list->list_tail->prev;
		for (index = list->count - 1; index > position; index--)
		{
			temp_node = temp_node->prev;
		}
	}

	/* 覆盖修改旧内容 */
	memcpy(temp_node->data, data, list->data_size);

	return 1;
}

/*********************************************************************************************************
** 函数名称:	list_delete
** 功能描述:	删除元素
** 输 入:		list: 链表
				position: 位置
** 输 出:		1: 删除成功
				0: 删除失败
********************************************************************************************************/
unsigned char list_delete(LIST *list, unsigned int position)
{
	unsigned int index = 0;
	LIST_NODE *temp_node = NULL;
	LIST_NODE *prev_node = NULL, *next_node = NULL;

	/* 检查位置是否超过链表 */
	if (position >= list->count)
	{
		return 0;
	}

	if (position <= (list->count - 1) / 2) // 从头部开始查找
	{
		/* 遍历找到节点位置 */
		temp_node = list->list_head->next;
		for (index = 0; index < position; index++)
		{
			temp_node = temp_node->next;
		}
	}
	else // 从尾部开始查找
	{
		/* 遍历找到节点位置 */
		temp_node = list->list_tail->prev;
		for (index = list->count - 1; index > position; index--)
		{
			temp_node = temp_node->prev;
		}
	}

	prev_node = temp_node->prev;
	next_node = temp_node->next;

	/* 重新连接链表 */
	prev_node->next = next_node;
	next_node->prev = prev_node;

	/* 删除data,释放节点 */
	free(temp_node->data);
	free(temp_node);

	/* 链表节点数更新 */
	list->count--;

	return 1;
}

/*********************************************************************************************************
** 函数名称:	list_delete_head
** 功能描述:	头删
** 输 入:		list: 链表
** 输 出:		1: 删除成功
				0: 删除失败
********************************************************************************************************/
unsigned char list_delete_head(LIST *list)
{
	return list_delete(list, 0);
}

/*********************************************************************************************************
** 函数名称:	list_delete_tail
** 功能描述:	尾删
** 输 入:		list: 链表
** 输 出:		1: 删除成功
				0: 删除失败
********************************************************************************************************/
unsigned char list_delete_tail(LIST *list)
{
	return list_delete(list, list->count - 1);
}

/*********************************************************************************************************
** 函数名称:	list_clean
** 功能描述:	清空链表
** 输 入:		list: 链表
** 输 出:		无
********************************************************************************************************/
void list_clean(LIST *list)
{
	unsigned int index = 0;
	LIST_NODE *temp_node = NULL;
	LIST_NODE *delete_node = NULL;

	/* 检查链表是否为空 */
	if (list->count == 0)
	{
		return;
	}

	/* 循环删除每个节点 */
	temp_node = list->list_head->next;
	for (index = 0; index < list->count; index++)
	{
		delete_node = temp_node;
		temp_node = temp_node->next;
		
		free(delete_node->data);
		free(delete_node);
	}

	/* 重新连接链表头尾节点 */
	list->list_head->next = list->list_tail;
	list->list_tail->prev = list->list_head;

	list->count = 0;
}

/*********************************************************************************************************
** 函数名称:	list_free
** 功能描述:	释放链表
** 输 入:		list: 链表
** 输 出:		无
********************************************************************************************************/
void list_free(LIST *list)
{
	unsigned int index = 0;
	LIST_NODE *temp_node = NULL;
	LIST_NODE *delete_node = NULL;

	/* 循环删除每个节点 */
	temp_node = list->list_head;
	for (index = 0; index < list->count + 2; index++)
	{
		delete_node = temp_node;
		temp_node = temp_node->next;

		free(delete_node->data);
		free(delete_node);
	}

	list->list_head = NULL;
	list->list_tail = NULL;
	list->count = 0;
	list->data_size = 0;
}

/*********************************************************************************************************
** 函数名称:	list_get_count
** 功能描述:	获取链表结点数
** 输 入:		list: 链表
** 输 出:		链表结点数
********************************************************************************************************/
unsigned int list_get_count(LIST *list)
{
	return list->count;
}

/*********************************************************************************************************
** 函数名称:	list_empty
** 功能描述:	判断链表是否为空
** 输 入:		list: 链表
** 输 出:		1: 为空
				0: 非空
********************************************************************************************************/
unsigned char list_empty(LIST *list)
{
	if (list->count == 0)
	{
		return 1;
	}
	else 
	{
		return 0;
	}
}

可以看到这些函数对比原来,数据域的传入都是改为值void指针,初始化函数也增加数据域大小的设置。
双向链表也对比单向链表多了个反向的访问,所以寻找结点的时候可以先计算该节点离链表头近还是离链表尾近,从而选择是从哪个方向开始遍历。

测试

#include <stdio.h>
#include "list.h"

typedef struct 
{
	char name[12];
	int heght;
	float weight;
}STUDENT;

void print_student(STUDENT student)
{
	printf("name: %s, height = %d, weight = %.2f\r\n", student.name, student.heght, student.weight);
}

void show_list(LIST *list)
{
	LIST_NODE *t_node = NULL;
	unsigned int index = 0;
	STUDENT student;

	t_node = list->list_head->next;
	while (t_node != list->list_tail)
	{
		student = *(STUDENT *)t_node->data;
		printf("list<%d>: ", index++);
		print_student(student);
		t_node = t_node->next;
	}
}

int main()
{
	int i = 0;

	LIST list;
	STUDENT student;

	list_init(&list, sizeof(STUDENT));

	for (i = 0; i < 10; i++)
	{
		sprintf(student.name, "student%d", i);
		student.heght = 170 + i;
		student.weight = 60.5 + i;
		list_add_head(&list, &student);
	}

	show_list(&list);
	list_delete(&list, 3);
	list_delete_head(&list);
	list_delete_tail(&list);
	list_delete_tail(&list);
	show_list(&list);
	list_clean(&list);
	show_list(&list);


	for (i = 0; i < list.count; i++)
	{
		list_get(&list, i, &student);
		printf("data: ");
		print_student(student);
	}
	
	printf("list len: %d\r\n", list.count);

	getchar();
	return 0;
}

运行结果:
测试结果
最后的最后,本人才疏学浅,此代码仅供参考交流,本人如有叙述错误,麻烦各位指正,有什么好的建议欢迎留言。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值