找出带环链表中入环处节点问题

环形链表是一种特殊的链表,其中链表的末尾节点指向头节点,形成一个环。在环形链表中,我们需要找到入环节点(环的起点)和出环节点(环的终点)。

一、环形链表的初始化

首先,我们需要初始化一个环形链表。以下是一个简单的环形链表结构体示例:

// 环形链表节点结构体
typedef struct stData {
    float data;
    struct stData *pNext;
} stData;

初始化环形链表的过程如下:

  1. 为环形链表分配一个节点的内存,这也是整个环形链表的地址。
  2. 使用两个临时指针,一个用于连接各个节点(指定各节点的 pNext 指针指向下一个节点),另一个用于不断分配下一个节点的内存。
void init_list(stData **pList, int len) {
    // 分配第一个节点的内存
    *pList = (stData*)malloc(sizeof(stData));
    (*pList)->data = 0;
    (*pList)->pNext = NULL;

    stData *pTmp1 = *pList;
    for (int i = 0; i < len - 1; i++) {
        // 分配下一个节点的内存
        stData *pTmp2 = (stData*)malloc(sizeof(stData));
        pTmp2->data = 0;
        pTmp2->pNext = NULL;

        // 连接节点
        pTmp1->pNext = pTmp2;
        pTmp1 = pTmp2;
    }

    // 将最后一个节点的 next 指向头节点,构成环形链表
    pTmp1->pNext = *pList;
}

二、查询环形链表中有效数据的长度

在向环形链表写入数据时,我们需要知道有效数据的长度,以便在合适的位置写入新的数据。以下是查询有效数据长度的函数:

int check_list_isfull(stData *pHead, stData *pTail) {
    int cnt = 0;
    stData *pTmp = pHead;

    while (pTmp != pTail && pTmp != NULL) {
        cnt++;
        pTmp = pTmp->pNext;
    }

    return cnt;
}

三、找到入环节点

一旦我们找到相遇点(即环中的某个节点),我们可以从链表的头节点和相遇点同时出发,直到它们再次相等。此时,相等的节点就是入环节点。

stData *detectCycle(stData *head) {
    stData *fast = head;
    stData *slow = head;

    while (fast && fast->pNext) {
        slow = slow->pNext;
        fast = fast->pNext->pNext;

        if (fast == slow) {
            // 从头节点和相遇点同时走,直到相等,即为入环点
            while (head != slow) {
                head = head->pNext;
                slow = slow->pNext;
            }
            return head; // 入环节点
        }
    }

    return NULL; // 无环
}

算法原理

当我们需要在一个链表中找到带环的入环节点时,快慢指针算法是一种常见且高效的解决方案。下面详细解释一下这个算法的原理:

  1. 初始化快慢指针

    • 我们定义两个指针:fast 和 slow,初始时都指向链表的头部。
    • fast 每次移动两步,而 slow 每次移动一步。
  2. 判断是否有环

    • 如果链表中不存在环,那么 fast 指针最终会遇到 null,此时我们结束算法。
    • 如果链表中存在环,那么 fast 和 slow 指针一定会再次相遇。
  3. 找到入环节点

    • 当 fast 和 slow 指针相遇时,我们额外创建一个指针 ptr,并将其指向链表的头部。
    • 然后,我们同时移动 ptr 和 slow,每次都走一步,直到它们再次相遇。
    • 此时,相遇点就是链表的入环节点。
  4. 为什么这个算法有效

    • 假设链表中存在环,我们可以将环的长度记为 c,从链表头部到入环点的距离记为 a,从入环点到相遇点的距离记为 b
    • 当 slow 和 fast 指针相遇时,slow 走过的距离为 a + b,而 fast 走过的距离为 a + n·c + b,其中 n 表示 fast 绕环的圈数。
    • 由于 fast 指针的速度是 slow 的两倍,我们可以得到以下关系:a + n·c + b = 2(a + b)
    • 进一步简化,我们可以得到 a = n·c - b
    • 这意味着,如果我们从头部再创建一个指针,让它和 slow 同步移动,当它们相遇时,相遇点就是入环节点。
  5. 时间复杂度和空间复杂度

    • 时间复杂度:O(N),其中 N 是链表节点的数量。
    • 空间复杂度:O(1),因为我们只使用了 slowfast 和 ptr 三个指针。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值