数据结构中双向循环链表的常用函数源码:
#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); //释放头结点
}