循环单链表的简单实现(C语言)
简述
循环单链表和单链表的不同之处在于——单链表的表尾指向空,而循环单链表的表尾指向头部第一个结点。因此,他们的边界处理会有些不同。循环单链表的好处在于——给出任一结点都能找到链表中的所有结点。而单链表只能找到该结点后面的结点,对于该节点前面的结点单链表是完全未知的。
插入
前插结点
// 前插结点
bool InsertpriorNode(CLNode *p, ElemType e){
if(p == NULL) return false; // 数据合法性判断
CLNode *s = (CLNode*)malloc(sizeof(CLNode)); // p为当前结点,s为欲插结点
if(s == NULL) return false; // 内存不足,分配失败
// 偷梁换柱法 将p存入s,再令p存入新数据.本质上还是后插
s->data = p->data; // p数据域存入s
s->next = p->next; // s下一结点指向p原后继结点
p->next = s; // p下一结点指向s
p->data = e; // p存入插入的数据
return true;
}
后插结点
// 后插结点
bool InsertNextNode(CLNode *p, ElemType e){
if(p == NULL) return false; // 数据合法性判断
CLNode *s = (CLNode*)malloc(sizeof(CLNode));
if((s) == NULL) return false; // 内存不足,分配失败
s->data = e; // 存入数据域
s->next = p->next; // s联结p原后继
p->next = s; // p联结s
return true;
}
头插法
// 头插法
CLinkList List_HeadInsert(CLinkList L){
ElemType e;
scanf("%d", &e);
while(e != 9999){
// 第一个结点要特殊处理
if(L->next == L){ // 链表为空则创建一个新结点
CLNode *p = (CLNode*)malloc(sizeof(CLNode));
L->next = p; // 令新结点成为头结点的后继结点
p->next = L;
p->data = e;
}else{
InsertpriorNode(L->next, e); // 进行结点后插操作
}
scanf("%d", &e);
}
return L;
}
尾插法
// 尾插法
CLinkList List_TailInsert(CLinkList L){
ElemType e;
scanf("%d", &e);
while(e != 9999){
CLNode *p = (CLNode*)malloc(sizeof(CLNode));
p = L;
while(p->next != L){ // 第一个结点进行特殊处理
if(L->next == L){ // 头结点为空则创建一个新结点
CLNode *p = (CLNode*)malloc(sizeof(CLNode));
L->next = p; // L的下一结点指向p
p->next = L; // p的下一结点指向L
p->data = e; // p存入数据
}
p = p->next; // 移到下一个结点
}
InsertNextNode(p, e); // 调用后插结点函数进行后插操作
scanf("%d", &e);
}
return L;
}
按位插入
// 按位插入
bool ListInsert(CLinkList *L, int pos, ElemType e){
CLNode *p = *L;
int i = 0;
for(i; i < pos-1; i++){ // 定位到欲插入位序的前继结点
p = p->next;
}
if(InsertNextNode(p, e)){ // 调用后插结点函数进行后插操作并判断操作是否成功
return true;
}else false;
}
删除
结点后删
// 结点后删
bool DeleteNode(CLNode *p){ // 删除p结点的后继结点
if(p == NULL) return false; // 数据非法性判断
CLNode *s = p->next; // s为p的后继结点
p->next = s->next; // p指向s的后继结点
free(s); // 释放s的空间
return true;
}
按位删除
// 按位删除
bool ListDelete(CLinkList *L, int pos, ElemType *e){
if(L == NULL || pos < 1) return false; // 数据合法性判断
CLNode *p = *L;
int i = 0;
for(i; i < pos-1; i++){ // 定位欲删除结点的前继结点
p = p->next;
}
*e = p->next->data;
if(DeleteNode(p)){ // 调用结点后删函数进行删除
return true;
}else return false;
}
销毁表
// 销毁表
bool DestoryList(CLinkList *L){
while((*L)->next != *L){ // 循环遍历链表,逐个删除结点
DeleteNode(*L); // 调用结点后删函数
}
free(*L); // 释放头结点的空间
*L = NULL;
return true;
}
查找
按位查找
// 按位查找
ElemType GetElem(CLinkList L, int pos){
CLNode *p = L;
int i = 0;
for(i; i < pos; i++){ // 定位到位序上的结点
p = p->next;
}
return p->data; // 返回结点数据域
}
按值查找
// 按值查找
int LocateElem(CLinkList L, ElemType e){
CLNode *p = L;
int pos = 0; // 记录位序
while(p->next != L && p->data != e){ // 定位到结点数据域 ==e的前继结点
p = p->next;
pos++;
}
return pos++; // 返回所在结点位序
}
其他
定义
typedef int ElemType;
typedef struct CLNode{
ElemType data;
struct CLNode *next;
}CLNode, *CLinkList;
初始化
// 初始化
bool InitList(CLinkList *L){
(*L) = (CLinkList)malloc(sizeof(CLNode));
if((*L) == NULL) return false; // 内存不足,分配失败
(*L)->next = (*L); // 初始化让头结点指向自己
return true;
}
求表长
// 求表长
int Length(CLinkList L){
CLNode *p = L;
int i = 0; // 头结点为0号位序
while(p->next != L){ // 遍历链表,用i来记录长度
p = p->next;
i++;
}
return i;
}
输出表
// 输出表
void PrintList(CLinkList L){
CLNode *p = L;
while(p->next != L){ // 遍历链表
p = p->next; // 还未到达表尾则进行结点读取后移
printf("%d ", p->data);
}
printf("\n");
}
表判空
// 判空
bool Empty(CLinkList L){
if(L->next == L) // 如果头结点指向自己,则表为空
return true;
else return false;
}
判断表尾
// 判断结点是否位于表尾
bool IsTail(CLinkList L, CLNode *p){
if(p->next == L){ // 后继结点为头结点则为表尾
return true;
}else
return false;
}
完整代码
// 循环单链表
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int ElemType;
typedef struct CLNode{
ElemType data;
struct CLNode *next;
}CLNode, *CLinkList;
// 初始化
bool InitList(CLinkList *L){
(*L) = (CLinkList)malloc(sizeof(CLNode));
if((*L) == NULL) return false; // 内存不足,分配失败
(*L)->next = (*L); // 初始化让头结点指向自己
return true;
}
// 后插结点
bool InsertNextNode(CLNode *p, ElemType e){
if(p == NULL) return false; // 数据合法性判断
CLNode *s = (CLNode*)malloc(sizeof(CLNode));
if((s) == NULL) return false; // 内存不足,分配失败
s->data = e; // 存入数据域
s->next = p->next; // s联结p原后继
p->next = s; // p联结s
return true;
}
// 前插结点
bool InsertpriorNode(CLNode *p, ElemType e){
if(p == NULL) return false; // 数据合法性判断
CLNode *s = (CLNode*)malloc(sizeof(CLNode)); // p为当前结点,s为欲插结点
if(s == NULL) return false; // 内存不足,分配失败
// 偷梁换柱法 将p存入s,再令p存入新数据.本质上还是后插
s->data = p->data; // p数据域存入s
s->next = p->next; // s下一结点指向p原后继结点
p->next = s; // p下一结点指向s
p->data = e; // p存入插入的数据
return true;
}
// 尾插法
CLinkList List_TailInsert(CLinkList L){
ElemType e;
scanf("%d", &e);
while(e != 9999){
CLNode *p = (CLNode*)malloc(sizeof(CLNode));
p = L;
while(p->next != L){ // 第一个结点进行特殊处理
if(L->next == L){ // 头结点为空则创建一个新结点
CLNode *p = (CLNode*)malloc(sizeof(CLNode));
L->next = p; // L的下一结点指向p
p->next = L; // p的下一结点指向L
p->data = e; // p存入数据
}
p = p->next; // 移到下一个结点
}
InsertNextNode(p, e); // 调用后插结点函数进行后插操作
scanf("%d", &e);
}
return L;
}
// 头插法
CLinkList List_HeadInsert(CLinkList L){
ElemType e;
scanf("%d", &e);
while(e != 9999){
// 第一个结点要特殊处理
if(L->next == L){ // 链表为空则创建一个新结点
CLNode *p = (CLNode*)malloc(sizeof(CLNode));
L->next = p; // 令新结点成为头结点的后继结点
p->next = L;
p->data = e;
}else{
InsertpriorNode(L->next, e); // 进行结点后插操作
}
scanf("%d", &e);
}
return L;
}
// 按位插入
bool ListInsert(CLinkList *L, int pos, ElemType e){
CLNode *p = *L;
int i = 0;
for(i; i < pos-1; i++){ // 定位到欲插入位序的前继结点
p = p->next;
}
if(InsertNextNode(p, e)){ // 调用后插结点函数进行后插操作并判断操作是否成功
return true;
}else false;
}
// 结点后删
bool DeleteNode(CLNode *p){ // 删除p结点的后继结点
if(p == NULL) return false; // 数据非法性判断
CLNode *s = p->next; // s为p的后继结点
p->next = s->next; // p指向s的后继结点
free(s); // 释放s的空间
return true;
}
// 按位删除
bool ListDelete(CLinkList *L, int pos, ElemType *e){
if(L == NULL || pos < 1) return false; // 数据合法性判断
CLNode *p = *L;
int i = 0;
for(i; i < pos-1; i++){ // 定位欲删除结点的前继结点
p = p->next;
}
*e = p->next->data;
if(DeleteNode(p)){ // 调用结点后删函数进行删除
return true;
}else return false;
}
// 按位查找
ElemType GetElem(CLinkList L, int pos){
CLNode *p = L;
int i = 0;
for(i; i < pos; i++){ // 定位到位序上的结点
p = p->next;
}
return p->data; // 返回结点数据域
}
// 按值查找
int LocateElem(CLinkList L, ElemType e){
CLNode *p = L;
int pos = 0; // 记录位序
while(p->next != L && p->data != e){ // 定位到结点数据域 ==e的前继结点
p = p->next;
pos++;
}
return pos++; // 返回所在结点位序
}
// 销毁表
bool DestoryList(CLinkList *L){
while((*L)->next != *L){ // 循环遍历链表,逐个删除结点
DeleteNode(*L); // 调用结点后删函数
}
free(*L); // 释放头结点的空间
*L = NULL;
return true;
}
// 输出表
void PrintList(CLinkList L){
CLNode *p = L;
while(p->next != L){ // 遍历链表
p = p->next; // 还未到达表尾则进行结点读取后移
printf("%d ", p->data);
}
printf("\n");
}
// 判空
bool Empty(CLinkList L){
if(L->next == L) // 如果头结点指向自己,则表为空
return true;
else return false;
}
// 求表长
int Length(CLinkList L){
CLNode *p = L;
int i = 0; // 头结点为0号位序
while(p->next != L){ // 遍历链表,用i来记录长度
p = p->next;
i++;
}
return i;
}
// 判断结点是否位于表尾
bool IsTail(CLinkList L, CLNode *p){
if(p->next == L){ // 后继结点为头结点则为表尾
return true;
}else
return false;
}
int main(){
CLinkList 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");
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;
}
运行展示