链表的概念
在这篇博客“数据结构 - 链表(单向链表 、 单一结构)”简单介绍了链表,但是这种形式只能单一数据结构的,但可能在项目中有不同的数据结构都需要用到链表,每个数据都重写一遍链表,那将是很麻烦很冗余的事情。
本文介绍一种是可以通用数据结构的链表,只用一套链表代码就可以实现对不同数据结构的复用。其实基本的思路就是,在数据域中不在具体定义某个数据类型的变量,而是定义为一个空指针,该指针指向的才是真正的数据域。
所以在链表添加元素时候,除了要分配结点空间,还要分配数据域空间,同样在删除元素时候就需要释放数据域空间。那么怎么知道分配数据域空间时要分配多大空间呢,在链表结构体中新增个“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;
}
运行结果:
最后的最后,本人才疏学浅,此代码仅供参考交流,本人如有叙述错误,麻烦各位指正,有什么好的建议欢迎留言。