带头结点的双向循环链表

目录

一、图例

1.基本图例

​2.插入结点

3.删除结点

 二、根据用户需求对链表进行操作

1. llist.h文件中宏定义

2. llist.c文件中创建头节点函数

3. llist.c插入结点函数

4.llist.c查找结点函数

5.llist中删除结点函数

6.llist中删除结点并进行回显

7.llist中遍历函数

8.llist中销毁结点函数

9.main函数


一、图例

1.基本图例

2.插入结点

3.删除结点

 二、根据用户需求对链表进行操作

1. llist.h文件中宏定义

#ifndef __LLIST_H__
#define __LLIST_H__

#define LLIST_FORWARD		1
#define LLIST_BACKWARD		2

typedef void llist_op(void *);
typedef int llist_cmp(const void *, const void *);

struct llist_node_st    //初始化结构体   常规结点类型
{
	struct llist_node_st *prev,*next;
	char data[1];
};

typedef struct         //头节点类型
{
	int size;
	struct llist_node_st head;
}LLIST;


LLIST* llist_create(int size);

int llist_insert(LLIST *, const void *data,int mode);

int llist_delete(LLIST*, const void *key, llist_cmp *cmp);

int llist_fetch(LLIST*, const void *key, llist_cmp *cmp,void *data);

void *llist_find(LLIST *,const void *key,llist_cmp *cmp);

void llist_travel(LLIST *, llist_op *op);

void llist_destroy(LLIST *);

#endif

2. llist.c文件中创建头节点函数

LLIST* llist_create(int size)
{
	LLIST *new;	
	new = malloc(sizeof(*new));
	if(new == NULL)
		return NULL;
	new->size = size;    //头节点中size大小根据用户需求设定
	new->head.prev = new->head.next = &new->head;    //让头结点的前驱和后继都指向自己
	return new;
}

3. llist.c插入结点函数

int llist_insert(LLIST *ptr, const void *data,int mode)
{
	struct llist_node_st *newnode;

	newnode = malloc(sizeof(*newnode) + ptr->size);
	if(newnode == NULL)
		return -1;
	
	memcpy(newnode->data, data, ptr->size);    //将传入的data赋值到新结点中的data中

	if(mode == LLIST_FORWARD)    //头部插入
	{
		newnode->prev = &ptr->head;    //新结点的前驱是头结点中的head
		newnode->next = ptr->head.next;    //新结点的后继是头结点 head原来指向的后继结点
	}
	else
	{
		if(mode == LLIST_BACKWARD)    //尾部插入
		{
			newnode->prev = ptr->head.prev;   //新结点的前驱是头结点 head原来指向的前驱结点
			newnode->next = &ptr->head;    //新结点的后继是头结点中的head
		}
		else
			return -3;
	}
	newnode->prev->next = newnode;    //新结点前驱的后继是新结点
	newnode->next->prev = newnode;    //新结点后继的前驱是新结点

	return 0;
}

4.llist.c查找结点函数

struct llist_node_st *find_(LLIST *ptr,const void *key,llist_cmp *cmp)
{
	struct llist_node_st *cur;

	for(cur = ptr->head.next; cur != &ptr->head ; cur = cur->next)
	{
		if(cmp(key, cur->data) == 0)    //调用比较函数,在链表中进行查找
			break;
	}

	return cur;    将找到的结点返回
}

void *llist_find(LLIST *ptr,const void *key,llist_cmp *cmp)
{
	struct llist_node_st *node;

	node = find_(ptr,key,cmp);    //找到结点
	if(node == &ptr->head)    //判断不是头结点
		return NULL;
	else
		return node->data;    //返回找到结点中的数据
}

5.llist中删除结点函数

int llist_delete(LLIST *ptr, const void *key, llist_cmp *cmp)
{
	struct llist_node_st *node;

	node = find_(ptr,key,cmp);    //找到要删除的结点
	if(node == &ptr->head)
		return -1;	
	node->prev->next = node->next;    //将要删除结点前驱的后继指向要删除结点的后继
	node->next->prev = node->prev;    //将要删除结点后继的前驱指向要删除结点的前驱
	free(node);    //将要删除的结点释放
	return 0;
}

6.llist中删除结点并进行回显

int llist_fetch(LLIST* ptr, const void *key, llist_cmp *cmp,void *data)
{
	struct llist_node_st *node;

	node = find_(ptr,key,cmp);     //找到要删除的结点
	if(node == &ptr->head)
		return -1;

	node->prev->next = node->next;    //将要删除结点前驱的后继指向要删除结点的后继
	node->next->prev = node->prev;    //将要删除结点后继的前驱指向要删除结点的前驱
	memcpy(data, node->data, ptr->size);    //将要删除结点中的data赋值给data
	free(node);    //释放要被删除的结点
	return 0;
}

7.llist中遍历函数

void llist_travel(LLIST *ptr, llist_op *op)
{
	struct llist_node_st *cur;

	for(cur = ptr->head.next; cur != &ptr->head ; cur = cur->next)
		op(cur->data);    //调用输出函数,输出结点中data的数据
}

8.llist中销毁结点函数

void llist_destroy(LLIST *ptr)
{
	struct llist_node_st *cur,*next;

	for(cur = ptr->head.next; cur != &ptr->head ; cur = next)
	{
		next = cur->next;
		free(cur);
	}    //循环销毁普通结点
	free(ptr);    //销毁头结点
}

9.main函数

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

#define NAMESIZE	32

struct score_st
{
	int id;
	char name[NAMESIZE];
	int math;
};    //用户想要在链表中操作的数据

void print_s(void *data)    //定义输出函数
{
	struct score_st *d = data;    

	printf("%d %s %d\n",d->id, d->name, d->math);//将传回来的结构体类型的参数进行输出
}

int id_cmp(const void *key, const void *data)    //比较id
{
	const int *k = key;
	const struct score_st *d = data;    
	return *k - d->id;    //将用户输入的id和链表中data中的id进行比较,0表示成功找到
}

int name_cmp(const void *key, const void *data)    比较name
{
	const char *k = key;            
	const struct score_st *d = data;
	return strcmp(k, d->name);    //将用户输入的name和链表中data中的name进行比较,0表示成功找到
}

int main()
{
	LLIST *handler;
	struct score_st tmp;
	int i;

	handler = llist_create(sizeof(struct score_st));
	if(handler == NULL)
	{
		printf("llist_create() failed.\n");
		exit(1);
	}


	for(i = 0 ; i < 7 ; i++)
	{
		tmp.id = i;
		tmp.math = 100-i;
		snprintf(tmp.name, NAMESIZE,"STU%d",i);

		llist_insert(handler, &tmp, LLIST_BACKWARD);    //调用插入结点函数,在尾部插入
	}

	llist_travel(handler,print_s);    //调用遍历函数,对链表中的data进行输出
	printf("\n\n");

	int findid = 2;
	char *findname = "STU4";
	struct score_st *retp;

/*
	//retp = llist_find(handler,&findid,id_cmp);    //通过id找
	retp = llist_find(handler,findname,name_cmp);    //通过name找
	if(retp)
		print_s(retp);
	else
		printf("Can not find.\n");
*/


//	llist_delete(handler,&findid,id_cmp);    //通过id找删除结点
	if(llist_fetch(handler,&findid,id_cmp,&tmp) == 0)    
		print_s(&tmp);    //通过id找删除结点并回显删除结点中的数据
	printf("\n\n");	
	

	llist_travel(handler,print_s);    
	llist_destroy(handler);    //销毁结点
	exit(0);
}



注:本文是通过听李慧芹老师上课记的笔记,如有理解不到位请多多包涵,也请多多指教

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RLC214

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值