C语言实现通用链表初步(一)

注意:本文讨论的是无头单向非循环链表。

假设不采用Linux内核链表的思路,怎样用C语言实现通用链表呢?

一种常用的做法是:

typedef int element_t;

struct node_info

{

element_t data;

struct node_info *next;

};


其实这样的链表算不上通用,因为无法同时使用不同的数据类型。参考网友的思路,以上可以改为下面的定义:

//无头非循环单向链表
struct node_info {
	void *data;
	struct node_info *next;
};

先看看头文件:

#pragma once


//无头非循环单向链表
struct node_info {
	void *data;
	struct node_info *next;
};



struct student
{
	char name[20];
	unsigned char age;

};//for test

struct  slist_info {
	struct node_info *first; //指向第一个节点
	
	void (*insert_head)(void *one_data, 
			struct slist_info *info);//头插
	
	int (*del)(struct node_info *node, 
			struct slist_info *info);//删除节点

	struct node_info* (*find)(struct slist_info *info,
					int(*compare)(void *dest,void *key),void *key);
	//这里的compare是回调函数,由用户提供

	void (*for_each)(const struct slist_info *info,
			void (*todo)(void *one_data));//遍历,todo是回调函数

	void (*for_each_safe)(const struct slist_info *info,
			void (*todo)(void *one_data));//安全遍历(觉得在这里这个方法意义不大)

	void (*invert)(struct slist_info *info);//反转链表

	int (*is_dead_loop)(struct slist_info *info);//判断是否有死环,是返回1,不是返回0
};

#define   slist_is_empty(info)  ((info)->first == NULL) //链表是否为空

//构造和析构
void  slist_init(struct  slist_info *info);
void  slist_destroy(struct slist_info *info);





与这个链表相关的操作如下。

1.插入元素(头插法)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "slist.h"

/*无头非循环单链表*/
static void slist_insert_head(void *one_data, 
                        struct slist_info *info)
{//头插法
	assert(one_data != NULL && info != NULL);
	
	
	struct node_info *node = 
		(struct node_info *)malloc(sizeof(struct node_info));//分配内存
	assert(node!=NULL);
	
	node->data = one_data;//注意,这里只是简单地把指针指向用户的数据,并没有把用户数据复制过来
	
	if (slist_is_empty(info)) {//1.空链表
		info->first = node;
		node->next = NULL;
	}else { //2. 非空
		node->next = info->first;
		info->first = node;
	}
}

2.遍历

static void slist_for_each(const struct slist_info *info,
       							void (*todo)(void *one_data))
{
	assert(info != NULL && todo != NULL);
	struct node_info *cur = NULL;

	for (cur = info->first; cur != NULL; cur = cur->next) {
		todo(cur->data);
	}
}

static void slist_for_each_safe(const struct slist_info *info,
       							void (*todo)(void *one_data))
{
	assert(info != NULL && todo != NULL);
	struct node_info *cur = NULL;
	struct node_info *Next = NULL;
	for (cur = info->first; cur != NULL; cur = Next) {
		Next = cur->next;//Next保存了下一个节点,如果这个节点被删除了,那么下一个节点还可以找到
		todo(cur->data);
	}
}

3.反转(面试的时候,有可能会问到,直接背过好了!)

static void  slist_invert(struct slist_info *info)
{
	assert(info != NULL);

	struct node_info *Cur = NULL;
	struct node_info *Prev = NULL;
	struct node_info *Next = NULL;
	//1.移动Next 2.反向 3.移动Prev 4.移动Cur
	for (Cur = info->first; Cur  != NULL; Cur = Next) {
		Next = Cur->next;
		Cur->next = Prev;
		Prev = Cur;
	}
	//修改头指针,指向首节点
	info->first = Prev;	
}

4.查找


struct node_info *slist_find(struct slist_info *info,int(*compare)(void *dest,void *key),void *key)
{
	assert(info != NULL && compare != NULL);
	struct node_info *cur = NULL;

	for (cur = info->first; cur != NULL; cur = cur->next) {
		if(compare(cur->data,key)==1)//回调函数,把链表中的每一个数据和用户传入的关键字做比较,符合条件返回1
			return cur;//返回节点的地址给用户
	}
	return NULL;//没有找到返回空指针
}

注意,这里的第三个参数(关键字)也是用户传进来的。


5.构造和析构

void slist_init(struct slist_info *info)
{
	//空链表, 第一个节点为NULL
	info->first = NULL;

	info->insert_head = slist_insert_head;
	info->del = slist_del;
	info->find = slist_find;
	info->invert = slist_invert;
	info->is_dead_loop = slist_is_dead_loop;
	info->for_each = slist_for_each;
	info->for_each_safe = slist_for_each_safe;
}

void slist_destroy(struct slist_info *info)
{
	if(!slist_is_empty(info))
	{
		struct node_info *cur = NULL;
		struct node_info *Next = NULL;
		for (cur = info->first; cur != NULL; cur = Next) 
		{
			Next = cur->next;//Next保存了下一个节点,如果这个节点被删除了,那么下一个节点还可以找到
			free(cur);//释放每个节点占用的内存
		}
	}
	
}

细心的读者会发现,好像有的函数没有实现啊。下一篇博文我们再讨论!


posted @ 2014-04-30 15:12  漂泊的指针  阅读(618)  评论(0编辑  收藏  举报