数据结构中,双向循环链表的常用14种函数源码(带详细注释)

数据结构中双向循环链表的常用函数源码:

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

/*
双向循环链表(头结点不带数据):
双向循环链表包括数据域和指针域
其中指针域包含两个指针,分别指向前一个结点和后一个结点
*/

typedef int DATATYPE;         //为整型数据起别名为 DATATYPE
typedef struct STUDENT        //定义双向循环链表的结构体类型
{ 
	int number;           //定义双向链表的数据域 可以是其它或多种类型 也可以是结构体类型
	struct STUDENT *prev; //定义双向链表的指针域 指向前一个结点的指针
	struct STUDENT *next; //定义双向链表的指针域 指向后一个结点的指针

}Student, *Double_link;       //为双向链表的结构体和结构体指针起别名 为了后面写起来更加的简单


// 函数声明部分:
Double_link init_head();                                    // 1.初始化双向链表头结点
Double_link create_node(DATATYPE number);                   // 2.创建新结点
bool insert_any(Double_link new_node,Double_link a,Double_link b);// 3.0插入任意结点
bool insert_head(Double_link head, Double_link new_node);   // 3.头插法插入结点
bool insert_tail(Double_link head, Double_link new_node);   // 4.尾插法插入结点
bool insert_min_max(Double_link head, Double_link new_node);// 5.根据元素从小到大依次插入结点
bool  is_empty(Double_link head);                           // 6.判断链表是否为空
void display(Double_link head);                             // 7.遍历双向链表
Double_link found_node(Double_link head ,DATATYPE n);       // 8.根据元素查找某一个结点
bool update_node(Double_link head, DATATYPE old, DATATYPE new);// 9.根据元素修改结点值
bool deleate_node(Double_link head, DATATYPE number);       // 10.根据元素删除某一个结点
Double_link remove_node(Double_link head, DATATYPE number); // 11.根据元素取出某一个结点
bool move_node(Double_link head, DATATYPE a, DATATYPE b);   // 12.根据元素移动结点
void clean_link(Double_link head);                          // 13.清空链表
  

int main(int argc, char const *argv[])
{
	/*Double_link head = init_head();                   //初始化头结点
	// 函数测试 初始值为1-10的新结点作为双向循环链表
	for (int i = 0; i < 10; i++)
	{
		Double_link node = create_node(i+1);
		insert_tail(head, node);
	}
	display(head);
	insert_min_max(head,create_node(8));
	display(head);
	update_node(head,10,100);
	display(head);
	deleate_node(head,7);
	display(head);
	remove_node(head,9);
	display(head);
	move_node(head,6,8);
	display(head);
	printf("清空后\n");
	clean_link(head);*/
	printf("cai_Grass的博客,所有函数都经过测试,可运行,如若不对,请指正!!\n");
	return 0;
}



// 函数定义部分:
// 1.初始化双向链表头结点
Double_link init_head()
{
	Double_link head = malloc(sizeof(Student));//为双向链表的头结点申请空间
	if (head != NULL)                          //申请成功
	{
		head->next = head;
		head->prev = head;	           //头结点前一个结点和后一个结点指向自己
		return head;			   //返回头结点
	}
	else
	{
		return;				   //空间申请失败 结束
	}

}

// 2.创建新结点
Double_link create_node(DATATYPE number)
{
	Double_link new_node = malloc(sizeof(Student));//为新结点申请空间
	if(new_node != NULL)
	{
		new_node->number = number;
		new_node->prev = NULL;
		new_node->next = NULL;             //申请成功,初始化数据域和指针域
		return new_node;		   //返回新的结点
	}
	else
	{
		return;			           //申请失败,结束
	}
}

// 3.0双向链表的万能插入法
/*
将一个新结点插入任意两个结点之间
如果写了这个函数,我们后面的头插法,尾插法可以得到简化
当然,也不一定要有这个函数,没有一样可以完成头插法和尾插法
这里只是为了方便
*/
bool insert_any(Double_link new_node,Double_link a,Double_link b)
{
		if (new_node == NULL || a == NULL || b == NULL)
		{
			return false;
		}
		new_node->next = b;
		new_node->prev = a;
		a->next = new_node;
		b->prev = new_node;
		return true;		//这种插入方法显得十分简单,逻辑也非常的清晰
}


// 3.头插法插入结点
bool insert_head(Double_link head, Double_link new_node)
{
	if (head == NULL || new_node == NULL)
	{
		return false;
	}
	insert_any(new_node,head,head->next);//头插法即将新结点插入头结点和头结点后一个结点
	return true;                         //返回true 表示插入成功
}

// 4.尾插法插入结点
bool insert_tail(Double_link head, Double_link new_node)
{
	if (head == NULL || new_node == NULL)
	{
		return false;
	}
	insert_any(new_node,head->prev,head);
	return true;                         //尾插法即将新结点插入尾结点和头结点之间

}

/*
如果我们要按照结点元素从小到大的去插入
那么我们就要去遍历这个双向链表
符合条件才插入
*/
// 5.根据元素从小到大依次插入结点
bool insert_min_max(Double_link head, Double_link new_node)
{
	if (head == NULL || new_node == NULL)
	{
		return false;
	}
	Double_link p = head;		//遍历链表 用指针p指向头结点 依次往下遍历
	while(p->next != head)		//条件
	{
		p = p->next;
		if (new_node->number <= p->number)    //插入的条件
		{
		      insert_any(new_node,p->prev,p); //用万能插入法插入到p的前面一个结点 
		      return true;
		}
	}											                               //如果遍历到最后一个结点还不符合条件 就插入到最后结点
	insert_tail(head,new_node);
	return true;
}
// 6.判断链表是否为空
bool  is_empty(Double_link head)
{
	return head->next == head;     //为空的条件就是头结点的下一个结点是本身
}

// 7.遍历双向链表
void display(Double_link head)
{
	if (head == NULL)
	{
		return;
	}
	Double_link p = head;	             //定义临时指针p,指向头结点
	while(p->next != head)	             //遍历的条件
	{
		p = p->next;
		printf("%d\t", p->number);   //输出即可
	}
	printf("\n");

}

// 8.根据元素查找某一个结点
Double_link found_node(Double_link head ,DATATYPE n)
{
	if (head == NULL)
	{
		return NULL;
	}
	Double_link p = head;
	while(p->next != head)
	{	
		p = p->next;
		if (p->number == n)           //遍历链表,若符合条件,返回该节点即可
		{
			// printf("成功查到该结点\n");
			return p;
		}

	}
	// printf("不存在该结点\n");
	return NULL;
}
// 9.根据元素修改结点值
bool update_node(Double_link head, DATATYPE old, DATATYPE new)
{
	if (head == NULL)
	{
		return false;
	}
	Double_link old_node = found_node(head, old);//找到旧结点,更改即可
	old_node->number = new;
	return true;
}

// 10.根据元素删除某一个结点
bool deleate_node(Double_link head, DATATYPE number)
{
	if (head == NULL)
	{
		return false;
	}
	Double_link deleate = found_node(head, number);//先找到要删除的结点
	Double_link PREV = deleate->prev;	       //找到待删结点的前一个结点
	Double_link NEXT = deleate->next;	       //找到待删结点的后一个结点
	PREV->next = NEXT;	
	NEXT->prev = PREV;			       //建立前一个结点和后一个结点的联系
	free(deleate);				       //释放要删除的结点即可
	return true;
}

// 11.根据元素取出某一个结点
/*
取出结点和删除结点的操作相差不大 只是多了一个返回值,但不删除
*/
Double_link remove_node(Double_link head, DATATYPE number)
{
	if (head == NULL)
	{
		return NULL;
	}
	Double_link deleate = found_node(head, number);  
	Double_link PREV = deleate->prev;				 
	Double_link NEXT = deleate->next;				 
	PREV->next = NEXT;	
	NEXT->prev = PREV;								 
	// free(deleate);	
	deleate->next == NULL;
	deleate->prev == NULL;                           //最好是置为空,更加符合逻辑								 
	return deleate;
}

// 12.根据元素移动结点
/*
借助取出结点和插入结点的方法即可,十分简便
功能:将a结点放到b结点的后面
*/
bool move_node(Double_link head, DATATYPE a, DATATYPE b)
{
	if (head == NULL)
	{
		return false;
	}
	Double_link a1 = remove_node(head,a);           //先将a结点取出
	Double_link b1 = found_node(head,b);		//然后找到b结点
	insert_any(a1,b1,b1->next);			//将a结点插入到b结点后面
	return true;
}

// 13.清空链表
/*
先删除后面的所有
在删除头结点
*/
void clean_link(Double_link head)
{
	if (head == NULL)
	{
		return ;
	}
	Double_link p = head;
	while(p->next != head)            
	{
		p = p->next;
		deleate_node(head,p->number);           //遍历依次删除结点
	}
	free(head);				        //释放头结点
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值