-->

单向循环链表

单向循环链表


  • 循环链表的主要特征就是没有NULL,头结点需要自己指向自己

image

  • 链表的节点

//存储数据类型(可修改),在遍历数据的时候是需要输出的,所以输出类型也需要修改
//链表节点下面是数据的数据类型
typedef int DataType_t;


//带头结点的链表
//1.每一个节点都有自己的元素和连接下一个元素的地址
typedef struct circularlinklist
{
    DataType_t                  data;   //链表节点存放的数据
    struct circularlinklist     *next;  //链表节点存放下一个链表节点的地址
}CclLkList_t;

创建链表

/***************************************************
*       函数名称:   CclLkList_CreatList
*       函数功能:   创建一个新的单向循环链表
*       函数参数:   None
*       返回结果:   返回链表的地址:创建成功。NULL:创建失败。
*       注意事项:   None
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
CclLkList_t * CclLkList_CreatList(void)
{
    //在堆空间申请一个空间存放链表的头结点
    CclLkList_t * Head = (CclLkList_t *)calloc(1,sizeof(CclLkList_t));
    if(NULL == Head){
        perror("calloc memory for head is failed!!!");
        return NULL;
    }

    //头结点的初始化,数据不需要初始化,单向循环链表的头结点创建时要指向自己
    Head->next = Head;

    //返回头结点的地址
    return Head;
}

创建新结点

/***************************************************
*       函数名称:   CclLkList_NewNode
*       函数功能:   创建一个链表新的节点
*       函数参数:   
*                   @data   :创建链表节点时,下面存放的数值
*       返回结果:   返回链表的地址:创建成功。NULL:创建失败。
*       注意事项:   一般都在插入的时候需要使用,不会单独使用
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
CclLkList_t * CclLkList_NewNode(DataType_t data)
{
    //1.先从堆空间申请内存存放链表节点
    CclLkList_t * New = (CclLkList_t *)calloc(1,sizeof(CclLkList_t));
    if(NULL == New){
        perror("Calloc memory for NewNode is failed!");
        return NULL;
    }

    //初始化链表节点,节点也要指向自己
    New->data = data;
    New->next = New;
    
    //申请成功直接返回
    return New;
}

头部插入结点

image

/***************************************************
*       函数名称:   CclLkList_HeadInsertNode
*       函数功能:   在链表的头结点后面插入节点
*       函数参数:   
*                   @Head   :要插入链表的地址
*                   @data   :插入的数据
*       返回结果:   true:插入成功。false:插入失败。
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool CclLkList_HeadInsertNode(CclLkList_t *Head,DataType_t data)
{
    //先判断链表是否存在
    if(NULL == Head){
        printf("Circular Linklist head is not exist!!!\n");
        return false;
    }

    //创建新的节点
    CclLkList_t *New = CclLkList_NewNode(data);
    if(NULL == New){
        return false;
    }

    //头结点只是开头不存放数据,循环的尾结点的next要指向首节点,只有头结点的话他是自己指向自己,所以要特殊处理
    //需要先判断链表是否为空(只有头结点)
    if(Head == Head->next){
        //为空的话另外插入
        Head->next = New;
        return true;
    }

    //不为空
    //备份头结点去找尾结点
    CclLkList_t *Phead = Head->next;
    while(Phead->next != Head->next){		/* 尾结点的next指向首节点 */
        Phead = Phead->next; 
    }

    //现在可以开始插入
    New->next = Head->next;
    Phead->next = New;
    Head->next = New;

    return true;
}

尾部插入结点

image

/***************************************************
*       函数名称:   CclLkList_TailInsertNode
*       函数功能:   在链表的尾结点后面插入节点
*       函数参数:   
*                   @Head   :要插入链表的地址
*                   @data   :插入的数据
*       返回结果:   true:插入成功。false:插入失败。
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool CclLkList_TailInsertNode(CclLkList_t *Head,DataType_t data)
{
    //先判断链表是否存在
    if(NULL == Head){
        printf("Circular Linklist head is not exist!!!\n");
        return false;
    }

    //创建新的节点
    CclLkList_t *New = CclLkList_NewNode(data);
    if(NULL == New){
        return false;
    }

    //头结点只是开头不存放数据,循环的尾结点的next要指向首节点,只有头结点的话他是自己指向自己,所以要特殊处理
    //需要先判断链表是否为空(只有头结点)
    if(Head == Head->next){
        //为空的话另外插入
        Head->next = New;
        return true;
    }

    //不为空
    //备份头结点去寻找尾部
    CclLkList_t *Phead = Head->next;
    while(Phead->next != Head->next){
        Phead = Phead->next;
    }

    //寻找完成之后,在最后插入
    New->next = Phead->next;
    Phead->next = New;

    return true;
}

中间插入结点

image

/***************************************************
*       函数名称:   CclLkList_MediumInsertNode
*       函数功能:   在scr_data数据后面插入dest数据
*       函数参数:   
*                   @Head       :要插入链表的地址
*                   @dest_data  :源数据,在这个后面插入
*                   @data  		:目标数据,需要插入的数据
*       返回结果:   true:插入成功。false:插入失败。
*       注意事项:   如果是空链表,不能插入
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool CclLkList_MediumInsertNode(CclLkList_t *Head,DataType_t dest_data,DataType_t data)
{
    //先判断链表是否存在
    if(NULL == Head){
        printf("Circular Linklist head is not exist!!!\n");
        return false;
    }

    //创建新的节点
    CclLkList_t *New = CclLkList_NewNode(data);
    if(NULL == New){
        return false;
    }

    //头结点只是开头不存放数据,循环的尾结点的next要指向首节点,只有头结点的话他是自己指向自己,所以要特殊处理
    //需要先判断链表是否为空(只有头结点)
    if(Head == Head->next){
        //为空的话另外插入
        Head->next = New;
        return true;
    }

    //不为空
    //定义一个变量去寻找dest_data
    CclLkList_t *Phead = Head->next;
    while((Phead->data != dest_data) && (Phead->next != Head->next)){
        Phead = Phead->next;
    }
	
    //判断是否循环结束还没有找到
    if((Phead->next == Head->next) && (Phead->data != dest_data)){
        printf("dest_data(%d) is not found!\n",dest_data);
        return false;
    }

    //寻找成功可以开始插入
    New->next = Phead->next;
    Phead->next = New;

    return true;
}

头部删除结点

  • 在删除结点时,需要判断链表是否为空

image

/***************************************************
*       函数名称:   CclLkList_IsEmpty
*       函数功能:   判断链表是否为空
*       函数参数:   
*                   @Head   :头结点的地址
*       返回结果:   true:为空;false:不为空
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool CclLkList_IsEmpty(CclLkList_t *Head)
{
	return (Head->next == Head) ? true : false;
}

/***************************************************
*       函数名称:   CclLkList_HeadDelNode
*       函数功能:   把链表的头结点删除
*       函数参数:   
*                   @Head   :要删除链表的地址
*       返回结果:   true:删除成功。false:删除失败。
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool CclLkList_HeadDelNode(CclLkList_t *Head)
{
    //先判断链表是否存在
    if(NULL == Head){
        printf("Circular Linklist head is not exist!!!\n");
        return false;
    }

    //判断链表是否为空
	if(CclLkList_IsEmpty(Head)){
		printf("circular Linklist is empty!!!\n");
		return false;
	}

    //不为空可以删除
    //先找到尾结点
    CclLkList_t *Phead = Head->next;
    while(Phead->next != Head->next){
        Phead = Phead->next;
    }
	
	//需要判断单向循环链表是否为一个节点,最后需要让头结点指向自己
	if(Phead->next == Phead){
		Phead->next = NULL;
		free(Phead);
		Phead = NULL;
		Head->next = Head;
		return true;
	}

    //删除首节点
    Phead->next = (Head->next)->next;
	
    (Head->next)->next = NULL;
    free(Head->next);
	
    Head->next = Phead->next;

    return true;
}

尾部删除结点

image

/***************************************************
*       函数名称:   CclLkList_TailDelNode
*       函数功能:   把链表的尾结点删除
*       函数参数:   
*                   @Head   :要删除链表的地址
*       返回结果:   true:删除成功。false:删除失败。
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool CclLkList_TailDelNode(CclLkList_t *Head)
{
    //先判断链表是否存在
    if(NULL == Head){
        printf("Circular Linklist head is not exist!!!\n");
        return false;
    }

    //判断链表是否为空
	if(CclLkList_IsEmpty(Head)){
		printf("circular Linklist is empty!!!\n");
		return false;
	}

    //不为空可以删除
    //先找到尾结点,以及尾结点的直接前驱
    CclLkList_t *Phead = Head->next;
    CclLkList_t *Phead_prev = Head;
    while(Phead->next != Head->next){
        Phead = Phead->next;
        Phead_prev = Phead_prev->next;
    }
	
	//需要判断单向循环链表是否为一个节点,最后需要让头结点指向自己
	if(Phead->next == Phead){
		Phead->next = NULL;
		free(Phead);
		Phead = NULL;
		Head->next = Head;
		return true;
	}	
	
    //寻找到尾之后就可以开始删除
    Phead_prev->next = Phead->next;
    Phead->next =  NULL;
    free(Phead);
    Phead = NULL;

    return true;
}

中间删除结点

image

/***************************************************
*       函数名称:   CclLkList_MediumDelNode
*       函数功能:   把data所在的节点删除
*       函数参数:   
*                   @Head   :要删除链表的地址
*                   @data   :在这个数据删除
*       返回结果:   true:删除成功。false:删除失败。
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool CclLkList_MediumDelNode(CclLkList_t *Head,DataType_t data)
{
    //先判断链表是否存在
    if(NULL == Head){
        printf("Circular Linklist head is not exist!!!\n");
        return false;
    }

    //判断链表是否为空
	if(CclLkList_IsEmpty(Head)){
		printf("circular Linklist is empty!!!\n");
		return false;
	}

    //寻找到这个数据所在的节点
    CclLkList_t *Phead = Head->next;
    CclLkList_t *Phead_prev = Head;
    while((Phead->data != data) && (Phead->next != Head->next)){
        Phead = Phead->next;
        Phead_prev = Phead_prev->next;
    }

    //判断是不是循环了整个链表都没找到
    if((Phead->data != data) && (Phead->next == Head->next)){
        printf("data(%d) is not found!\n",data);
        return false;
    }
	
	//需要判断单向循环链表是否为一个节点,最后需要让头结点指向自己
	if(Phead->next == Phead){
		Phead->next = NULL;
		free(Phead);
		Phead = NULL;
		Head->next = Head;
		return true;
	}

    //现在到这个数据所在的位置之后,开始删除
    Phead_prev->next = Phead->next;
    Phead->next = NULL;
    free(Phead);
    Phead = NULL;

    return true;
}

遍历链表

/***************************************************
*       函数名称:   CclLkList_TraNode
*       函数功能:   遍历整个链表
*       函数参数:   
*                   @Head   :要遍历链表的地址
*       返回结果:   true:遍历成功。false:遍历失败。
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool CclLkList_TraNode(CclLkList_t *Head)
{
    //先判断链表是否存在
    if(NULL == Head){
        printf("Circular Linklist head is not exist!!!\n");
        return false;
    }

    //判断链表是否为空
    if(Head == Head->next){
        printf("Circular Linklist is empty!\n");
        return false;
    }

    //需要把Head备份一下,直接备份到可以输出的节点
    CclLkList_t * Phead = Head->next;
    int i = 1;
    //开始遍历
    while(Phead->next != Head->next){
        printf("[%d]th node is %d\n",i,Phead->data);
        Phead = Phead->next;
        i++;
    }
    printf("[%d]th node is %d\n",i,Phead->data);
    
    return true;
}

修改链表的数据

  • 根据所给对数据,把链表内部的数据修改
/***************************************************
*       函数名称:   CclLkList_DataRecNode
*       函数功能:   根据数据修改节点
*       函数参数:   
*                   @Head   	:头结点的地址
*					@dest_data	:需要修改的数据
*					@data		:修改的数据
*       返回结果:   true:修改成功;false:修改失败
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/04/29
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool CclLkList_DataRecNode(CclLkList_t * Head,DataType_t dest_data,DataType_t data)
{
	//1.先判断链表是否存在
    if(NULL == Head){
        printf("CclLkList head is not exist!!!\n");
        return false;
    }
	
	//判断链表是否为空
	if(CclLkList_IsEmpty(Head)){
		printf("circular Linklist is empty!!!\n");
		return false;
	}
	
	//备份头结点对地址,以免丢失链表
	CclLkList_t *Phead = Head->next;
	
    //寻找到数据节点,并且防止没有这个数据
    while((Phead->data != dest_data) && (Phead->next != Head->next)){
        Phead = Phead->next;
    }	
	
	//判断数据是否存在
	if((Phead->data != dest_data) && (Phead->next == Head->next)){
		printf("dest_data(%d) is not exist!!!\n",dest_data);
        return false;
	}
	
	Phead->data = data;
	
	return true;
}

删除整个链表

/***************************************************
*       函数名称:   CclLkList_Del
*       函数功能:   删除整个链表
*       函数参数:   
*                   @Head   :要删除链表的地址
*       返回结果:   true:删除成功。false:删除失败。
*       注意事项:   Node
*       函数作者:
*       创建日期:   2024/04/23
*       修改历史:
*       函数版本:   V1.0
***************************************************/
bool CclLkList_Del(CclLkList_t *Head)
{
    //先判断链表是否存在
    if(NULL == Head){
        printf("Circular Linklist head is not exist!!!\n");
        return false;
    }

    //判断链表是否为空
    if(CclLkList_IsEmpty(Head)){
        Head->next = NULL;
        free(Head);
        Head = NULL;
        return true;
    }

    //可以开始删除
    //需要定义两个变量来记录他的当前结点的前驱节点
    //前驱节点走一个删除一个
    CclLkList_t * First = Head->next;
    CclLkList_t * Phead = Head->next;
    CclLkList_t * Phead_prev = Head;
	
    while(Phead->next != First){
        Phead_prev->next = NULL;
        free(Phead_prev);
        Phead_prev = Phead;
        Phead = Phead->next;
    }

    Phead_prev->next = NULL;
    free(Phead_prev);
    Phead_prev = NULL;

	Phead->next = NULL;
    free(Phead);
    Phead = NULL;

    return true;
}

需要的头文件

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

posted @ 2024-05-06 18:40  wuju  阅读(44)  评论(0)    收藏  举报