C语言实现单向循环链表

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;
}


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值