度阡舅 发表于 2025-6-30 17:37:25

单向循环链表的初始化、插入、删除、遍历

/**
* @file name : CircularLnList.c
* @brief   :实现一个单向循环链表的接口,可实现链表的增删改查,另外为了提高可移植性,所以链表中
*               数据元素的类型为DataType_t,用户可以根据实际情况修改链表中元素的类型
* @author    :MINDSETT@163.com
* @date      :2025/6/27
* @version   :1.0
* @note      :特别说明:此接口实现的单向循环链表中尾结点的指针域指向头结点后面的首结点的地址,并不是头结点的地址
* CopyRight (c)2025MINDSETT@163.comAll Right Reserved
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
//指定链表中元素的数据类型,用户可根据需要进行修改
typedef int DataType_t;

//构造链表的结点,链表中所有结点的数据类型应该是相同的(数据域+指针域)
typedef struct CircularLinkedList
{
    DataType_t                  data;   //结点的数据域
    struct CircularLinkedList *   next;   //结点的指针域
}CircularLnList_t;


/**
* @name    :CircularLnList_Create
* @brief   :创建一个空链表,空链表应该有一个头节点,对链表进行初始化
* @param   :None
* @retval:返回头结点的地址
* @date    :2025/6/27
* @version :1.0
* @note    :None
*/
CircularLnList_t *CircularLnList_Create(void)
{
    //创建一个头结点并对头结点申请内存
    CircularLnList_t* Head=(CircularLnList_t*)calloc(1,sizeof(CircularLnList_t));
    if (NULL==Head){
      perror("calloc memory for Head is failed\n");
      exit(-1);
    }
    //对头结点进行初始化,头结点不存储数据域,指针域指向自身,体现“循环”思想
    Head->next=Head;
    //将头结点的地址返回
    return Head;
}


/**
* @name    :CircularLnList_NewNode
* @brief   :创建一个新结点,并为新结点申请堆内存以及对新结点的数据域和指针域进行初始化
* @param   :
*             @data:新结点的数据
* @retval:返回新结点的地址
* @date    :2025/6/27
* @version :1.0
* @note    :None
*/
CircularLnList_t * CircularLnList_NewNode(DataType_t data)
{
    //创建新结点,并未新结点申请堆内存
    CircularLnList_t * New=(CircularLnList_t*)calloc(1,sizeof(CircularLnList_t));
   if (NULL==New){
      perror("calloc memory for NewNode is failed\n");
      return NULL;
    }
    //对新结点的数据域和指针域进行初始化
    New->data=data;
    New->next=NULL;
    return New;
}


/**
* @name    :CircularLnList_FirstInsert
* @brief   :创建一个新结点,并把新结点插入链表的首部初始化
* @param   :
*             @Head:插入的链表
*             @data:新结点的数据
* @retval:功返回true,失败返回false
* @date    :2025/6/27
* @version :1.0
* @note    :None
*/
bool CircularLnList_FirstInsert(CircularLnList_t * Head,DataType_t data)
{
    //创建新结点,并对新结点进行初始化
    CircularLnList_t* New=CircularLnList_NewNode(data);
    if (NULL==New){
      perror("Create New node is failed\n");
      return false;
    }
    //判断链表是否为空,如果为空,则将新结点直接插入
    if (Head==Head->next){
      Head->next=New;
      New->next=New;//指针域指向自身,体现“循环”思想
      return true;
    }
    //备份首结点的地址
    CircularLnList_t * tail=Head->next;
    //找到尾结点
    while( tail->next != Head->next ){
      tail=tail->next;
    }
    tail->next=New;          //尾结点的指针域指向新首结点
    //新结点插入首部   
    New->next=Head->next;   //新结点的指针域指向旧首结点
    Head->next=New;         //头结点的指针域指向新首结点

    return true;
}

/**
* @name    :CircularLnList_TailInsert
* @brief   :创建一个新结点,并把新结点插入链表的尾部
* @param   :
*             @Head:插入的链表
*             @data:新结点的数据
* @retval:成功返回true,失败返回false
* @date    :2025/6/27
* @version :1.0
* @note    :None
*/
bool CircularLnList_TailInsert(CircularLnList_t * Head,DataType_t data)
{
    //创建新结点,并对新结点进行初始化
    CircularLnList_t* New=CircularLnList_NewNode(data);
    if (NULL==New){
      perror("Create New node is failed\n");
      return false;
    }
    //判断链表是否为空,如果为空,则将新结点直接插入
    if (Head==Head->next){
      Head->next=New;
      New->next=New;//指针域指向自身,体现“循环”思想
      return true;
    }
    //备份首结点的地址
    CircularLnList_t * tail=Head->next;
    //找到尾结点
    while( tail->next != Head->next ){
      tail=tail->next;
    }
    //新结点指向首结点,旧尾结点指向新结点
    New->next=tail->next;
    tail->next=New;

    return true;
}


/**
* @name    :CircularLnList_DestInsert
* @brief   :创建一个新结点,并把新结点插入链表的指定位置
* @param   :
*             @Head:插入的链表
*             @Dest:指定需要新结点插入位置的直接前驱的数据
*             @data:新结点的数据
* @retval:成功返回true,失败返回false
* @date    :2025/6/27
* @version :1.0
* @note    :None
*/
bool CircularLnList_DestInsert(CircularLnList_t * Head,DataType_t Dest,DataType_t data)
{
    //创建新结点,并对新结点进行初始化
    CircularLnList_t* New=CircularLnList_NewNode(data);
    if (NULL==New){
      perror("Create New node is failed\n");
      return false;
    }
    //判断链表是否为空,如果为空,则将新结点直接插入
    if (Head==Head->next){
      Head->next=New;
      New->next=New;//指针域指向自身,体现“循环”思想
      return true;
    }
    //备份首结点的地址
    CircularLnList_t * curr=Head->next;
    //如果链表为非空,遍历链表,目的是找到目标结点
    do{
      if(Dest==curr->data){
            //找到目标结点
            New->next=curr->next;
            curr->next=New;
            return true;
      }
      curr=curr->next;
    }while( curr!=Head->next);
    //未找到目标结点,错误输出
    printf("Dest is not found\n");
    free(New);
      
    return false;
}


/**
* @name    :CircularLnList_FirstDel
* @brief   :删除链表的首结点
* @param   :
*             @Head:链表的头结点
* @retval:成功返回true,失败返回false
* @date    :2025/6/27
* @version :1.0
* @note    :None
*/
bool CircularLnList_FirstDel(CircularLnList_t* Head)
{
    //备份首结点
    CircularLnList_t * first=Head->next;
    CircularLnList_t * tail=Head->next;
    //判断传参错误或链表为空
    if (NULL==Head || Head==Head->next){
      perror("Linked list is empty\n");
      return false;
    }
    //1.只有一个首结点
    if (Head->next->next==Head->next){
      Head->next=Head;
      first->next=NULL;//首结点的指针域指向NULL,从链表中断开(避免野指针)
      free(first);
      return true;
    }
    //2.链表有多个结点
    //找到尾结点
    while( tail->next!=Head->next ){

      tail=tail->next;
    }
    //尾结点指向首结点的直接后驱
    tail->next=first->next;
    //将头结点指针域指向首结点的直接后驱
    Head->next=first->next;
    //旧的首结点的指针域指向NULL,从链表中断开(避免野指针)
    first->next=NULL;
    //释放首结点占用的内存
    free(first);

    return true;
}


/**
* @name    :CircularLnList_TailDel
* @brief   :删除链表的尾结点
* @param   :
*             @Head:链表的头结点
* @retval:成功返回true,失败返回false
* @date    :2025/6/27
* @version :1.0
* @note    :None
*/
bool CircularLnList_TailDel(CircularLnList_t* Head)
{
    //备份头结点和首结点
    CircularLnList_t * prev=Head;
    CircularLnList_t * tail=Head->next;
    //判断传参错误或链表为空
    if (NULL==Head || Head==Head->next){
      perror("Linked list is empty\n");
      return false;
    }
    //1.只有一个首结点
    if (Head->next->next==Head->next){
      Head->next=Head;
      tail->next=NULL;//首结点的指针域指向NULL,从链表中断开(避免野指针)
      free(tail);
      return true;
    }
    //2.链表有多个结点
    //找到尾结点和它的直接前驱
    while( tail->next!=Head->next ){
      prev=tail;
      tail=tail->next;
    }
    //尾结点的直接前驱指向首结点
    prev->next=tail->next;
    //旧的尾结点的指针域指向NULL,从链表中断开(避免野指针)
    tail->next=NULL;
    free(tail);

    return true;
}


/**
* @name    :CircularLnList_DestDel
* @brief   :删除链表的指定结点
* @param   :
*             @Head:链表的头结点
*             @Dest:需要删除的指定结点
* @retval:功返回true,失败返回false
* @date    :2025/6/27
* @version :1.0
* @note    :None
*/
bool CircularLnList_DestDel(CircularLnList_t* Head,DataType_t Dest)
{
    //备份头结点和首结点
    CircularLnList_t * prev=Head;
    CircularLnList_t * curr=Head->next;
    //判断传参错误或链表为空
    if (NULL==Head || Head==Head->next){
      perror("Linked list is empty\n");
      return false;
    }
    //1.只有一个首结点
    if (Head->next->next==Head->next){
      Head->next=Head;
      curr->next=NULL;//首结点的指针域指向NULL,从链表中断开(避免野指针)
      free(curr);
      return true;
    }
    //2.链表有多个结点
    //遍历链表,目的是找到目标结点和它的直接前驱
    do{
      if(Dest==curr->data){
            //找到目标结点
            prev->next=curr->next;
            curr->next=NULL;//尾结点的指针域指向NULL,从链表中断开(避免野指针)
            free(curr);
            return true;
      }
      prev=curr;
      curr=curr->next;
    }while( curr!=Head->next);
    //未找到目标结点,错误输出
    printf("Dest is not found\n");

    return false;
}


/**
* @name    :CircularLnList_Print
* @brief   :遍历输出链表中各个结点的数据
* @param   :
*             @Head:需要遍历的链表
* @retval:成功返回true,失败返回false
* @date    :2025/6/27
* @version :1.0
* @note    :None
*/
bool CircularLnList_Print(CircularLnList_t* Head)
{
    //判断传参错误或链表为空
    if (NULL==Head || Head==Head->next){
      perror("Current CircularLinkedList is empty\n");
      return false;
    }
    //对链表的头节点的地址进行备份
    CircularLnList_t *curr=Head;
    //循环遍历链表
    while(curr->next){
      curr=curr->next;
      printf("Data=%d ",curr->data);
      if( curr->next == Head->next){
            break;
      }
    }

    return true;
}
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 单向循环链表的初始化、插入、删除、遍历