C语言 泛型链表的实现

Posted Dontla

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言 泛型链表的实现相关的知识,希望对你有一定的参考价值。

参考文章:使用C语言实现“泛型”链表

jiang工给的泛型链表例子,有点看不怎么懂,自己写个看看:

#include <stdio.h>
struct list_head {
    struct list_head *next, *prev;
};


#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

//根据structd的成员获取struct的地址
#define container_of(ptr, type, member) ((type *)(((char *)ptr) - (int)(&(((type*)0)->member))))


//链表遍历

#define list_for_each(pos, head) \\
    for (pos = (head)->next; pos != (head); pos = pos->next)

#define list_for_each_prev(pos, head) \\
    for (pos = (head)->prev; pos != (head); pos = pos->prev)





#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \\
    struct list_head name = LIST_HEAD_INIT(name)

static inline void __list_add(struct list_head *new,
                              struct list_head *prev,
                              struct list_head *next)
{
    next->prev = new;
    new->next = next;
    new->prev = prev;
    prev->next = new;
}


static inline void list_add(struct list_head *new, struct list_head *head)
{
    __list_add(new, head, head->next);
}

static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
    __list_add(new, head->prev, head);
}


static inline void __list_del(struct list_head * prev, struct list_head * next)
{
    next->prev = prev;
    prev->next = next;
}

static inline void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
}

typedef struct  _student {
    int data ;
    struct list_head mylist;
}student;


int main()
{
    LIST_HEAD(header_task);
    student s1 ={.data =1};
    student s2 ={.data =2};
    student s3 ={.data =3};

    struct list_head* pos;
    student* s;

    list_add(&s1.mylist,&header_task);
    list_add(&s2.mylist,&header_task);
    list_add(&s3.mylist,&header_task);


    list_for_each(pos, &header_task){
        s = container_of(pos, student, mylist);
        printf("data is %d\\n",s->data);
    }
    printf("----------\\n");
    list_del(&s2.mylist);

    list_for_each(pos, &header_task){
        s = container_of(pos, student, mylist);
        printf("data is %d\\n",s->data);
    }

    printf("Hello World!\\n");
    return 0;
}

实现方法

有一点懵逼,我的思路是,在结点中的数据域存放指向自定义数据结构的指针

另一种思路是在自定义数据结构中存放prev和next指针

思考一个问题,是先有数据,我们把数据串起来就,成了链表?

还是先有链表,我们再将数据一个个放进去?(好无聊的问题( ̄、 ̄))

那么我自己实现两个版本吧,其中一个版本是链表指针指向整个结点,结点中存放指向数据的指针;另一个版本是将数据作为结点,像jianggong写的那样,但是可以将mylist(包含prev和next指针)写在最上面,这样就不必用相对地址去计算了

方法1:链表指针指向整个结点,结点中存放指向数据的指针

#include <stdio.h>
#include <stdlib.h>
#include <String.h>


//定义结点
struct Node
{
	void* data;
	struct Node* prev;
	struct Node* next;
};

//尾插结点
void list_add_tail(Node* p, void* e) {
	struct Node* n = (Node*)malloc(sizeof(Node));
	if (NULL != n) {
		n->data = e;
		Node* tmp = p->prev;
		p->prev = n;
		n->prev = tmp;
		n->next = p;
		tmp->next = n;
	}
}

//删除结点
void list_del(Node* h, void* u) {
	while (NULL != h->next->data) {
		if (u == h->next->data) {
			Node* tmp = h->next;
			h->next = h->next->next;
			h->next->prev = h;
			if (NULL != tmp) {
				//if (NULL != tmp->data) {
					//free(tmp->data);
				free(tmp);
				printf("成功删除结点!\\n");
				return;
				//}
			}

		}
		else {
			h = h->next;
		}

	}
	printf("没有找到要删除的结点!\\n");
}

int main() {
	//创建链表
	Node header_task1 = { NULL, &header_task1, &header_task1 };

	//定义自定义结构体
	struct User
	{
		char name[20];
		char phone[20];
	};

	//创建自定义结构体u1(不是用malloc动态分配堆空间,不用free)
	User u1 = { "唐三藏", "18944443333" };
	User u2 = { "白骨精", "18922223333" };
	User u3 = { "猪八戒", "18911113333" };
	User u4 = { "牛魔王", "18900003333" };

	//尾插
	list_add_tail(&header_task1, &u1);
	list_add_tail(&header_task1, &u2);
	list_add_tail(&header_task1, &u3);
	list_add_tail(&header_task1, &u4);

	//遍历
	//条件也可以是:node != &header_task1
	for (Node* node = header_task1.next; node->data != NULL; node = node->next) {
		printf("姓名:%s\\t电话号码:%s\\n", ((User*)node->data)->name, ((User*)node->data)->phone);
	}

	//删除某个结点
	list_del(&header_task1, &u1);//成功

	list_del(&header_task1, &u1);//没有找到

	//遍历
	//条件也可以是:node != &header_task1
	for (Node* node = header_task1.next; node->data != NULL; node = node->next) {
		printf("姓名:%s\\t电话号码:%s\\n", ((User*)node->data)->name, ((User*)node->data)->phone);
	}

	return 0;
}

运行结果:

姓名:唐三藏    电话号码:18944443333
姓名:白骨精    电话号码:18922223333
姓名:猪八戒    电话号码:18911113333
姓名:牛魔王    电话号码:18900003333
成功删除结点!
没有找到要删除的结点!
姓名:白骨精    电话号码:18922223333
姓名:猪八戒    电话号码:18911113333
姓名:牛魔王    电话号码:18900003333

方法2:结构体数据作为结点,结点中存放指向prev、next的指针(好像这样的话结构体数据确实不能直接作为结点)

#include <stdio.h>
#include <stdlib.h>
#include <String.h>

struct list_head {
	struct list_head* next;
	struct list_head* prev;
};


//尾插结点
void list_add_tail(list_head* head, list_head* new_node) {
	/*list_head* tmp = head->prev;
	head->prev = new_node;
	new_node->prev = tmp;
	tmp->next = new_node;
	new_node->next = head;*/
	//根本不用temp啊!
	head->prev->next = new_node;
	new_node->prev = head->prev;
	head->prev = new_node;
	new_node->next = head;
}

//删除结点
void list_del(list_head* head, list_head* del) {
	list_head* i = head;
	for (i = i->next; i != head; i = i->next) {
		if (i == del) {
			i->prev->next = i->next;
			i->next->prev = i->prev;
			//删除结点也用不到tmp(只有单向链表增删才用到tmp吧!)
			printf("删除结点成功!\\n");
		}
	}
}

int main() {
	//创建链表
	list_head header_task1 = {&header_task1, &header_task1 };

	//定义自定义结构体
	struct User
	{
		char name[20];
		char phone[20];
		list_head my_list;
	};

	//创建自定义结构体u1(不是用malloc动态分配堆空间,不用free)
	User u1 = { "唐三藏", "18944443333", {NULL, NULL} };
	User u2 = { "白骨精", "18922223333", {NULL, NULL} };
	User u3 = { "猪八戒", "18911113333", {NULL, NULL} };
	User u4 = { "牛魔王", "18900003333", {NULL, NULL} };

	//尾插
	list_add_tail(&header_task1, &u1.my_list);
	list_add_tail(&header_task1, &u2.my_list);
	list_add_tail(&header_task1, &u3.my_list);
	list_add_tail(&header_task1, &u4.my_list);

	list_head* pos1;
	User* u;
	//遍历
	for (pos1 = header_task1.next; pos1 != &header_task1; pos1 = pos1->next) {
		u = (User*)((int)pos1 - (int)&(((User*)0)->my_list));
		printf("姓名:%s\\t电话:%s\\t\\n", u->name, u->phone);
	}

	//删除
	list_del(&header_task1, &u1.my_list);

	for (pos1 = header_task1.next; pos1 != &header_task1; pos1 = pos1->next) {
		u = (User*)((int)pos1 - (int)&(((User*)0)->my_list));
		printf("姓名:%s\\t电话:%s\\t\\n", u->name, u->phone);
	}
	return 0;
}
姓名:唐三藏    电话:18944443333
姓名:白骨精    电话:18922223333
姓名:猪八戒    电话:18911113333
姓名:牛魔王    电话:18900003333
删除结点成功!
姓名:白骨精    电话:18922223333
姓名:猪八戒    电话:18911113333
姓名:牛魔王    电话:18900003333

以上是关于C语言 泛型链表的实现的主要内容,如果未能解决你的问题,请参考以下文章

泛型链表结构

两个链表的第一个公共结点

C语言反转单向链表的代码

C语言实现链表的逆序打印

C语言链表

c语言,链表的反转,请写出代码,并讲解下,谢了!!!!!