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