2021-06-17环形链表!!

本文介绍两种检测链表中环的有效方法:使用Set存储节点和快慢指针技巧。通过实例解析,展示如何找到环的起始节点及其背后的逻辑。

leetcode每日一题之环形链表!!

题目链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/submissions/

题目描述:

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

例:
img

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

解法1:

利用Set存储节点,遍历,当出现重复说明有环

代码:比较简单,因此无注释

public ListNode detectCycle(ListNode head) {
        Set<ListNode> nodeSet = new HashSet<>();
        while (head != null) {
            if (nodeSet.contains(head)) {
                return head;
            }
            nodeSet.add(head);
            head = head.next;
        }
        return null;
    }

解法2:官方题解,快慢指针

具体分析看题解:https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/huan-xing-lian-biao-ii-by-leetcode-solution/

package com.tao.linklist;
/**
 * @Classname DetectCycle
 * @Description TODO
 * @Date 2021/6/17 13:22
 * @Author Anonymous
 */
@SuppressWarnings("all")
public class DetectCycle {
    public static void main(String[] args) {
        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(2);
        node1.next = node2;
        node2.next = node1;
        ListNode node = detectCycle(node1);
        System.out.println(node.val);
    }

    public static ListNode detectCycle(ListNode head) {
        // 链表本身不能为空 或者只有一个节点 也是无环的
        if (head == null || head.next == null) {
            return null;
        }
        ListNode slow = head;
        ListNode fast = head;
        while (true) {
            // 没有环时 fast会先走到链表尾部
            if (fast == null || fast.next == null) {
                return null;
            }
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) break;
        }
        // 第一次相遇后,让fast重新指向头结点head,slow保持不变。
        // fast和slow按照相同速度移动,第二次相遇后,此节点即为入口节点。
        fast=head;
        while(fast!=slow){
            fast=fast.next;
            slow=slow.next;
        }
        //相遇点就是入口
        return slow;
    }
}

拓展://可以求环的长度,当第二次相遇之后,也就是到达环的入口之后,开始遍历,存储到Set集合,当下次再出现环入口节点停止,就得到了环的长度

<think>我们正在ESP32-S3平台上使用ESP-IDF框架实现环形链表数据结构。环形链表是一种特殊的链表,其中最后一个节点指向头节点,形成一个环。这种数据结构在某些场景下非常有用,比如实现循环缓冲区或轮询调度算法。 步骤: 1. 定义链表节点的结构体。 2. 创建链表并实现基本操作:初始化、插入节点、删除节点、遍历等。 3. 注意在环形链表中,我们需要维护一个头节点(或尾节点)以方便操作。 由于ESP-IDF是一个基于FreeRTOS的框架,我们需要注意内存管理(使用heap_caps_malloc等)和线程安全(如果多个任务访问链表,可能需要互斥锁)。 下面我们逐步实现: 首先,定义节点结构体。每个节点包含数据和一个指向下一个节点的指针。</think>在 ESP32-S3 平台上使用 ESP-IDF 实现环形链表数据结构,需结合 FreeRTOS 特性进行内存管理和线程安全设计。以下是分步实现方案: ### 1. 节点结构体定义 ```c #include <stdlib.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" typedef struct Node { int data; // 节点数据 struct Node* next; // 指向下一个节点的指针 } CircularListNode; ``` ### 2. 链表管理结构体 ```c typedef struct { CircularListNode* tail; // 指向尾节点(环形链表中 tail->next = head) SemaphoreHandle_t mutex;// FreeRTOS 互斥锁 } CircularList; ``` ### 3. 初始化链表 ```c void list_init(CircularList* list) { list->tail = NULL; list->mutex = xSemaphoreCreateMutex(); // 创建互斥锁 if (!list->mutex) { ESP_LOGE("LIST", "Mutex creation failed"); } } ``` ### 4. 插入节点(线程安全) ```c void list_insert(CircularList* list, int data) { CircularListNode* new_node = malloc(sizeof(CircularListNode)); new_node->data = data; xSemaphoreTake(list->mutex, portMAX_DELAY); // 获取锁 if (list->tail == NULL) { new_node->next = new_node; // 自环 list->tail = new_node; } else { new_node->next = list->tail->next; // 新节点指向头节点 list->tail->next = new_node; // 尾节点指向新节点 list->tail = new_node; // 更新尾指针 } xSemaphoreGive(list->mutex); // 释放锁 } ``` ### 5. 删除节点(线程安全) ```c void list_delete(CircularList* list, int data) { if (list->tail == NULL) return; xSemaphoreTake(list->mutex, portMAX_DELAY); CircularListNode *curr = list->tail->next, *prev = list->tail; do { if (curr->data == data) { prev->next = curr->next; if (curr == list->tail) list->tail = (prev == curr) ? NULL : prev; free(curr); break; } prev = curr; curr = curr->next; } while (curr != list->tail->next); xSemaphoreGive(list->mutex); } ``` ### 6. 内存管理注意事项 - **ESP32 专用分配器**:替换 `malloc` 为 `heap_caps_malloc(MALLOC_CAP_SPIRAM)` 使用 PSRAM - **内存泄漏检测**:启用 ESP-IDF 的内存调试工具 `make monitor` 查看堆使用情况 - **销毁函数**:添加链表销毁函数遍历释放所有节点 ### 7. 使用示例 ```c void app_main() { CircularList my_list; list_init(&my_list); // 插入数据 list_insert(&my_list, 10); list_insert(&my_list, 20); // 删除数据 list_delete(&my_list, 10); } ``` ### 关键设计要点 1. **环形特性**:尾节点的 `next` 始终指向头节点,形成闭环 2. **线程安全**:通过 FreeRTOS 互斥锁保护临界区 3. **高效遍历**:从 `tail->next`(头节点)开始遍历 4. **内存优化**:使用 ESP32 的 `heap_caps` API 管理内存分区 > 该实现遵循 ESP-IDF 的线程安全规范,适用于传感器数据循环缓存、通信协议解析等场景[^1]。实际部署时建议添加错误检查并监控堆使用情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值