尾不指头:一种别致的单向循环链表

单向循环链表操作实现

单向循环链表是一种链表数据结构,和单链表不同的地方是:最后一个节点的指针不指向 null,而是指向链表的头节点,形成一个环状结构。这样从任何一个节点出发,都可以通过指针遍历整个链表。
20200726091943622

代码实现

单向循环链表的创建、插入、删除、查找等常用操作。

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

typedef int DataType_t; 
typedef struct circularlink_manager
{
	DataType_t data; 
	struct circularlink_manager *next;

}Circllink_t;

//用来创建一个头结点 
 Circllink_t* Circllink_Create(void)
{
	Circllink_t *Head = (Circllink_t *)calloc(1,sizeof(Circllink_t));
	if(Head == NULL){
		perror("Calloc memory for the Head is failed!\n");
		exit(-1);
	}
	//链表只有一个头结点时,头结点next指向自身
	Head->next = Head;
	return Head;
}

//用来创建一个新的有效结点 
Circllink_t* Circllink_NewNode(DataType_t data)
{
	Circllink_t *New = (Circllink_t *)calloc(1,sizeof(Circllink_t));
	if(New == NULL){
		perror("Calloc memory for the Head is failed!\n");
		return NULL;
	}
	//新结点初始化
	New->data = data;
	New->next = NULL;

	return New;
}

//头插(在头结点后插入一个结点)
bool Circl_HeadInsert(Circllink_t* Head,DataType_t data)
{
	Circllink_t *New = Circllink_NewNode(data);
  if (New == NULL){// 函数Circllink_NewNode给新结点内存分配失败会返回NULL
      return false; 
  }
	if(Head->next == Head){//如果只有头结点,New直接成为首结点
		Head->next = New;
		New ->next = New;
	}
	else{
	Circllink_t *Find_Tail = Head->next;
	//定义指针Tail,找到并指向最后一个结点
	while(Find_Tail->next != Head->next){
		Find_Tail = Find_Tail->next;
	}

	New ->next = Head->next;
	Head->next = New;
	//更新尾结点的next,尾结点next始终指向首结点
	Find_Tail->next = New;
}
	return true;
}
//尾插
bool Circll_TailInsert(Circllink_t* Head,DataType_t data)
{
	Circllink_t *New = Circllink_NewNode(data);// 函数Circllink_NewNode给新结点内存分配失败会返回NULL
  if (New == NULL) {
      return false; 
  }
  //如果只有头结点,New直接成为首结点
	if(Head->next == Head){
		Head->next = New;
		New ->next = New;
	}
	else{
		Circllink_t *Find_Tail = Head->next;
		while(Find_Tail->next != Head->next){//循环找链表最后一个结点
	
			Find_Tail = Find_Tail->next;
		}
		Find_Tail->next = New;
		New ->next = Head->next;
	}
	return true;
}
//指定链表位置插入结点
bool Circll_DestInsert(Circllink_t* Head,DataType_t dest,DataType_t data)
{
	Circllink_t *New = Circllink_NewNode(data);
  	if (New == NULL) {
    return false; 
  	}
	if(Head->next == Head){
		Head->next = New;
		New ->next = New;
		return true;
	}
	Circllink_t *Find_Dest = Head->next;
	//遍历链表,找到数据域data等于目标值dest的结点
	while(Find_Dest->data != dest){	
		if (Find_Dest->next == Head->next){//若遍历到链表最后一个结点,data和dest还不相等,说明dest为无效指定结点位置
		
			printf("无效指定结点位置\n");
			return false;
		}
		Find_Dest = Find_Dest->next;

	}
	if (Find_Dest->next  == Head->next){//若遍历到链表最后一个结点,数据域data等于dest,新结点New成为尾结点
	
		Find_Dest->next = New;
		New->next = Head->next;
	}
	else{
		New->next = Find_Dest->next;
		Find_Dest->next = New;
	}
	return true;
}



//遍历链表
bool CirLList_Print(Circllink_t *Head)
{
	Circllink_t *Phead = Head;
	if(Head->next == Head){
		printf("current circulinked is empty!\n");
		return false;
	}
	while(Phead->next){
		Phead = Phead->next;
		printf("data = %d\n",Phead->data);
		if(Phead->next == Head->next)
		{	
			break;
		}
	}
	return true;
}

//删除链表头结点
bool CirLList_HeadDel(Circllink_t *Head)
{
	Circllink_t *Phead = Head;
	Circllink_t *Temp  = Head->next;

	if(Head->next == Head){
		printf("current circulinked is empty!\n");
		return false;
	}
	//如果链表只有一个有效结点
	if(Head->next->next == Head->next){
		Head->next = Head;
		free(Temp);
		return true;
	}
	//找到最后一个结点
	while(Phead->next){
		Phead = Phead->next;
		if(Phead->next == Head->next){	
			break;
		}
	}

	Phead ->next = Head->next->next;//更新尾结点next的指向
	Head  ->next = Phead->next;
	Temp  ->next = NULL;
	free(Temp);
	return true;
}
//删除链表尾结点
bool CirLList_TailDel(Circllink_t *Head)
{
	Circllink_t *Phead = Head->next;
	if(Head->next == Head){
		printf("current circulinked is empty!\n");
		return false;
	}

	if(Phead->next == Head->next){//如果链表只有一个首结点(有效结点)
		Head->next = Head;
		free(Phead);
		Phead = NULL;
		return true;
	}
	while(Phead->next->next != Head->next){//找到最后一个结点
		Phead = Phead->next;
	}
	free(Phead->next);
	Phead->next = Head->next;//更新尾结点next指向
	return true;
}
//指定位置删除链表结点
bool cirlist_del_dest(Circllink_t* Head,DataType_t dest)
{	
	if(Head->next == Head){
		printf("current circulinked is empty!\n");
		return false;
	}
	Circllink_t *PreFind_Dest = Head;
	Circllink_t *Find_Dest = Head->next;
	while(Find_Dest->data != dest){	
		if (Find_Dest->next == Head->next){//若遍历到链表最后一个结点
		
			printf("未找到指定节点\n");
			return false;
		}
		PreFind_Dest = PreFind_Dest->next;
		Find_Dest = Find_Dest->next;
	}
	//若目标结点是唯一结点
	if (Find_Dest == Find_Dest->next){
		Head->next = Head;
	}
	else{
		//PreFind_Dest->next = Find_Dest->next;
		if(Find_Dest == Head->next){
			Circllink_t *Find_Tail = Head->next;
			while(Find_Tail->next != Head->next){//循环找链表最后一个结点
				Find_Tail = Find_Tail->next;
			}
			    Find_Tail->next = Find_Dest->next;
			    PreFind_Dest->next = Find_Dest->next;
		}
		else{
				PreFind_Dest->next = Find_Dest->next;			
		}
	}
	free(Find_Dest);
	return true;
}

主函数测试

int main(int argc, char const *argv[])
{
	Circllink_t *Head = Circllink_Create();
    printf("链表已创建\n");

    // 测试插入操作
    printf("\n=== 插入测试 ===\n");
    Circl_HeadInsert(Head, 10);  // 头插 10
    Circl_HeadInsert(Head, 20);  // 头插 20
    Circll_TailInsert(Head, 30); // 尾插 30
    Circll_TailInsert(Head, 40); // 尾插 40
    Circll_DestInsert(Head, 30, 35); // 在 30 后插入 35

    // 打印链表
    printf("\n插入后的链表:\n");
    CirLList_Print(Head);//预估结果:20,10 30 35 40

    // 测试删除操作
    printf("\n=== 删除测试 ===\n");
    printf("删除指定值 20 的节点:\n");
    if (cirlist_del_dest(Head, 20)) {
        printf("删除成功\n");
    } else {
        printf("删除失败\n");
    }
	CirLList_Print(Head);
    printf("\n删除头节点:\n");
    if (CirLList_HeadDel(Head)) {
        printf("删除成功\n");
    } else {
        printf("删除失败\n");
    }
	CirLList_Print(Head);
    printf("\n删除尾节点:\n");
    if (CirLList_TailDel(Head)) {
        printf("删除成功\n");
    } else {
        printf("删除失败\n");
    }
    // 再次打印链表
    printf("\n删除后的链表:\n");
    CirLList_Print(Head);//预估结果:30 35 
    // 释放头节点
    free(Head);
    printf("\n链表已释放\n");
    return 0;
}

测试结果

vmware_ihGBasSdkg

细心的读者可能会发现:在本实现中,链表的最后一个有效节点的 next 指向的是第一个有效节点(即 Head->next),而不是头结点 Head 本身。这与部分教材中“尾节点指向头结点”的描述略有不同。

设计思想:头结点是“管理节点”,不参与数据循环

在我的实现中,头结点(Head)被定义为一个纯管理性质的辅助节点,它不存储任何有效数据,也不应被纳入“数据节点”的循环逻辑中。真正的数据循环只发生在所有有效数据节点之间

  • 尾节点的 next 指向第一个有效节点(Head->next),形成一个仅包含有效数据节点的闭环

  • 头结点 Head 仅作为入口指针和结构管理存在,不参与数据环。

无论采用哪种方式,只要在整个代码中保持统一逻辑,应该都是合理的实现。

posted @ 2025-08-03 22:27  Yue+  阅读(14)  评论(0)    收藏  举报