链表相关知识(通用链表)

通用链表

通用链表作用

在链表相关知识(初学)中,写出来一个简单的链表基本操作,但是该链表有一个明显的弊端,即我们所实现的函数只能为person这个结构体服务,每个函数的入口参数都是person结构体,对应的原理和代码如下所示:
在这里插入图片描述

struct person{
	char *name;
	int age;
	struct person *next;
};//定义一个结构体成员

struct list{
	char *name;
	struct person head;
};//定义一个链表

因此我们引入一个新的结构体node_t,使之能够形成一个新的链接形式,如下所示,在下面这个架构中,我们不管结构体是啥样的,只要是成员中包含node_t node的节点即可进行链接起来。
在这里插入图片描述

struct node_t{
	struct node_t *next;
};

struct person{
	char *name;
	int age;
	struct node_t node;
};//定义一个结构体成员

struct list{
	char *name;
	struct node_t head;
};//定义一个链表

通用链表的三种实现方式

方法一

把node_t结构体放在person结构体的最前面,使用时就可以直接获取到节点的位置,这种方案一般不使用。
在这里插入图片描述
在这里插入图片描述使用此方案时,上述代码中的接引用可直接进行更改为下面这个简单形式。

p = (struct person *)((char *)node - (unsigned int)&((struct person *)0)->node);
更改为
p = (struct person *)node;

方法二

方法二就是上述完整代码所用到的方法,通过node_t反推出person结构体的首地址。常用于linux操作系统中。
在这里插入图片描述该方法我们通过用如下代码来代替上面的解引用代码,其中&((struct person *)0)->node)是为了得到基地址到node所占用的空间大小,(unsigned int)将其转化为整形的字节大小,(char *)node是为了将node变为字节类型的指针地址大小,减去后面的值即可得到对应的结构体基地址的地址大小,但是此时是字节类型的,因此最前面加上(struct person *)将其转化为结构体类型

#define container_of(ptr,type,member)  \
		(type *)((char *)ptr - (unsigned int)&((type *)0)->member)

p = (struct person *)((char *)node - (unsigned int)&((struct person *)0)->node);
更改为
p = container_of(node,struct person,node);

方法三

将node_t结构体加一个成员变量,直接指向对应节点的首地址,常用于单片机实时操作系统中。
在这里插入图片描述
方法三的使用方法如下
在这里插入图片描述

p = (struct person *)((char *)node - (unsigned int)&((struct person *)0)->node);
更改为
p = node->container;

参考链接

此文章参考视频完成,具体链接为韦东山freeRTOS系列教程:FreeRTOS的内部机制

通用链表代码展示

#include "stdio.h"

struct node_t{
	struct node_t *next;
};

struct person{
	char *name;
	int age;
	struct node_t node;
};//定义一个结构体成员

struct list{
	char *name;
	struct node_t head;
};//定义一个链表

void InitList(struct list * plist,char *name)
{
	plist->name = name;
	plist->head.next = NULL;
}//初始化链表

void AddItemToList(struct list *plist, struct node_t *new_node)
{
	struct node_t *last = &plist->head;
	while (last->next){
		last = last->next;
	}
	last->next = new_node;
	new_node->next = NULL;
}//增加一个结构体成员,只能加在链表的最后面

void AddItemAfter(struct node_t *pre,struct node_t *new_node)
{
	new_node->next = pre->next;
	pre->next = new_node;
}//加入结构体成员,加在中间的某个位置,前提需要已知前一个节点



void DelItemFromList(struct list *plist, struct node_t *node)
{
	struct node_t *per = &plist->head;
	while (per != NULL && per->next != node){
		per = per->next;
	}
	if(per == NULL)
		return;
	if(per->next == node)
		per->next = node->next;
}//删除链表中的某个结构体成员,需要传入结构体成员地址

int CmpPersonAge(struct node_t *pre,struct node_t *next)
{
	struct person *p;
	struct person *n;

	p = (struct person *)((char *)pre - (unsigned int)&((struct person *)0)->node);
	n = (struct person *)((char *)next - (unsigned int)&((struct person *)0)->node);
	if(p->age < n->age)
		return -1;
	else
		return 0;
}


void SortList(struct list *plist)
{
	struct node_t *pre1 = &plist->head;
	struct node_t *pre2;
	struct node_t *cur = plist->head.next;
	struct node_t *next;
	struct node_t *temp;
	while (cur){
		pre2 = cur;
		next = cur->next;
		while(next){
			if(CmpPersonAge(cur,next) == 0){
				//删除节点cur
				DelItemFromList(plist,cur);
				//删除节点next
				DelItemFromList(plist,next);
				//在pre1后面插入next
				AddItemAfter(pre1,next);
				//在pre2后面插入cur
				if(pre2 == cur) //如果出现连续两个节点交换的情况
					AddItemAfter(next,cur);
				else
					AddItemAfter(pre2,cur);
				//指针互换
				temp = cur;
				cur = next;
				next = temp;
			}
			pre2 = next;
			next = next->next;
		}
		pre1 = cur;
		cur = cur->next;
	}
}//对链表进行排序(快速排序法)

void PrintList(struct list *plist)
{
	int i = 0;
	struct node_t *node = plist->head.next;
	struct person *p;
	while (node != NULL)
	{
		p = (struct person *)((char *)node - (unsigned int)&((struct person *)0)->node);
		printf("person %d : %s is %d %x\r\n",i++,p->name,p->age,p->node.next);
		node = node->next;
	}
}//对链表进行打印


int main()
{
    printf("List_test!!!\r\n");

	struct list list_A;//定义链表
	struct person p[]={
		{"p0",23,{NULL}},
		{"p1",12,{NULL}},
		{"p2",43,{NULL}},
		{"p3",54,{NULL}},
		{"p4",3,{NULL}},
		{"p5",76,{NULL}},
		{"p6",32,{NULL}},
		{"p7",87,{NULL}},
		{"p8",45,{NULL}},
		{"p9",76,{NULL}},
		{NULL,0,{NULL}}
	};//定义结构体成员信息

	InitList(&list_A,"class_A");//初始化链表
	int i = 0;
	while(p[i].name != NULL){
		AddItemToList(&list_A,&p[i].node);
		i++;
	}//将结构体成员加载到链表中
	PrintList(&list_A);
	printf("\r\n");

	DelItemFromList(&list_A,&p[3].node);//删除链表成员4
	PrintList(&list_A);
	printf("\r\n");

	SortList(&list_A);//链表排序
	PrintList(&list_A);
	printf("\r\n");
	
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值