当前位置:首页 > 技术 > 正文内容

单向循环链表:原理与核心操作实现

访客 技术 2026年8月4日 1

概述

单向循环链表是链式存储结构的特殊形式,其尾节点的指针域不再指向NULL,而是回绕到头节点,形成逻辑上的环形结构。这种设计消除了传统单链表的方向性限制,使得从任意节点出发均可访问整个链表。

存储结构对比分析

1. 内存分配策略

顺序存储结构需要预先申请连续内存空间,容量固定;而单向循环链表采用动态节点分配,各节点物理位置离散,通过指针建立逻辑关联,容量可弹性扩展。

2. 时间复杂度表现

  • 查找操作:顺序存储支持O(1)随机访问;单向循环链表需顺序遍历,平均时间复杂度为O(n)
  • 增删操作:顺序存储需移动大量元素,平均O(n);单向循环链表在定位后仅需修改指针,操作本身为O(1)

3. 空间利用率

顺序存储存在预分配浪费或溢出风险;单向循环链表按需分配,空间利用率理论上可达100%,但需额外存储指针域。

核心特性

  1. 环形遍历能力:无需空指针判断,通过检测指针是否回到起始位置即可控制遍历终止
  2. 全节点可达性:从任一节点出发均可访问所有其他节点,解决了单链表逆向访问困难的问题
  3. 结构轻量化:无需增加额外字段,仅调整指针指向即可实现循环特性

实现详解

节点定义

typedef int ResultCode;
typedef int DataType;

typedef struct CircularNode {
    DataType value;
    struct CircularNode *next;
} CircularNode;

typedef CircularNode* CircularList;

初始化与构建

采用尾插法构建循环链表时,需特别注意维护尾节点与头节点的闭环关系。

ResultCode BuildList(CircularList *head) {
    DataType inputValue;
    CircularList newNode = NULL;
    CircularList tailPtr = NULL;
    
    printf("请输入节点值(-1结束):");
    while (scanf("%d", &inputValue) == 1) {
        if (inputValue == -1) break;
        
        newNode = (CircularList)malloc(sizeof(CircularNode));
        if (!newNode) return -1;
        
        newNode->value = inputValue;
        
        if (*head == NULL) {
            newNode->next = newNode;
            tailPtr = newNode;
            *head = newNode;
        } else {
            newNode->next = *head;
            tailPtr->next = newNode;
            tailPtr = newNode;
        }
    }
    return 0;
}

遍历输出

三种典型的遍历实现方式:

void DisplayList(CircularList head) {
    if (!head) {
        printf("链表为空\n");
        return;
    }
    
    CircularList current = head;
    
    // 方案一:do-while结构
    do {
        printf("%d -> ", current->value);
        current = current->next;
    } while (current != head);
    
    // 方案二:while结构(预读式)
    // while (current->next != head) {
    //     printf("%d -> ", current->value);
    //     current = current->next;
    // }
    // printf("%d", current->value);
    
    // 方案三:for循环
    // for (; current->next != head; current = current->next) {
    //     printf("%d -> ", current->value);
    // }
    // printf("%d", current->value);
    
    printf("(回到头节点)\n");
}

节点插入

插入操作需区分头位置与其他位置。在头部插入时,必须同步更新尾节点的next指针以维持循环性。

ResultCode InsertNode(CircularList *head, int position, DataType newData) {
    if (position < 1) return -1;
    
    CircularList newNode = (CircularList)malloc(sizeof(CircularNode));
    if (!newNode) return -1;
    
    newNode->value = newData;
    
    if (position == 1) {
        // 插入到头部
        CircularList tailNode = *head;
        if (tailNode) {
            while (tailNode->next != *head) {
                tailNode = tailNode->next;
            }
        }
        
        newNode->next = *head;
        if (*head) {
            tailNode->next = newNode;
        } else {
            newNode->next = newNode;
        }
        *head = newNode;
    } else {
        // 插入到中间或尾部
        CircularList prevNode = *head;
        for (int i = 1; i < position - 1 && prevNode->next != *head; i++) {
            prevNode = prevNode->next;
        }
        
        newNode->next = prevNode->next;
        prevNode->next = newNode;
    }
    return 0;
}

节点删除

删除操作同样需特殊处理头节点,确保删除后尾节点仍能正确指向新的头节点。

ResultCode RemoveNode(CircularList *head, int position) {
    if (!*head) return -1;
    
    CircularList targetNode, prevNode;
    
    if (position == 1) {
        // 删除头节点
        targetNode = *head;
        prevNode = *head;
        
        while (prevNode->next != *head) {
            prevNode = prevNode->next;
        }
        
        if (prevNode == *head) {
            // 仅有一个节点
            free(*head);
            *head = NULL;
            return 0;
        }
        
        prevNode->next = (*head)->next;
        *head = (*head)->next;
        free(targetNode);
    } else {
        // 删除其他位置
        prevNode = *head;
        for (int i = 1; i < position - 1 && prevNode->next != *head; i++) {
            prevNode = prevNode->next;
        }
        
        if (prevNode->next == *head) return -1; // 位置超出范围
        
        targetNode = prevNode->next;
        prevNode->next = targetNode->next;
        free(targetNode);
    }
    return 0;
}

查询操作

基于位置的查询:

DataType GetElementByPos(CircularList head, int position) {
    if (!head || position < 1) return 0;
    
    CircularList current = head;
    for (int i = 1; i < position && current->next != head; i++) {
        current = current->next;
    }
    
    return current->value;
}

基于值的查找(假设值唯一):

int GetPositionByValue(CircularList head, DataType targetValue) {
    if (!head) return -1;
    
    CircularList current = head;
    int position = 1;
    
    while (current->value != targetValue && current->next != head) {
        current = current->next;
        position++;
    }
    
    return (current->value == targetValue) ? position : -1;
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。