C语言实现单向循环链表
循环链表简介
循环链表的结构和单链表结构一样,不过对于单链表,每个结点只存储了向后的指针,到了尾标志就停止了向后链的操作,这样知道某个结点却无法找到它的前驱结点。将单链表中的终端点的指针由空指针改为指向头结点,就使整个单链表形成一个环,这种头尾相接的单链表称为循环单链表,简称循环链表。
代码实践
data.h
/*
* @Author: xgh
* @Date: 2020-06-08 22:52:43
* @LastEditTime: 2020-06-13 21:44:44
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \VS-CODE-C\.vscode\cycleLinkedLists\data.h
*/
#ifndef __DATA_H__
#define __DATA_H__
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define SUCCESS 1 // 成功
#define FAILURE 0 // 失败
#define ERROER 0 // 错误
#define NOT_EMPTY 1 // 链表不为空
#define EMPTY 0 // 链表为空
typedef int Statue; // 为int取别名,用于区分表示执行状态的int
typedef int ElemType; // 数据域类型
/*
循环链表的结构和单链表结构一样,不过对于单链表,每个结点只存储了向后的指针,
到了尾标志就停止了向后链的操作,这样知道某个结点却无法找到它的前驱结点。
将单链表中的终端点的指针由空指针改为指向头结点,就使整个单链表形成一个环,
这种头尾相接的单链表称为循环单链表,简称循环链表。
*/
typedef struct ClinkNode
{
ElemType data; //数据域
// 这里不能写成Node *next
// 因为类型名的作用域是从大括号结尾开始,而在结构体内部不能使用,因为还没有定义
// 需要声明
struct ClinkNode *next; // 指针域
}clinkNode;
// 使用*CLinkLine而不直接使用ClinkNode的原因为:
// 节省空间, 无论ClinkNode中的data有多大, ClinkNode只占一个指针变量大小
typedef struct ClinkNode *CLinkLine;
#endif
实现代码
/*
* @Author: xgh
* @Date: 2020-06-08 22:43:59
* @LastEditTime: 2020-06-13 23:43:55
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: \VS-CODE-C\.vscode\cycleLinkedLists\cycleLinkedLists.c
*/
#include "data.h"
Statue InitList(CLinkLine *L);
int GetLenght(CLinkLine L);
Statue ListInsert(CLinkLine L, int index, ElemType data);
void showList(CLinkLine L);
Statue ListDelete(CLinkLine L, int index, ElemType *data);
void ClearList(CLinkLine L);
Statue ListEmpty(CLinkLine L);
void DestoryList(CLinkLine *L);
Statue GetElem(CLinkLine L, int index, ElemType *data);
int GetLocation(CLinkLine L, int Elem);
Statue BeforeElem(CLinkLine L, ElemType choose, ElemType *before);
int main(void)
{
CLinkLine cLine = NULL;
int i, index = 2;
ElemType data;
InitList(&cLine);
printf("循环链表长度为:%d\n\n", GetLenght(cLine));
for (i = 1; i <= 5; i++)
{
ListInsert(cLine, i, i);
}
ListInsert(cLine, 1, 6);
printf("插入完成, 循环链表长度为:%d\n\n", GetLenght(cLine));
showList(cLine);
GetElem(cLine, index, &data);
printf("第%d个位置的数据为:%d\n", index, data);
printf("元素6的位置为:%d\n", GetLocation(cLine, 6));
ListDelete(cLine, 1, &data);
printf("删除的数据为:%d\n", data);
showList(cLine);
BeforeElem(cLine, 5, &data);
printf("数据5的前驱结点元素为%d\n", data);
ClearList(cLine);
printf("表是否为空(1: 不为空, 0: 空): %d, 表长度为: %d\n", ListEmpty(cLine), GetLenght(cLine));
DestoryList(&cLine);
return 0;
}
/**
* @description: 创建空循环链表
* @param {CLinkLine *L: 二级指针}
* @return: SUCCESS:创建成功
*/
Statue InitList(CLinkLine *L)
{
(*L) = (CLinkLine)malloc(sizeof(clinkNode)); // 分配内存
if ((*L) == NULL)
{
printf("循环链表内存分配失败\n\n");
exit(ERROER);
}
(*L)->next = (*L); // 指针域指向自己
printf("循环链表内存分配成功\n\n");
return SUCCESS;
}
/**
* @description: 销毁链表
* @param {CLinkLine *L: 二级指针}
* @return: NULL
*/
void DestoryList(CLinkLine *L){
ClearList((*L)); // 清除表数据
free((*L)); // 释放头结点
(*L) = NULL; // 头指针置空
printf("链表销毁成功\n\n");
}
/**
* @description: 清空链表
* @param {CLinkLine L: 一级指针}
* @return: NULL
*/
void ClearList(CLinkLine L){
CLinkLine p, q;
p = L->next; // 指向第一个结点
// 循环清除结点
while(p != L){
q = p->next; // 获取要清除的结点的下一结点
free(p); // 释放结点
p = q; // 将要清除的结点的下一结点给到p,以此类推
}
L->next = L; // 指向自己
printf("清除数据结束,链表长度为%d\n\n", GetLenght(L));
}
/**
* @description: 判断链表是否为空
* @param {CLinkLine L: 一级指针}
* @return: EMPTY: 链表为空
* @return: NOT_EMPTY: 链表不为空
*/
Statue ListEmpty(CLinkLine L){
return L != L->next ? NOT_EMPTY : EMPTY;
}
/**
* @description: 获取循环链表长度
* @param {CLinkLine L: 一级指针}
* @return: len: 表长度
*/
int GetLenght(CLinkLine L)
{
int len = 0;
CLinkLine p = L->next; // 指向头结点
// 判断是否到表尾
while (p != L)
{
len++;
p = p->next;
}
return len;
}
/**
* @description: 遍历链表
* @param {CLinkLine L: 一级指针}
* @return: NULL
*/
void showList(CLinkLine L)
{
CLinkLine p = L->next;
printf("循环链表数据为:");
while (p != L)
{
printf("%4d", p->data);
p = p->next;
}
printf("\n\n");
}
/**
* @description: 插入数据
* @param {CLinkLine L: 一级指针}
* @param {int index: 插入的位置}
* @param {ElemType data: 插入的数据}
* @return: SUCCESS: 删除成功
*/
Statue ListInsert(CLinkLine L, int index, ElemType data)
{
CLinkLine s, p = L;
int j = 1;
// 判断插入位置是否合法
if (index <= 0 || index > (GetLenght(L) + 1))
{
printf("循环链表插入位置不合法\n\n");
return ERROER;
}
// 查找插入结点的前一个结点
while (j <= (index - 1))
{
++j;
p = p->next;
}
// 生成新结点
s = (CLinkLine)malloc(sizeof(clinkNode));
if (s == NULL)
{
printf("循环链表插入内存分配失败\n\n");
exit(ERROER);
}
// 将数据写入新结点的数据域
s->data = data;
// 将新结点的指针域指向插入位置的上一个结点的下一个结点
s->next = p->next;
// 将插入位置的上一个结点的指针域指向新结点
p->next = s;
//假如插入的位置是表尾,把新的表尾地址给尾指针
/* if(p == L)
{
L = s;
} */
return SUCCESS;
}
/**
* @description: 删除元素
* @param {CLinkLine L: 一级指针}
* @param {int index: 插入的位置}
* @param {ElemType *data: 删除的结点数据存放地址}
* @return: FAILURE: 删除失败
* @return: SUCCESS: 删除成功
*/
Statue ListDelete(CLinkLine L, int index, ElemType *data)
{
CLinkLine s, p = L;
int j = 1;
// 判断删除的位置是否合法
if (index < 1 || index > GetLenght(L))
return FAILURE;
// 找到删除结点的前一个结点
while (j <= (index - 1))
{
j++;
p = p->next;
}
s = p->next; // 获取到要删除的结点
p->next = s->next; // 将删除结点的后一个结点地址给到上一个结点的指针域
*data = s->data; // 获取到删除的数据
if(s == L)
L = p;
free(s);
return SUCCESS;
}
/**
* @description: 获取表中指定位置的数据
* @param {CLinkLine L: 一级指针}
* @param {int index: 插入的位置}
* @param {ElemType *data: 删除的结点数据存放地址}
* @return: FAILURE: 查找位置错误
* @return:SUCCESS: 查找成功
*/
Statue GetElem(CLinkLine L, int index, ElemType *data){
int j = 1;
if(index < 0 || index > GetLenght(L)){
return FAILURE;
}
CLinkLine p = L->next; // 指向第一个结点
while(j < index){ // 找到要查找的结点
j++;
p = p->next;
}
*data = p->data; // 获取到数据域数据
return SUCCESS;
}
/**
* @description: 获取元素的位置
* @param {CLinkLine L: 一级指针}
* @param {int Elem: 查找的元素}
* @return: 0: 表中不存在该数据
* @return: location: 结点位置
*/
int GetLocation(CLinkLine L, int Elem){
CLinkLine p = L->next; // 获取首结点
int location = 1;
while (p->data != Elem && p != L)
{
location++;
p = p->next;
}
// 判断是否是表尾, 到达表尾则元素不存在
if( p == L){
return 0;
}
return location;
}
/**
* @description: 查找某元素的前驱结点元素
* @param {CLinkLine L: 一级指针}
* @param {ElemType choose: 查找的元素}
* @param {ElemType *before: 前驱结点元素}
* @return: FAILURE: 查找失败
* @return: SUCCESS: 查找成功
*/
Statue BeforeElem(CLinkLine L, ElemType choose, ElemType *before){
// 获取两个相邻结点,判断第二个结点数据域数据是不是要查找的元素;
// 是则获取第一个结点的数据域数据;
// 否则两个结点向前移,将第二个结点给到第一个结点,第二个结点next;
// 当到达表尾还未找到要查找的元素,则返回查找失败
CLinkLine p = L->next; //p,q为相邻结点,
CLinkLine q = p->next;
// 判断是否达到表尾
while(q != L){
// 判断是否是要寻找的元素
if(q->data == choose){
*before = p->data; // 获取前驱结点元素
return SUCCESS;
}
p = q; //继续后移
q = q->next;
}
return FAILURE;
}