循环双链表的简单实现(C语言)
小结
双链表就像一群人手拉手站成一排,最旁边的两个人都有一个手空着。循环双链表就是一群人手拉手站成一圈,第一个人的手和最后一个人的手互相牵着。
双链表与循环双链表的插入删除等操作基本一致,表尾判断有所不同。双链表判断表尾为后继指向NULL,循环双链表判断表尾则看其后继是否指向头指针。
插入
结点后插
// 后插结点
bool InsertNextNode(DLNode *p, DLNode *s){
if(p == NULL && s == NULL) return true; // 数据非法性判断
p->next->prior = s; // p原后继结点的前继指向s
s->next = p->next; // s后继结点设为p的原后继结点
p->next = s; // p的后继结点设为s
s->prior = p; // s的前继结点设为p
return true;
}
结点前插
// 前插结点
bool InsertPriorNode(DLNode *p, DLNode *s){ // 在p结点前插入s结点
if(p == NULL && s == NULL) return true; // 数据非法性判断
s->prior = p->prior; // s的前继结点设为原p的前继结点
p->prior->next = s; // p的前继结点的后继指向s
s->next = p; // s的后继结点设为p
p->prior = s; // p的前继结点设为s
return true;
}
尾插法
// 尾插法
DLinkList List_TailInsert(DLinkList L){
ElemType e;
DLNode *p = L; // 声明一个结点,用于定位
scanf("%d", &e);
while(e != 9999){
while(p->next != L){ // 定位至表尾
p = p->next;
}
DLNode *s = (DLNode*)malloc(sizeof(DLNode)); // 声明一个新结点用于插入
s->data = e; // 存入数据
s->next = NULL;
s->prior = NULL;
InsertNextNode(p, s); // 调用后插结点函数实现插入
scanf("%d", &e);
}
return L;
}
头插法
// 头插法
DLinkList List_HeadInsert(DLinkList L){
ElemType e;
DLNode *p = L; // 声明一个结点,用于定位
scanf("%d", &e);
while(e != 9999){
if(p->next == L){ // 第一个结点特殊处理
DLNode *s = (DLNode*)malloc(sizeof(DLNode)); // 声明一个新结点用于插入
s->data = e; // 存入数据
L->next = s;
L->prior = s;
s->next = L;
s->prior = L;
}else{
DLNode *s = (DLNode*)malloc(sizeof(DLNode)); // 声明一个新结点用于插入
s->data = e; // 存入数据
s->next = NULL;
s->prior = NULL;
InsertPriorNode(L->next, s); // 调用前插结点函数,在第一个结点进行前插
}
scanf("%d", &e);
}
return L;
}
按位插入
// 按位插入
bool ListInsert(DLinkList L, int pos, ElemType e){
if(L == NULL || pos < 1) return false;
DLNode *p = L;
DLNode *s = (DLNode*)malloc(sizeof(DLNode)); // 创建一个新结点用于插入
s->data = e;
s->next = NULL;
s->prior = NULL;
while(--pos){ // 定位到插入位序的前一个结点
p = p->next;
}
if(InsertNextNode(p, s)){ //调用结点后插函数进行后插操作
return true;
}else false;
}
删除
结点后删
// 后删结点
bool DeleteNextNode(DLNode *p){
if(p == NULL) return true; // 数据合法性判断
DLNode *q = p->next; // q为将删除的结点
q->next->prior = p; // q的后继结点前继指向p
p->next = q->next; // p的后继改为q的后继
free(q); // 释放q
return true;
}
按位删除
// 按位删除
bool ListDelete(DLinkList L, int pos, ElemType *e){
if(L == NULL || pos < 1) return false;
DLNode *p = L;
while(--pos){ // 定位到删除结点的前继结点
p = p->next;
}
*e = p->next->data; // 通过指针把删除结点的数据域传回主函数
if(DeleteNextNode(p)){ // 调用结点后删操作进行删除
return true;
}else return false;
}
查找
按位查找
// 按位查找
ElemType GetElem(DLinkList L, int pos){
DLNode *p = L; // 用于定位
while(pos){ // pos值每-1移动p一次直到知道位置
p = p->next;
pos--;
}
return p->data; // 返回查找到的值
}
按值查找
// 按值查找
int LocateElem(DLinkList L, ElemType e){
int pos = 0;
DLNode *p = L;
while(e != p->data){ // 数据域 ==e 时停止
p = p->next;
pos++;
}
return pos; // 返回查找到的位置
}
其他
定义
typedef int ElemType;
typedef struct DLNode{
ElemType data;
struct DLNode *prior,*next; // prior存储前继结点的指针,next存储后继结点的指针
}DLNode, *DLinkList;
初始化
// 初始化
bool InitList(DLinkList *L){
// 创建空表
(*L) = (DLNode*)malloc(sizeof(DLNode));
if(L == NULL) return false; // 分配失败
(*L)->next = (*L);
(*L)->prior = (*L); // 前后结点都指向自己
return true;
}
求表长
// 求表长
int Length(DLinkList L){
int len = 0;
DLNode *p = L; // 用于定位
while(p->next != L){ // 遍历链表,到最后一个结点停止
p = p->next;
len++;
}
return len;
}
表判空
双链表的判空为头结点的后继是否为NULL,而循环双链表的判空是头结点是否指向自己。
// 判空
bool Empty(DLinkList L){
if(L->next == L){ // 头结点指向自己则为空
return true;
}else return false;
}
销毁表
// 销毁表
bool DestoryList(DLinkList *L){
while((*L)->next != *L){
DeleteNextNode(*L);
}
free(*L); // 释放头结点
(*L) = NULL; // 调用free()后L仍然会指向相同的内存空间,但是此时该内存已无效,不能被使用.故建议将L的值设为NULL
return true;
}
输出表
// 输出表
void PrintList(DLinkList L){
DLNode *p = L;
while(p->next != L){ // 遍历链表逐个输出
p = p->next;
printf("%d ", p->data);
}
printf("\n");
}
完整代码
// 循环双链表
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int ElemType;
typedef struct DLNode{
ElemType data;
struct DLNode *prior,*next; // prior存储前继结点的指针,next存储后继结点的指针
}DLNode, *DLinkList;
// 初始化
bool InitList(DLinkList *L){
// 创建空表
(*L) = (DLNode*)malloc(sizeof(DLNode));
if(L == NULL) return false; // 分配失败
(*L)->next = (*L);
(*L)->prior = (*L); // 前后结点都指向自己
return true;
}
// 后插结点
bool InsertNextNode(DLNode *p, DLNode *s){
if(p == NULL && s == NULL) return true; // 数据非法性判断
p->next->prior = s; // p原后继结点的前继指向s
s->next = p->next; // s后继结点设为p的原后继结点
p->next = s; // p的后继结点设为s
s->prior = p; // s的前继结点设为p
return true;
}
// 前插结点
bool InsertPriorNode(DLNode *p, DLNode *s){ // 在p结点前插入s结点
if(p == NULL && s == NULL) return true; // 数据非法性判断
s->prior = p->prior; // s的前继结点设为原p的前继结点
p->prior->next = s; // p的前继结点的后继指向s
s->next = p; // s的后继结点设为p
p->prior = s; // p的前继结点设为s
return true;
}
// 尾插法
DLinkList List_TailInsert(DLinkList L){
ElemType e;
DLNode *p = L; // 声明一个结点,用于定位
scanf("%d", &e);
while(e != 9999){
while(p->next != L){ // 定位至表尾
p = p->next;
}
DLNode *s = (DLNode*)malloc(sizeof(DLNode)); // 声明一个新结点用于插入
s->data = e; // 存入数据
s->next = NULL;
s->prior = NULL;
InsertNextNode(p, s); // 调用后插结点函数实现插入
scanf("%d", &e);
}
return L;
}
// 头插法
DLinkList List_HeadInsert(DLinkList L){
ElemType e;
DLNode *p = L; // 声明一个结点,用于定位
scanf("%d", &e);
while(e != 9999){
if(p->next == L){ // 第一个结点特殊处理
DLNode *s = (DLNode*)malloc(sizeof(DLNode)); // 声明一个新结点用于插入
s->data = e; // 存入数据
L->next = s;
L->prior = s;
s->next = L;
s->prior = L;
}else{
DLNode *s = (DLNode*)malloc(sizeof(DLNode)); // 声明一个新结点用于插入
s->data = e; // 存入数据
s->next = NULL;
s->prior = NULL;
InsertPriorNode(L->next, s); // 调用前插结点函数,在第一个结点进行前插
}
scanf("%d", &e);
}
return L;
}
// 按位插入
bool ListInsert(DLinkList L, int pos, ElemType e){
if(L == NULL || pos < 1) return false;
DLNode *p = L;
DLNode *s = (DLNode*)malloc(sizeof(DLNode)); // 创建一个新结点用于插入
s->data = e;
s->next = NULL;
s->prior = NULL;
while(--pos){ // 定位到插入位序的前一个结点
p = p->next;
}
if(InsertNextNode(p, s)){ //调用结点后插函数进行后插操作
return true;
}else false;
}
// 后删结点
bool DeleteNextNode(DLNode *p){
if(p == NULL) return true; // 数据合法性判断
DLNode *q = p->next; // q为将删除的结点
q->next->prior = p; // q的后继结点前继指向p
p->next = q->next; // p的后继改为q的后继
free(q); // 释放q
return true;
}
// 按位删除
bool ListDelete(DLinkList L, int pos, ElemType *e){
if(L == NULL || pos < 1) return false;
DLNode *p = L;
while(--pos){ // 定位到删除结点的前继结点
p = p->next;
}
*e = p->next->data; // 通过指针把删除结点的数据域传回主函数
if(DeleteNextNode(p)){ // 调用结点后删操作进行删除
return true;
}else return false;
}
// 按位查找
ElemType GetElem(DLinkList L, int pos){
DLNode *p = L; // 用于定位
while(pos){ // pos值每-1移动p一次直到知道位置
p = p->next;
pos--;
}
return p->data; // 返回查找到的值
}
// 按值查找
int LocateElem(DLinkList L, ElemType e){
int pos = 0;
DLNode *p = L;
while(e != p->data){ // 数据域 ==e 时停止
p = p->next;
pos++;
}
return pos; // 返回查找到的位置
}
// 销毁表
bool DestoryList(DLinkList *L){
while((*L)->next != *L){
DeleteNextNode(*L);
}
free(*L); // 释放头结点
(*L) = NULL; // 调用free()后L仍然会指向相同的内存空间,但是此时该内存已无效,不能被使用.故建议将L的值设为NULL
return true;
}
// 判空
bool Empty(DLinkList L){
if(L->next == L){ // 头结点指向自己则为空
return true;
}else return false;
}
// 求表长
int Length(DLinkList L){
int len = 0;
DLNode *p = L; // 用于定位
while(p->next != L){ // 遍历链表,到最后一个结点停止
p = p->next;
len++;
}
return len;
}
// 输出表
void PrintList(DLinkList L){
DLNode *p = L;
while(p->next != L){ // 遍历链表逐个输出
p = p->next;
printf("%d ", p->data);
}
printf("\n");
}
int main(){
DLinkList L; // 声明一个双链表
ElemType e; // 元素
int status; // 操作码
int pos; // 位序
int len; // 表长
InitList(&L); // 链表的初始化
while(1){
printf("Please enter a status code:\n1.List_TailInsert 2.List_HeadInsert 3.GetElem\n");
printf("4.LocateElem 5.ListDelete 6.ListInsert\n7.Length 8.Empty 9.PrintList\n");
printf("10.DestoryList 11.InitList 0.Exit\n");
scanf("%d", &status);
if(status == 0) break;
switch (status){
case 1: // 尾插法正序创建双链表
printf("Please enter the DlinkList elements one by one!(end with 9999)\n");
L = List_TailInsert(L);
PrintList(L);
break;
case 2: // 头插法逆序创建双链表
printf("Please enter the DlinkList elements one by one!(end with 9999)\n");
L = List_HeadInsert(L);
PrintList(L);
break;
case 3: // 按位查找
printf("Please enter the position of the node you want to find!\n");
scanf("%d", &pos);
e = GetElem(L, pos);
printf("The value in the %d position is %d\n", pos, e);
break;
case 4: // 按值查找
printf("Please enter the element value you want to find!\n");
scanf("%d", &e);
pos = LocateElem(L, e);
printf("The address of the linked list where the element with the value %d is located is %d\n", e, pos);
break;
case 5: // 按位删除
printf("Please enter the position of the node you want to delete!\n");
scanf("%d", &pos);
if(ListDelete(L, pos, &e)){
printf("Element value %d deleted successfully\n", e);
PrintList(L);
}else{
printf("Failed to delete!\n");
}
break;
case 6: // 按位插入
printf("Please enter the position and element value you want to insert\n");
scanf("%d%d", &pos, &e);
if(ListInsert(L, pos, e)){
printf("Inserted successfully\n");
PrintList(L);
}else{
printf("Failed to insert!\n");
}
break;
case 7: // 求表长
len = Length(L);
printf("The length of the DLinkList is %d\n", len);
break;
case 8: // 判空
if(L == NULL){
printf("DList not exist!\n");
}
else if(Empty(L)){
printf("DLinkList is empty!\n");
}else{
printf("DLinkList not empty!\n");
PrintList(L);
}
break;
case 9: // 输出表
PrintList(L);
break;
case 10: // 销毁表
if(DestoryList(&L)){
printf("DList destroyed successfully\n");
}else
printf("Failed to destroy\n");
break;
case 11: // 初始化
if(InitList(&L)){
printf("DList initialized successfully\n");
}else
printf("Failed to initialize!\n");
break;
default:
printf("Error!\n");
break;
}
}
return 0;
}