C语言实现简单的循环链表(单链结构)

为了方便,将元素类型和操作结构状态码的定义放到头文件自定义"ElemType.h"中

/**
 * #ifndef: 一种宏定义,是"if not defined"的简写,当包含该头文件的C源文件第一次包含该头文件时,未定义过该头文件,条件为真
 * 回执行语句#ifndef和语句#endif间的代码,当多次包含时,
 * 不再执行#ifndef和#endif间的代码,应为已经定义过了,条件为假
 */
#ifndef DATASTRUCTURE_ELEMTYPE_H
#define DATASTRUCTURE_ELEMTYPE_H

#include "stdio.h"
typedef double ElemType;

//操作成功
#define OK 1
//操作错误
#define ERROR 0
//操作异常
#define OVERFLOW -2
//定义元素类型,int可使用基本数据类型和用户自定义数据类型替换,根据使用场景选择
typedef int Status ;

double absForDouble(double a);

/**
 * 求绝对值
 * @param a
 * @return 返回a的绝对值
 */
double absForDouble(double a) {
    if (a < 0)return -a;
    return a;
}

/**
 * 实现两个ElemType类型变量的匹配
 * @param e1 ElemType类型变量e1
 * @param e2 ElemType类型变量e2
 * @return 匹配结果,1表示匹配,0表示不匹配
 */
int match(ElemType e1, ElemType e2){
    if(absForDouble(e1-e2)<1e-6)
        return 1;
        return 0;
}

/**
 * 打印数据元素
 */
printElem(ElemType e){
    printf("Current element's data is %lf!\n",e);
}

#endif //DATASTRUCTURE_ELEMTYPE_H

 

 

循环链表的定义

#include<stdio.h>
#include<stdlib.h>
#include "ElemType.h"

/**
 * 循环链表
 * 什么是循环链表?
 * 循环链表是另一种形式的链式存储结构。其特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环,由此,从表中任一个结点出发均可找到表中其他结点。
 */

/**
 * 循环链表的定义
 */

typedef struct {
    ElemType data;
    struct LNode *next;
} LNode, *CircularLinkedList;

 

循环链表上基本操作的实现

 

/**
 * 循环链表基本操作的实现
 */

/**
 * 初始化
 * 初始化循环链表,申请一个头结点,使头指针指向头结点,置头结点的指针域为头指针
 * 算法描述:
 * 1、申请一个头结点,使指向头指针的指针L指向的头指针指向头结点,若申请失败,返回OVERFLOW
 * 2、置头结点的指针域为头指针
 * 3、返回OK
 * @param L 指向单链表头指针的指针
 * @return 操作结果状态码
 */
Status initCircularLinkedList(CircularLinkedList *L) {
    //申请一个头结点,使指向头指针的指针L指向的头指针指向头结点
    *L = (LNode *) malloc(sizeof(LNode));
    //若申请失败,返回OVERFLOW
    if (!(*L)) return OVERFLOW;
    //置头结点的指针域为头指针
    (*L)->next = *L;
    (*L)->data = 0;
    //返回OK
    return OK;
}

/*
 * 销毁循环链表
 * 算法描述:
 * 1、判断指向头指针的指针L是否合法(即*L(头指针)是否指向NULL),若不合法(*L指向NULL),返回ERROR
 * 2、定义指向当前结点的指针p,使p指向首元结点
 * 3、定义临时指针t指向指针p指向的结点的直接后继
 * 4、循环置指针p指向结点的指针域为NULL,释放指针p指向的结点所占用的内存空间,并使p指向指针t指向的结点,使指针t指向指针p指向的结点的直接后继结点,当p=*L时,即销毁完全部结点时,循环终止
 * 5、释放头指针*L指向的头结点占用的内存空间
 * 6、置指针*L(头指针),p,t为NULL,返回OK
 * @param L 指向循环链表头结点的指针L
 * @return 操作结果状态码
 */
Status destroyCircularLinkedList(CircularLinkedList *L) {
    //判断指向头指针的指针L是否合法(即*L(头指针)或L是否指向NULL),若不合法(*L或L指向NULL),返回ERROR
    if (!(*L) || !L) {
        return ERROR;
    }
    //定义指向当前结点的指针p,使p指向首元结点
    LNode *p = (*L)->next;
    //定义临时指针t指向指针p指向的结点的直接后继
    LNode *t = p->next;
    //当p=*L时,即销毁完除头结点外的全部结点时,循环终止
    while (p != *L) {
        //置指针p指向结点的指针域为NULL
        p->next = NULL;
        //释放指针p指向的结点所占用的内存空间
        free(p);
        //使p指向指针t指向的结点
        p = t;
        //使指针t指向指针p指向的结点的直接后继结点
        t = p->next;
    }
    //释放头指针*L指向的头结点占用的内存空间
    free(*L);
    //置指针*L,p,t为NULL,返回OK
    *L = NULL;
    p = NULL;
    t = NULL;
    return OK;
}

/**
 * 清空循环链表
 * 算法描述:
 * 1、判断指针L是否合法,若不合法,返回ERROR
 * 2、定义指向当前结点的指针p指向首元结点
 * 3、定义临时指针t指向指针p指向的结点的直接后继结点
 * 4、循环置指针p指向结点的指针域为NULL,释放指针p指向的结点占用的内存,并使指针p指向下一个结点(指针t指向的结点),使指针t指向指针p指向的结点的直接后继结点,当p=*L(p指向头结点,除头节点外的其他结点均被释放),终止循环
 * 5、置L的指针域为L
 * 6、置指针p,t指向NULL,返回OK
 * @param L 指向循环链表的头指针的指针L
 * @return 操作结果状态码
 */
Status clearCircularLinkedList(CircularLinkedList L) {
    //判断指针L是否合法,若不合法,返回ERROR
    if (!L) return ERROR;
    //定义指向当前结点的指针p指向首元结点
    LNode *p = L->next;
    //定义临时指针t指向指针p指向的结点的直接后继结点
    LNode *t = p->next;
    //当p=L(p指向头结点,除头节点外的其他结点均被释放),终止循环
    while (p != L) {
        //置指针p指向结点的指针域为NULL
        p->next = NULL;
        //释放指针p指向的结点占用的内存
        free(p);
        //使指针p指向下一个结点(指针t指向的结点)
        p = t;
        //使指针t指向指针p指向的结点的直接后继结点
        t = p->next;
    }
    //置L的指针域为L
    L->next = L;
    //置指针p,t指向NULL,返回OK
    p = NULL;
    t = NULL;
    return OK;
}

/**
 * 空表判断
 * 算法描述:
 * 1、判断头指针p是否合法,若不合法,置*ret为零(假值),返回ERROR
 * 2、若循环链表为空表,则只包含头结点,且头结点的直接后继结点是头结点本身,因此若L->next=L,置*ret为1(真值),表示当前循环链表为空表
 * 3、否则,当前循环链表不是空表,置*ret为零(假值)
 * 4、返回OK
 * @param L 指针循环链表头结点的头指针L
 * @param ret 指向用以保持单链表是否为空表信息的变量的指针ret
 * @return 操作结果状态码
 */
Status circularLinkedListIsEmpty(CircularLinkedList L, int *ret) {
    //判断头指针p是否合法,若不合法,置ret为零(假值),返回ERROR
    if (!L) {
        *ret = 0;
        return ERROR;
    }
    if (L == L->next) {
        ////若循环链表为空表,则只包含头结点,且头结点的直接后继结点是头结点本身,因此若L->next=L,置ret为1(真值),表示当前循环链表为空表
        *ret = 1;
    } else {
        //否则,当前循环链表不是空表,置ret为零(假值)
        *ret = 0;
    }
    //返回OK
    return OK;
}

/**
 * 获取循环链表长度
 * 算法描述:
 * 1、判断指针L是否合法,不合法,置*len为负一(表示循环链表为初始化),返回ERROR
 * 2、定义指向当前结点的指针p使其指向首元结点
 * 3、定义计数器j记录指针p由首元结点移动至头结点需要依次的次数,j初始值为零
 * 4、循环移动指针p并使计数器j自增直至p=L(已经移动到了首元结点)
 * 5、把计数器j的值赋值给*len
 * 6、置指针p指向NULL,返回OK
 * @param L 指向循环链表的头结点的头指针L
 * @param len 指向用以保存循环链表的结点个数的变量的指针len
 * @return 操作结果状态码
 */
Status getCircularLinkedListLength(CircularLinkedList L, int *len) {
    //判断指针L是否合法,不合法,置*len为负一(表示循环链表为初始化),返回ERROR
    if (!L) {
        *len = -1;
        return ERROR;
    }
    //定义指向当前结点的指针p使其指向首元结点
    LNode *p = L->next;
    //定义计数器j记录指针p由首元结点移动至头结点需要依次的次数,j初始值为零
    int j = 0;
    //循环移动指针p并使计数器j自增直至p=L(已经移动到了首元结点)
    while (p != L) {
        p = p->next;
        ++j;
    }
    //把计数器j的值赋值给*len
    *len = j;
    //置指针p指向NULL,返回OK
    p = NULL;
    return OK;
}

/**
 * 取值
 * 取循环链表上第i个结点的数据域的值,i值必须合法,其范围为从1到n的整数,n为当前单链表的结点个数
 * 算法描述:
 * 1、判断指针L是否合法,若不合法,置*e的值为0(无任何意义),返回ERROR
 * 2、定义指向当前结点的指针p并使其指向首元结点
 * 3、定义计数器j记录指针p由首元结点移动到结点i的次数,即当j=i-1时即说明移动到结点i
 * 4、循环移动指针p并使计数器j自增直至p=L或者j=i-1
 * 5、判断循环终止条件,若p=L则说明遍历了所有循环链表的结点仍未到达结点i,即i > n,i非法,置*e的值为0(无任何意义),返回ERROR
 * 6、若j>i-1则说明未移动i-1次循环即结束,即i<1,i非法,置*e的值为0(无任何意义),返回ERROR
 * 7、i合法,将指向当前结点的指针p(此时恰好指向结点i)的数据域赋值给*e
 * 8、置指针p=NULL,返回OK
 * @param L 指向循环链表的头结点的头指针L
 * @param i 结点位置i
 * @param e 指向用以保存结点i的数据域的变量的指针e
 * @return 操作结果状态码
 */
Status getCircularLinkedListElem(CircularLinkedList L, int i, ElemType *e) {
    //判断指针L是否合法,若不合法,置*e的值为0(无任何意义),返回ERROR
    if (!L) {
        *e = 0;
        return ERROR;
    }
    //定义指向当前结点的指针p并使其指向首元结点
    LNode *p = L->next;
    //定义计数器j记录指针p由首元结点移动到结点i的次数,即当j=i-1时即说明移动到结点i,初始值未零(首元结点移动到第一个结点需要移动零次)
    int j = 0;
    //循环移动指针p并使计数器j自增直至p=L或者j=i-1
    while (p != L && j < i - 1) {
        p = p->next;
        ++j;
    }
    //判断循环终止条件,若p=L则说明遍历了所有循环链表的结点仍未到达结点i,即i > n,i非法,置*e的值为0(无任何意义),返回ERROR
    //若j>i-1则说明未移动i-1次循环即结束,即i<1,i非法,置*e的值为0(无任何意义),返回ERROR
    if (p == L || j > i - 1) {
        *e = 0;
        return ERROR;
    }
    //i合法,将指向当前结点的指针p(此时恰好指向结点i)的数据域赋值给*e
    *e = p->data;
    //置指针p=NULL,返回OK
    p = NULL;
    return OK;
}

/**
 * 定位元素
 * 定位循环链表中第一个数据域与给定ElemType匹配的结点的位置i和指向该结点的指针
 * 算法描述:
 * 1、判断指针L是否合法,不合法,置*i为零,置*curP为NULL(表示循环链表为初始化),返回ERROR
 * 2、定义指向当前结点的指针p使其指向首元结点
 * 3、定义计数器j记录当前结点序号,初始值为1(首元结点为第一个结点)
 * 4、循环移动指针p使计数器j自增直至p=L或者指针p的数据域与e匹配
 * 5、判断循环终止原因,若p=L,则说明遍历了循环链表所有结点仍未能找到数据域与e匹配的结点,置*i未负一,置*curP为NULL,表示未找到
 * 6、否则,把计数器j的值赋值给*i,把指针p的值赋值给*curP,表示结点i是循环链表的第一个数据域与e匹配的结点,*curP指向该结点
 * 7、置指针p为NULL,返回OK
 * @param L 指向循环链表的头结点的头指针L
 * @param i 指向用以保存结点位置信息的变量的指针
 * @param e 给定的ElemType的置
 * @param curP 指向匹配结点的指针的指针
 * @return 操作结果状态码
 */
Status locateAndIndexOfCircularLinkedListElem(CircularLinkedList L, int *i, LNode **curP, ElemType e) {
    //判断指针L是否合法,不合法,置*i为零,置*curP为NULL(表示循环链表为初始化),返回ERROR
    if (!L) {
        *i = 0;
        *curP = NULL;
        return ERROR;
    }
    //定义指向当前结点的指针p使其指向首元结点
    LNode *p = L->next;
    //定义计数器j记录当前结点序号,初始值为1(首元结点为第一个结点)
    int j = 1;
    //循环移动指针p使计数器j自增直至p=L或者指针p的数据域与e匹配
    while (p != L && !match(p->data, e)) {
        p = p->next;
        ++j;
    }
    //判断循环终止原因,若p=L,则说明遍历了循环链表所有结点仍未能找到数据域与e匹配的结点,置*i未负一,,置*curP为NULL表示未找到
    if (p == L) {
        *curP = NULL;
        *i = -1;
    }
    //否则,把计数器j的值赋值给*i,把指针p的值赋值给*curP,表示结点i是循环链表的第一个数据域与e匹配的结点,*curP指向该结点
    *curP = p;
    *i = j;
    //置指针p为NULL,返回OK
    p = NULL;
    return OK;
}

/**
 * 获取前趋
 * 获取循环链表上某结点的前趋结点
 * 算法描述:
 * 1、判断指针L是否合法,若非法,置*priorP为NULL,返回ERROR
 * 2、定义指向当前结点的指针p使其指向循环链表的头结点
 * 3、定义标识变量isVisitHead,置其值为零(假值),假值表示指针p第一次访问头结点
 * 4、循环移动指针p直至p->next=curE或者(p=L且isVisitHead为真)(直至p第二次指向头结点,说明变量完所有结点仍未找到curP指向的结点的前趋,即curP没有指向循环链表上的任何结点)
 * 5、判断循环终止原因,若p=L且isVisitHead为真则说明curP不指向循环链表上的任何结点,置*priorP为NULL,返回ERROR
 * 6、否则,将指针p的值赋值给*priorP
 * 7、置指针p为NULL,返回OK
 * @param L 指向循环链表头结点的头指针
 * @param curP 指向当前结点的指针
 * @param priorP 指向保存指针当前结点的前趋结点的增长的增长
 * @return 操作结果状态码
 */
Status getProirCircularLinkedList(CircularLinkedList L, LNode *curP, LNode **priorP) {
    //判断指针L是否合法,若非法,置*priorP为NULL,返回ERROR
    if (!L) {
        *priorP = NULL;
        return ERROR;
    }
    //定义指向当前结点的指针p使其指向循环链表的头结点
    LNode *p = L;
    //定义标识变量isVisitHead,置其值为零(假值),假值表示指针p第一次访问头结点
    int isVisitHead = 0;
    //循环移动指针p直至p->next=curP或者(p=L且isVisitHead为真)(直至p第二次指向头结点,说明变量完所有结点仍未找到curP指向的结点的前趋,即curP没有指向循环链表上的任何结点)
    while (p->next != curP && !(p == L && isVisitHead)) {
        //修改标识变量
        isVisitHead = 1;
        p = p->next;
    }
    //判断循环终止原因,若p=L且isVisitHead为真则说明curP不指向循环链表上的任何结点,置*priorP为NULL,返回ERROR
    if (p == L && isVisitHead) {
        *priorP = NULL;
        return ERROR;
    }
    //否则,将指针p的值赋值给*priorP
    *priorP = p;
    //置指针p为NULL,返回OK
    p = NULL;
    return OK;
}

/**
 * 获取后继
 * 获取循环链表当前结点的直接后继结点
 * 算法描述:
 * 1、判断指针L是否合法,若非法,置*nextP为NULL,返回NULL
 * 2、定义指向当前结点的指针p使其指向循环链表的首元结点
 * 3、若curP=L,说明当前结点是头结点,将其指针域赋值给*nextP,返回OK
 * 4、否则循环移动指针p直至p=curP或者p=L
 * 5、判断循环终止原因,若p=L则,说明遍历了循环链表上的所有结点,仍未找到与curP指向的结点,说明curP非法,置*nextP为NULL,返回ERROR
 * 6、否则说明curP指向的结点的确是循环链表上的结点,直接将curP指向的结点的指针域的值赋值给*nextP即可
 * 7、置指针p=NULL,返回OK
 * @param L 指向循环列表的头结点的头指针
 * @param curP 指向当前结点的指针
 * @param nextP 指向指向当前结点后继结点的指针的指针
 * @return 操作结果状态码
 */
Status getNextCircularLinkedList(CircularLinkedList L, LNode *curP, LNode **nextP) {
    //判断指针L是否合法,若非法,置*nextP为NULL,返回NULL
    if (!L) {
        *nextP = NULL;
        return ERROR;
    }
    //定义指向当前结点的指针p使其指向循环链表的首元结点
    LNode *p = L->next;
    //若curP=L,说明当前结点是头结点,将其指针域赋值给*nextP,返回OK
    if (curP == L) {
        *nextP = curP->next;
        return OK;
    }
    //否则循环移动指针p直至p=curP或者p=L
    while (p != curP && p != L) {
        p = p->next;
    }
    //判断循环终止原因,若p=L则,说明遍历了循环链表上的所有结点,仍未找到与curP指向的结点,说明curP非法,置*nextP为NULL,返回ERROR
    if (p == L) {
        *nextP = NULL;
        return ERROR;
    }
    //否则说明curP指向的结点的确是循环链表上的结点,直接将curP指向的结点的指针域的值赋值给*nextP即可
    *nextP = curP->next;
    //置指针p=NULL,返回OK
    p = NULL;
    return OK;
}

/**
 * 插入
 * 在循环链表位置i上插入数据域为e的新结点,即在结点i-1和结点i间插入一个新结点(结点i-1和结点i可能不存在),i的取值范围[1,n+1],i是整数
 * 算法描述:
 * 1、判断指针L及指向的指针是否合法,不合法,返回ERROR
 * 2、定义指向当前结点的指针p使其指向头结点
 * 3、定义计数器j记录指针p由首元结点移动到结点i-1所需的移动次数,置j初始值为0,当移动至结点i-1时,计数器j应该等于i-1
 * 4、定义计数器count记录指针p指向头结点的次数
 * 5、循环移动指针p直至指针p第二次指向头结点(说明移动了n次仍未到达结点i-1,故重新指向头结点)或者j=i-1(移动到了结点i-1)
 * 6、判断循环终止条件,若count=2,则说明i-1>n,移项得i>n+1,i值非法,返回ERROR
 * 7、若j>i-1说明还未移动i-1次,循环就结束了,即i<0,i不合法,返回ERROR
 * 8、否则循环终止原因是指针p指向了结点i-1可以进行插入操作了
 * 9、申请新结点,定义指向结点的指针s指向新结点,置新结点的数据域为e,置新结点的指针域指向原来循环链表上的结点i(指针p指向结点的直接后继)
 * 10、使指针p的指向的结点(结点i-1)的指针域指向新结点
 * 11、置指针p、s为NULL,返回OK
 * @param L 指向指向循环列表的头结点的头指针的指针
 * @param i 欲插入的位置i
 * @param e 插入结点的数据域e
 * @return 操作结果状态码
 */
Status insertNodeCircularLinkedList(CircularLinkedList L, int i, int e) {
    //判断指针L及指向的指针是否合法,不合法,返回ERROR
    if (!L)return ERROR;
    //定义指向当前结点的指针p使其指向头结点
    LNode *p = L;
    //定义计数器j记录指针p由首元结点移动到结点i-1所需的移动次数,置j初始值为0,当移动至结点i-1时,计数器j应该等于i-1
    int j = 0;
    //定义计数器count记录指针p指向头结点的次数
    int count = 1;
    //循环移动指针p直至指针p第二次指向头结点(说明移动了n+1次仍未到达或恰好到达结点i-1,故重新指向头结点)或者j=i-1(移动到了结点i-1)
    while (count < 2 && j < i - 1) {
        p = p->next;
        ++j;
        //指针p第二次指向头结点,修改计数器count的值
        if (p == L)count++;
    }
    //判断循环终止条件,若count=2,则说明i-1>=n+1,移项得i>=n+2,i值非法,返回ERROR
    //若j>i-1说明还未移动i-1次,循环就结束了,即i<0,i不合法,返回ERROR
    if (count == 2 || j > i - 1) {
        return ERROR;
    }
    //否则循环终止原因是指针p指向了结点i-1可以进行插入操作了
    //申请新结点,定义指向结点的指针s指向新结点,置新结点的数据域为e,置新结点的指针域指向原来循环链表上的结点i(指针p指向结点的直接后继)
    LNode *s = (LNode *) malloc(sizeof(LNode));
    s->data = e;
    s->next = p->next;
    //使指针p的指向的结点(结点i-1)的指针域指向新结点
    p->next = s;
    //置指针p、s为NULL,返回OK
    p = NULL;
    s = NULL;
    return OK;
}

/**
 * 删除
 * 删除循环链表位置i上的结点,即删除结点i-1和结点i+1之间的结点(结点i-1和结点i+1有可能不存在,必须检验i值的合法性,i(-[1,n],i是整数)
 * 算法描述:
 * 1、检验指针L是否合法,若非法,返回ERROR
 * 2、定义指向当前结点的增长p使其指向头结点
 * 3、定义计数器j记录指针p由头结点移动至结点i-1需移动的次数,置计数器初始值为零,当j=i -1即移动到了结点i-1
 * 4、循环移动指针p直至p->next=L或者j=i-1
 * 5、判断循环终止条件,若p->next=L说明指针p指向尾结点,而指针由头结点移动至尾结点(移动n次)仍未到达结点或者恰好到达结点i-1,即i-1>=n,移项可得i>=n+1,i非法,返回ERROR
 * 6、若j>i-1则说明还未移动满i-1次,循环就结束了,说明计数器j值仍然是零没有变化,即i-1<0,i<1,i非法,返回ERROR
 * 7、否则,说明循环结束原因是指针p指向了结点i-1,可以进行删除操作了
 * 8、定义临时指针变量t使其指向指针p指向结点的直接后继结点(结点i)
 * 9、使指针p指向的结点(结点i-1)的指针域指向指针t指向结点(结点i)的直接后继结点(结点i+1)
 * 10、释放指针t指向结点(结点i)所占用的内存空间
 * 11、置指针p,t未NULL,返回OK
 * @param L 指向循环链表头结点的头指针
 * @param i 欲删除的结点的位置
 * @return 操作结果状态码
 */
Status deleteNodeCircularLinkedList(CircularLinkedList L, int i) {
    //检验指针L是否合法,若非法,返回ERROR
    if (!L) {
        return ERROR;
    }
    //定义指向当前结点的增长p使其指向头结点
    LNode *p = L;
    //定义计数器j记录指针p由头结点移动至结点i-1需移动的次数,置计数器初始值为零,当j=i -1即移动到了结点i-1
    int j = 0;
    //循环移动指针p直至p->next=L或者j=i-1
    while (p->next != L && j < i - 1) {
        p = p->next;
        ++j;
    }
    //判断循环终止条件,若p->next=L说明指针p指向尾结点,而指针由头结点移动至尾结点(移动n次)仍未到达结点或者恰好到达结点i-1,即i-1>=n,移项可得i>=n+1,i非法,返回ERROR
    //若j>i-1则说明还未移动满i-1次,循环就结束了,说明计数器j值仍然是零没有变化,即i-1<0,i<1,i非法,返回ERROR
    if (p->next == L || j > i - 1) {
        return ERROR;
    }
    //否则,说明循环结束原因是指针p指向了结点i-1,可以进行删除操作了
    //定义临时指针变量t使其指向指针p指向结点的直接后继结点(结点i)
    LNode *t = p->next;
    //使指针p指向的结点(结点i-1)的指针域指向指针t指向结点(结点i)的直接后继结点(结点i+1)
    p->next = t->next;
    //释放指针t指向结点(结点i)所占用的内存空间
    free(t);
    //置指针p,t未NULL,返回OK
    p = NULL;
    t = NULL;
    return OK;
}

/**
 * 构造循环链表
 * 使用前插法构造具有n个结点(不计算头结点)的循环链表
 * 算法描述:
 * 1、判断指针L是否合法,若不合法,返回ERROR
 * 2、申请一个头结点,使指针*L指向头结点,若申请失败,返回OVERFLOW
 * 3、使头结点的指针域指向头结点,数据域为零(无意义)
 * 4、定义指向新结点的指针s
 * 5、循环申请新结点,使指针s指向新结点,使若申请失败,返回OVERFLOW,读入相应的数据,将其指针域指向当前头结点指针域指向的结点,使头结点指向新结点
 * 6、置指针s为NULL,返回OK
 * @param L 指向指向循环链表头结点的头指针的指针
 * @param n 循环链表的结点个数(不计算头结点)
 * @return  操作结果状态码
 */
Status createCircularLinkedListByInsertNewNodeAfterHeadNode(CircularLinkedList *L, int n) {
    //判断指针L是否合法,若不合法,返回ERROR
    if (!L)return ERROR;
    //申请一个头结点,使指针*L指向头结点
    *L = (LNode *) malloc(sizeof(LNode));
    //若申请失败,返回OVERFLOW
    if (!(*L))return OVERFLOW;
    //使头结点的指针域指向头结点,数据域为零(无意义)
    (*L)->next = *L;
    (*L)->data = 0;
    //定义指向新结点的指针s
    LNode *s = NULL;
    //循环
    ElemType e;
    int i;
    for (i = 0; i < n; ++i) {
        //申请新结点,使指针s指向新结点
        s = (LNode *) malloc(sizeof(LNode));
        //使若申请失败,返回OVERFLOW
        if (!s)return OVERFLOW;
        //读入相应的数据
        printf("请输入结点%d的数据:\n", i + 1);
        scanf("%lf", &e);
        s->data = e;
        //将其指针域指向当前头结点指针域指向的结点,使头结点指向新结点
        s->next = (*L)->next;
        (*L)->next = s;
    }
    //置指针s为NULL,返回OK
    s = NULL;
    return OK;
}

/**
 * 构造循环链表
 * 使用后插法构造具有n个结点(不计算头结点)的循环链表
 * 算法描述:
 * 1、判断指针L是否合法,若不合法,返回ERROR
 * 2、申请一个头结点,使指针*L指向头结点,若申请失败,返回OVERFLOW
 * 3、使头结点的指针域指向头结点,数据域为零(无意义)
 * 4、定义尾指针r使其指向头结点
 * 5、定义指向新结点的指针s
 * 6、循环申请新结点,使指针s指向新结点,使若申请失败,返回OVERFLOW,读入相应的数据,使新结点指针域指向头结点,使尾指针指向结点的指针域指向新结点
 * 7、置指针s,r为NULL,返回OK
 * @param L 指向指向循环链表头结点的头指针的指针
 * @param n 循环链表的结点个数(不计算头结点)
 * @return  操作结果状态码
 */
Status createCircularLinkedListByInsertNewNodeAfterRearNode(CircularLinkedList *L, int n) {
    //判断指针L是否合法,若不合法,返回ERROR
    if (!L) {
        return ERROR;
    }
    //申请一个头结点,使指针*L指向头结点
    *L = (LNode *) malloc(sizeof(LNode));
    //若申请失败,返回OVERFLOW
    if (!(*L))return OVERFLOW;
    //使头结点的指针域指向头结点,数据域为零(无意义)
    (*L)->next = *L;
    (*L)->data = 0;
    //定义尾指针r使其指向头结点
    LNode *r = *L;
    //定义指向新结点的指针s
    LNode *s = NULL;
    ElemType e;
    int i;
    for (i = 0; i < n; ++i) {
        //循环申请新结点,使指针s指向新结点
        s = (LNode *) malloc(sizeof(LNode));
        //使若申请失败,返回OVERFLOW
        if (!s)return OVERFLOW;
        //读入相应的数据
        printf("请输入结点%d的数据:\n", i + 1);
        scanf("%lf", &e);
        s->data = e;
        //使新结点指针域指向头结点
        s->next = *L;
        // 尾指针指向结点的指针域指向新结点
        r->next = s;
    }
    //置指针s,r为NULL,返回OK
    s = NULL;
    r = NULL;
    return OK;
}

/**
 * 遍历
 * 遍历循环链表
 * 算法描述:
 * 1、判断指针L是否合法,若非法,返回ERROR
 * 2、定义指向当前结点的指针p指向首元结点
 * 3、循环访问指针p指向结点的数据域并移动指针p直至p=L(遍历完成)
 * 4、置指针p尾NULL,返回OK
 * @param L 指向循环链表头结点的头指针
 * @return  操作结果状态码
 */
Status traverseCircularLinkedList(CircularLinkedList L) {
    //判断指针L是否合法,若非法,返回ERROR
    if (!L)return ERROR;
    //定义指向当前结点的指针p指向首元结点
    LNode *p = L->next;
    //循环访问指针p指向结点的数据域并移动指针p直至p=L(遍历完成)
    while (p != L) {
        printElem(p->data);
        p = p->next;
    }
    //置指针p尾NULL,返回OK
    p = NULL;
    return OK;
}

 

测试定义好的循环链表和其基本操作的功能

 

int main() {
    CircularLinkedList L = NULL;
    //测试初始化循环链表
    initCircularLinkedList(&L);
    if (!L) {
        printf("初始化失败!\n");
    } else {
        printf("初始化成功!\n");
        //测试空表判断
        int ret = 0;
        if (circularLinkedListIsEmpty(L, &ret)) {
            if (ret) {
                printf("循环链表是空表\n");
            } else {
                printf("循环链表不是空表\n");
            }
        }
        //测试获取长度
        int len;
        if (getCircularLinkedListLength(L, &len)) {
            printf("现在循环链表长度为 %d\n", len);
        } else {
            printf("现在循环链表长度为 %d\n", len);
        }
        //测试插入
        if (insertNodeCircularLinkedList(L, 1, 2) && insertNodeCircularLinkedList(L, 1, 3)) {
            printf("插入成功\n");
            printf("现在在循环链表的位置1上插入一个数据域为2的新结点\n");
            printf("现在在循环链表的位置1上插入一个数据域为3的新结点\n");
            //测试取值
            ElemType e;
            if (getCircularLinkedListElem(L, 1, &e)) {
                printf("循环链表上结点序号为1的结点的数据域为 %lf\n", e);
            } else {
                printf("取值失败\n");
            }
            //测试获取长度
            if (getCircularLinkedListLength(L, &len)) {
                printf("现在循环链表长度为 %d\n", len);
            } else {
                printf("现在循环链表长度为 %d\n", len);
            }
            //测试空表判断
            if (circularLinkedListIsEmpty(L, &ret)) {
                if (ret) {
                    printf("循环链表是空表\n");
                } else {
                    printf("循环链表不是空表\n");
                }
            }
            //测试定位
            e = 2;
            int curI = 0;
            LNode *curP = NULL;
            if (locateAndIndexOfCircularLinkedListElem(L, &curI, &curP, e)) {
                printf("定位成功\n");
                printf("循环链表上第一个数据域为%lf的结点的序号为%d,其地址是%d\n", e, curI, curP);
            } else {
                printf("定位失败\n");
                printf("循环链表上第一个数据域为e的结点的序号为%d,其地址是%d\n", curI, curP);
            }
            curP = NULL;
        } else {
            printf("插入失败\n");
        }

    }
    //测试清空循环链表
    if (clearCircularLinkedList(L)) {
        printf("清空成功\n");
    } else {
        printf("清空失败\n");
    }
    //测试空表判断
    int ret = 0;
    if (circularLinkedListIsEmpty(L, &ret)) {
        if (ret) {
            printf("循环链表是空表\n");
        } else {
            printf("循环链表不是空表\n");
        }
    }
    //测试获取长度
    int len;
    if (getCircularLinkedListLength(L, &len)) {
        printf("现在循环链表长度为 %d\n", len);
    } else {
        printf("现在循环链表长度为 %d\n", len);
    }
    //测试销毁循环链表
    if (destroyCircularLinkedList(&L)) {
        printf("销毁成功\n");
        L = NULL;
    } else {
        printf("销毁失败\n");
    }
    //测试空表判断
    if (circularLinkedListIsEmpty(L, &ret)) {
        if (ret) {
            printf("循环链表是空表\n");
        } else {
            printf("循环链表不是空表\n");
        }
    }
    //测试获取长度
    if (getCircularLinkedListLength(L, &len)) {
        printf("现在循环链表长度为 %d\n", len);
    } else {
        printf("现在循环链表长度为 %d\n", len);
    }
    L = NULL;
    //测试前插法
    int size;
    printf("请输入要构造的循环链表的长度:\n");
    scanf("%d", &size);
    if (createCircularLinkedListByInsertNewNodeAfterHeadNode(&L, size)) {
        printf("前插法功能正常!\n");
        //测试遍历
        traverseCircularLinkedList(L);
    } else {
        printf("前插法功能不正常!\n");
    }
    //测试删除
    int curI = 3;
    printf("请输入删除的结点序号:\n");
    scanf("%d", &curI);
    if (deleteNodeCircularLinkedList(L, curI)) {
        printf("现在删除循环链表上序号为%d的结点\n", curI);
        printf("删除成功!\n");
        //测试遍历
        traverseCircularLinkedList(L);
        LNode *curP = NULL;
        ElemType e;
        printf("请输入要定位的结点的数据域:\n");
        scanf("%lf", &e);
        if (locateAndIndexOfCircularLinkedListElem(L, &curI, &curP, e)) {
            //测试获取前趋
            /*LNode *antherP = (LNode *) malloc(sizeof(LNode));
            antherP->next = NULL;
            antherP->data = 0;*/
            LNode *priorP = NULL;
            if (getProirCircularLinkedList(L, curP, &priorP)) {
                printf("获取前趋成功!\n");
                printf("数据域为%lf的结点的前趋结点的地址是%d,数据域为%lf\n", e, priorP, priorP->data);
            } else {
                printf("获取前趋失败!\n");
            }
            //测试获取后继
            LNode *nextP = NULL;
            if (getNextCircularLinkedList(L, curP, &nextP)) {
                printf("获取后继成功!\n");
                printf("数据域为%lf的结点的后继结点的地址是%d,数据域为%lf\n", e, nextP, nextP->data);
            } else {
                printf("获取后继失败!\n");
            }
            priorP = NULL;
            nextP = NULL;
        } else {
            printf("定位失败!\n");
        }
        curP = NULL;
        /*antherP = NULL;*/
    } else {
        printf("删除失败!\n");
        //测试遍历
        traverseCircularLinkedList(L);
    }
    return 0;
}

 

posted @ 2020-07-25 15:53  DNoSay  阅读(542)  评论(0编辑  收藏  举报