首页 > 编程语言 >【数据结构-链表】链表的相关算法

【数据结构-链表】链表的相关算法

时间:2022-11-07 16:47:39浏览次数:42  
标签:结点 NULL nextNode next 链表 算法 数据结构 LinkNode

目录

1 删除元素

1.1 删除值为 x 的所有结点

1.1.1 不带头结点的单链表

  • 正常写法:
void Delete_X (LinkList &L, int x){
    LinkNode *p = L;
    LinkNode *prevNode; // 记录 p 的前驱结点
    LinkNode *nextNode; // 记录 p 的后继结点
    
    while (p != NULL){
        if (p->data == x){
            if (p == L){ // 如果是头结点
                nextNode = p->next;
                free(p);
                L = nextNode;
            }
            else{ // 如果是中间结点或尾结点
                nextNode = p->next;
                free(p);
                prevNode->next = nextNode;
            }
        }
        prevNode = p; // 记录 p 的前驱结点
        p = p->next;  // 遍历下一个结点 
    }
}
  • 递归写法:
void Delete_X (LinkList &L, int x){
    LinkNode *p;
    
    if (L->data == x){
        p = L;
        L = L->next;
        free(p);
        Delete_X(L, x);
    }
    else
        Delete_X(L->next, x);
}

1.1.2 带头结点的单链表

void Delete_X (LinkList &L, int x){
    LinkNode *p;
    LinkNode *prevNode; // 记录 p 的前驱结点
    LinkNode *nextNode; // 记录 p 的后继结点
    
    p = L->next;
    prevNode = L;
    while (p != NULL){
        if (p->data == x){ 
            nextNode = p->next;
            free(p);
            prevNode->next = nextNode;
        }
        prevNode = p; // 记录 p 的前驱结点
        p = p->next;  // 遍历下一个结点 
    }
}

【注】欲删除值介于 s 和 t 之间的所有结点,需把条件改为(s <= p->data) && (p->data <= t)

1.2 删除重复元素

1.2.1 不带头结点的单链有序表

void Delete_Same (LinkList &L){
    LinkNode *p = L; // 初始指向链表头部
    LinkNode *nextNode; // 记录 p 的后继结点
    
    while (p != NULL){
        nextNode = p->next; // 记录 p 的后继结点
        if ((nextNode != NULL) && (nextNode->data == p->data)){ // 发现重复元素
            p->next = nextNode->next;
            free(nextNode);
        }
        else // 没有发现重复元素
            p = p->next;  // 遍历下一个结点 
    }
}

1.2.2 带头结点的单链有序表

void Delete_Same (LinkList &L){
    LinkNode *p = L->next; // 初始指向链表第一个结点
    LinkNode *nextNode; // 记录 p 的后继结点
    
    while (p != NULL){
        nextNode = p->next; // 记录 p 的后继结点
        if ((nextNode != NULL) && (nextNode->data == p->data)){ // 发现重复元素
            p->next = nextNode->next;
            free(nextNode);
        }
        else // 没有发现重复元素
            p = p->next;  // 遍历下一个结点 
    }
}

2 链表逆置

2.1 逆序输出结点

2.1.1 不带头结点的单链表

设 L 为不带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。

void Reverse_Print (LinkList &L){
    LinkNode *p = L; // 指向链表头部
    Stack S; // 定义栈
    int x;
    
    InitStack(S); // 初始化栈
    
    while (p != NULL){
        PushStack(S, p->data); // 入栈
        p = p->next;
    }
    
    while (!EmptyStack(S)){
        x = PopStack(S); // 出栈
        输出 x;
    }
}

2.1.2 带头结点的单链表

设 L 为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。

void Reverse_Print (LinkList &L){
    LinkNode *p = L->next;
    Stack S; // 定义栈
    int x;
    
    InitStack(S); // 初始化栈
    
    while (p != NULL){
        PushStack(S, p->data); // 入栈
        p = p->next;
    }
    
    while (!EmptyStack(S)){
        x = PopStack(S); // 出栈
        输出 x;
    }
}

2.2 就地逆置

2.2.1 不带头结点的单链表

试编写一个算法将不带头结点的单链表就地逆置。

就地:空间复杂度为 O(1)。

思路:总是把第一个结点取下来,然后用尾插法重新建立链表。

LinkList Reverse (LinkList &L){
    LinkNode *head = L;         // 指向原链表头部
    LinkNode *nextNode;         // 保存原链表头部的指针域
    LinkNode *newHead = NULL;   // 指向新链表头部
    
    while (head != NULL){
        nextNode = head->next;  // 保存原链表头部的指针域
        head->next = newHead;   // 原链表头部的指针域指向新链表头部
        newHead = head;         // 新链表头部更新
        head = nextNode;        // 原链表头部更新
    }
    
    L = newHead;
    return L;
}

2.2.2 带头结点的单链表

LinkList Reverse (LinkList &L){
    LinkNode *head = L->next;   // 指向原链表第一个结点
    LinkNode *nextNode;         // 保存原链表头部的指针域
    LinkNode *newHead = NULL;   // 指向新链表头部
    
    while (head != NULL){
        nextNode = head->next;  // 保存原链表头部的指针域
        head->next = newHead;   // 原链表头部的指针域指向新链表头部
        newHead = head;         // 新链表头指针更新
        head = nextNode;        // 原链表头指针更新
    }
    
    L->next = newHead;          // 更新头结点指针域,头结点插到新链表头部
    return L;
}

3 链表有序

3.1 链表排序

3.1.1 不带头结点的单链表

有一个不带头结点的单链表 L,设计一个算法使其元素递增有序。

思路:每次取出原链表的第一个元素,与有序链表中每个元素进行对比,然后插入到有序链表的相应位置。

void Sort (LinkList &L){
    LinkNode *head = L;             // 原链表头指针
    LinkNode *nextNode;             // 保存原链表头部的指针域
    LinkNode *newHead;              // 有序链表头指针
    LinkNode *newNode, *newPrev;    // 有序链表结点指针及其前驱结点
    
    newHead = (LinkNode *) malloc(sizeof(LinkNode));
    newHead->data = L->data;
    newHead->next = NULL;
    
    while (head != NULL){
        newPrev = NULL;
        newNode = newHead;
        
        while ((newNode != NULL) && (head->data <= newNode->data)){ // 有序链表指针指向第一个大于 head 元素的结点
            newPrev = newNode;       // 记录其前驱结点
            newNode = newNode->next; // 继续遍历新链表
        }                            // 运行到此处,newPrev 在前,newNode 在后
        
        nextNode = head->next;  // 保存原链表头部的指针域
        if (newPrev == NULL){   // 如果要插入有序链表的头部位置
            head->next = newNode; 
            newHead = head;     // 更新有序链表头指针
        }
        else{                   // 如果要插入有序链表的中间和尾部位置
            newPrev->next = head;
            head->next = newNode;
        }
        head = nextNode;        // 继续遍历原链表
    }
    L = newHead;
}

3.1.2 带头结点的单链表

有一个带头结点的单链表 L,设计一个算法使其元素递增有序。

void Sort (LinkList &L){
    LinkNode *head = L->next;       // 原链表头指针
    LinkNode *nextNode;             // 保存原链表头部的指针域
    LinkNode *newNode, *newPrev;    // 有序链表结点指针及其前驱结点
    
    while (head != NULL){
        newPrev = L;                // 原链表头结点作为新链表的头结点
        newNode = L->next;
        
        while ((newNode != NULL) && (head->data <= newNode->data)){ // 有序链表指针指向第一个大于 head 元素的结点
            newPrev = newNode;       // 记录其前驱结点
            newNode = newNode->next; // 继续遍历新链表
        }                            // 运行到此处,newPrev 在前,newNode 在后
        
        nextNode = head->next;  // 保存原链表头部的指针域
        newPrev->next = head;
        head->next = newNode;
        head = nextNode;        // 继续遍历原链表
    }
}

3.2 链表有序输出

3.2.1 不带头结点的单链表

按递增次序输出单链表各结点的数据元素,每输出一个将该结点删除。

思路:每次遍历链表找出最小元素,输出该元素,然后删除。

void Print_Min (LinkList &L){
    LinkNode *prev; // 扫描指针
    LinkNode *prevNode, *nodeMin = L, *nextNode;  // 最小值结点(初始指向链表第一个结点)以及其前驱和后继结点
    int min = L->data;
    
    while (L != NULL){
        prev = L;
        while (prev->next != NULL){
            if (prev->next->data < min){
                prevNode = prev;            // 记录最小值的前驱结点
                nodeMin = prev->next;       // 记录最小值结点
                min = nodeMin->data;  
                nextNode = prev->next->next;// 记录最小值的后继结点
            }
            prev = prev->next;
        }
        输出 min;
        
        if (nodeMin == L){ // 如果第一个结点为最小值
            free(nodeMin);
            L = nextNode;
        }
        else{
            free(nodeMin);
            prevNode->next = nextNode;
        }
    }
}

3.2.2 带头结点的单链表

按递增次序输出单链表各结点的数据元素,每输出一个将该结点删除。

思路:每次遍历链表找出最小元素,输出该元素,然后删除。

void Print_Min (LinkList &L){
    LinkNode *prev; // 扫描指针
    LinkNode *nodeMin = L->next, *prevNode, *nextNode;  // 最小值结点(初始指向链表第一个结点)以及其前驱和后继结点
    int min = L->next->data;
    
    while (L->next != NULL){ // 循环到只剩头结点
        prev = L;
        while (prev->next != NULL){
            if (prev->next->data < min){
                prevNode = prev;            // 记录最小值的前驱结点
                nodeMin = prev->next;       // 记录最小值结点
                min = nodeMin->data;
                nextNode = prev->next->next;// 记录最小值的后继结点
            }
            prev = prev->next;
        }
        
        输出 min;
        free(nodeMin); // 删除最小值结点
        prevNode->next = nextNode; 
    }
    
    free(L);
}

4 合并链表

4.1 两个递增链表合并为一个递增链表

4.1.1 不带头结点的单链表

思路:设置双指针,建立新链表,运用尾插法,把两个链表的结点按升序插入即可。

LinkList Merge (LinkList &A, LinkList &B){
    LinkNode *pa = A, *pb = B; // 扫描指针,指向前驱结点
    LinkNode *prevNode, *node, *nextNode; // 记录前驱结点、当前要插入的结点、后继结点
    LinkNode *C, *newNode;    // 新链表
    LinkNode *tail; // 新链表的尾指针
    
    if (A->data < B->data){ // 单独处理两个链表的第一个结点,这两个结点单独保留,将问题转化为“带头结点的单链表”
        newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第一个结点
        newNode->data = A->data;
        C = newNode;
        newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第二个结点
        newNode->data = B->data;
        newNode->next = NULL;
        C->next = newNode;
        tail = newNode;
    }
    else{
        newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第一个结点
        newNode->data = B->data;
        C = newNode;
        newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第二个结点
        newNode->data = A->data;
        newNode->next = NULL;
        C->next = newNode;
        tail = newNode;
    }

    while ((pa->next != NULL) && (pb->next != NULL)){
        if (pb->next->data < pa->next->data){
            prevNode = pb;          // 记录前驱结点···(1)
            node = pb->next;        // 记录当前要插入的结点
            nextNode = node->next;  // 记录后继结点
            prevNode->next = nextNode; // 前驱结点指向后继结点···(2)
            tail->next = node;      // 往新链表尾插当前结点···(3)
            tail = node;            // 更新新链表尾指针
            pb = pb->next;          // 链表 B 的扫描指针继续遍历···(4)
        }
        else{
            prevNode = pa;          // 记录前驱结点···(1)
            node = pa->next;        // 记录当前要插入的结点
            nextNode = node->next;  // 记录后继结点
            prevNode->next = nextNode;  // 前驱结点指向后继结点···(2)
            tail->next = node;      // 往新链表尾插当前结点···(3)
            tail = node;            // 更新新链表尾指针
            pa = pa->next;          // 链表 A 的扫描指针继续遍历···(4)
        }
    }
    
    // 运行到此处,一定有且仅有一个链表非空
    if (A->next != NULL)    // 若链表 A 还未空
        tail->next = A->next; // 新链表尾部直接插入链表 A 的剩余部分
    if (B->next != NULL)    // 若链表 B 还未空
        tail->next = B->next; // 新链表尾部直接插入链表 B 的剩余部分
        
    free(A); // 删除链表 A
    free(B); // 删除链表 B
    return C;
}

4.1.2 带头结点的单链表

思路:设置双指针,建立新链表,运用尾插法,把两个链表的结点按升序插入即可。

LinkList Merge (LinkList &A, LinkList &B){
    LinkNode *pa = A, *pb = B; // 扫描指针,指向前驱结点
    LinkNode *prevNode, *node, *nextNode; // 记录前驱结点、当前要插入的结点、后继结点
    LinkNode *C = (LinkNode *) malloc(sizeof(LinkNode)); // 新链表的头结点
    LinkNode *tail = C; // 新链表的尾指针

    while ((pa->next != NULL) && (pb->next != NULL)){
        if (pb->next->data < pa->next->data){
            prevNode = pb;          // 记录前驱结点···(1)
            node = pb->next;        // 记录当前要插入的结点
            nextNode = node->next;  // 记录后继结点
            prevNode->next = nextNode; // 前驱结点指向后继结点···(2)
            tail->next = node;      // 往新链表尾插当前结点···(3)
            tail = node;            // 更新新链表尾指针
            pb = pb->next;          // 链表 B 的扫描指针继续遍历···(4)
        }
        else{
            prevNode = pa;          // 记录前驱结点···(1)
            node = pa->next;        // 记录当前要插入的结点
            nextNode = node->next;  // 记录后继结点
            prevNode->next = nextNode;  // 前驱结点指向后继结点···(2)
            tail->next = node;      // 往新链表尾插当前结点···(3)
            tail = node;            // 更新新链表尾指针
            pa = pa->next;          // 链表 A 的扫描指针继续遍历···(4)
        }
    }
    
    // 运行到此处,一定有且仅有一个链表非空
    if (A->next != NULL)    // 若链表 A 还未空
        tail->next = A->next; // 新链表尾部直接插入链表 A 的剩余部分
    if (B->next != NULL)    // 若链表 B 还未空
        tail->next = B->next; // 新链表尾部直接插入链表 B 的剩余部分
        
    free(A); // 删除链表 A 的头结点
    free(B); // 删除链表 B 的头结点
    return C;
}

4.2 两个递增链表合并为一个递减链表

4.2.1 不带头结点的单链表

思路:设置双指针,建立新链表,运用头插法,把两个链表的结点按升序插入即可。

LinkList Merge (LinkList &A, LinkList &B){
    LinkNode *pa = A, *pb = B; // 扫描指针,指向前驱结点
    LinkNode *prevNode, *node, *nextNode; // 记录前驱结点、当前要插入的结点、后继结点
    LinkNode *C, *newNode;  // 新链表
    LinkNode *headNext;     // 新链表的头结点后继指针
    
    if (A->data > B->data){ // 单独处理两个链表的第一个结点,这两个结点单独保留,将问题转化为“带头结点的单链表”
        newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第一个结点
        newNode->data = A->data;
        C = newNode;
        newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第二个结点
        newNode->data = B->data;
        newNode->next = NULL;
        C->next = newNode;
    }
    else{
        newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第一个结点
        newNode->data = B->data;
        C = newNode;
        newNode = (LinkNode *) malloc(sizeof(LinkNode)); // 链表 C 的第二个结点
        newNode->data = A->data;
        newNode->next = NULL;
        C->next = newNode;
    }

    while ((pa->next != NULL) && (pb->next != NULL)){
        if (pb->next->data < pa->next->data){
            prevNode = pb;          // 记录前驱结点···(1)
            node = pb->next;        // 记录当前要插入的结点
            nextNode = node->next;  // 记录后继结点
            prevNode->next = nextNode; // 前驱结点指向后继结点···(2)
            node->next = C;         // 当前结点插入到新链表中···(3)
            C = node;               // 更新新链表的头指针
            pb = pb->next;          // 链表 B 的扫描指针继续遍历···(4)
        }
        else{
            prevNode = pa;          // 记录前驱结点···(1)
            node = pa->next;        // 记录当前要插入的结点
            nextNode = node->next;  // 记录后继结点
            prevNode->next = nextNode;  // 前驱结点指向后继结点···(2)
            node->next = C;         // 当前结点插入到新链表中···(3)
            C = node;               // 更新新链表的头指针
            pa = pa->next;          // 链表 A 的扫描指针继续遍历···(4)
        }
    }
    
    // 运行到此处,一定有且仅有一个链表非空
    if (A->next != NULL){       // 若链表 A 还未空
        while (pa->next != NULL)// 先找到链表 A 的尾部
            pa = pa->next;
        headNext = head->next;  // 记录新链表头结点的后继指针
        head->next = A->next;   // 新链表尾部直接插入链表 A 的剩余部分
        pa->next = headNext;
    }
    if (B->next != NULL){       // 若链表 B 还未空
        while (pb->next != NULL)// 先找到链表 B 的尾部
            pb = pb->next;
        headNext = head->next;  // 记录新链表头结点的后继指针
        tail->next = B->next;   // 新链表尾部直接插入链表 B 的剩余部分
        pb->next = headNext;
    }
        
    free(A); // 删除链表 A
    free(B); // 删除链表 B
    return C;
}

4.2.2 带头结点的单链表

思路:设置双指针,建立新链表,运用头插法,把两个链表的结点按升序插入即可。

LinkList Merge (LinkList &A, LinkList &B){
    LinkNode *pa = A, *pb = B; // 扫描指针,指向前驱结点
    LinkNode *prevNode, *node, *nextNode; // 记录前驱结点、当前要插入的结点、后继结点
    LinkNode *C = (LinkNode *) malloc(sizeof(LinkNode)); // 新链表的头结点
    LinkNode *head = C, *headNext = C->next; // 新链表的头结点,及头结点的后继指针

    while ((pa->next != NULL) && (pb->next != NULL)){
        if (pb->next->data < pa->next->data){
            prevNode = pb;          // 记录前驱结点···(1)
            node = pb->next;        // 记录当前要插入的结点
            nextNode = node->next;  // 记录后继结点
            prevNode->next = nextNode; // 前驱结点指向后继结点···(2)
            headNext = head->next;  // 记录新链表头结点的后继指针···(3)
            head->next = node;      // 往新链表尾插当前结点
            node->next = headNext;  // 当前结点指向头结点的后继指针
            pb = pb->next;          // 链表 B 的扫描指针继续遍历···(4)
        }
        else{
            prevNode = pa;          // 记录前驱结点···(1)
            node = pa->next;        // 记录当前要插入的结点
            nextNode = node->next;  // 记录后继结点
            prevNode->next = nextNode;  // 前驱结点指向后继结点···(2)
            headNext = head->next;  // 记录新链表头结点的后继指针···(3)
            head->next = node;      // 往新链表尾插当前结点
            node->next = headNext;  // 当前结点指向头结点的后继指针
            pa = pa->next;          // 链表 A 的扫描指针继续遍历···(4)
        }
    }
    
    // 运行到此处,一定有且仅有一个链表非空
    if (A->next != NULL){       // 若链表 A 还未空
        while (pa->next != NULL)// 先找到链表 A 的尾部
            pa = pa->next;
        headNext = head->next;  // 记录新链表头结点的后继指针
        head->next = A->next;   // 新链表尾部直接插入链表 A 的剩余部分
        pa->next = headNext;
    }
    if (B->next != NULL){       // 若链表 B 还未空
        while (pb->next != NULL)// 先找到链表 B 的尾部
            pb = pb->next;
        headNext = head->next;  // 记录新链表头结点的后继指针
        tail->next = B->next;   // 新链表尾部直接插入链表 B 的剩余部分
        pb->next = headNext;
    }
        
    free(A); // 删除链表 A 的头结点
    free(B); // 删除链表 B 的头结点
    return C;
}

5 拆分链表

5.1 不带头结点的单链表

设 C = {a1, b1, a2, b2, a3, b3,···}为线性表,采用不带头结点的单链表存放,设计一个就地算法,将其拆分为两个线性表,使得 A = {a1, a2, ···, an}, B = {bn,···,b2, b1}。

思路:链表 A 使用尾插法,链表 B 使用头插法。

void Split (LinkList &C){
    LinkNode *headC, *headNextC, *headNextNextC; // headC 指向链表 C 的第二个元素,将其当做头结点
    LinkNode *tailA, *headB; // 链表 A 的尾指针,链表 B 的头指针
    LinkNode *A, *B;
    bool flag = true;
    
    // 首先单独处理链表 C 的第一个和第二个元素,将问题转化为“带头结点的单链表”
    A = (LinkNode *) malloc(sizeof(LinkNode));
    B = (LinkNode *) malloc(sizeof(LinkNode));
    A->data = C->data;
    A->next = NULL;
    B->data = C->next->data;
    B->next = NULL;
    tailA = A;
    headB = B;
    
    headC = C->next->next; // headC 指向第二个元素,将其当做头结点
    while (headC->next != NULL){
        headNextC = headC->next;         // 链表 C 的第三个元素
        headNextNextC = headNextC->next; // 链表 C 的第四个元素
        headC->next = headNextNextC;     // 将链表 C 的第三个元素脱离出来
        
        if (flag == true){      // 轮到 an
            tailA->next = headNextC;    // 尾插入到链表 A
            tailA = headNextC;
            flag = false;
        }
        else{                   // 轮到 bn
            headNextC->next = headB;    // 头插入到链表 B
            headB = headNextC;
            flag = true;
        }
    }
    
    free(C->next);
    free(C);
}

5.2 带头结点的单链表

设 C= {a1, b1, a2, b2, a3, b3,···} 为线性表,采用带头结点的单链表存放,设计一个就地算法,将其拆分为两个线性表,使得 A = {a1, a2, ···, an}, B = {bn,···,b2, b1}。

思路:链表 A 使用尾插法,链表 B 使用头插法。

void Split (LinkList &C){
    LinkNode *headC, *headNextC; // 链表 C 的第一个元素及其后继结点,链表 C 的第一个元素也是待插入元素
    LinkNode *tailA, *headB, *headNextB; // 链表 A 的尾指针,链表 B 的第一个元素及其后继结点
    LinkNode *A, *B;
    bool flag = true;
    
    A = (LinkNode *) malloc(sizeof(LinkNode));
    B = (LinkNode *) malloc(sizeof(LinkNode));
    A->next = NULL;
    B->next = NULL;
    tailA = A;
    headB = B;
    
    while (C->next != NULL){
        headC = C->next;        // 链表 C 的第一个元素
        headNextC = headC->next; // 链表 C 的第二个元素
        C->next = headNextC;    // 将链表 C 的第一个元素脱离出来
        
        if (flag == true){      // 轮到 an
            tailA->next = headC;        // 尾插入到链表 A
            tailA = headC;
            flag = false;
        }
        else{                   // 轮到 bn
            headB = B->next;            // 链表 B 的第一个元素
            headNextB = headB->next;    // 链表 B 的第二个元素
            B->next = headC;            // 头插入到链表 B
            headC->next = headNextB;
            flag = true;
        }
    }
    
    free(C);
}

6 链表对称和链表环

6.1 判断链表对称

设计一个算法用于判断带头结点的循环双链表是否对称。

bool Test (LinkList &L){
    LinkNode *p, *q;
    
    p = L->next;
    q = L->prev;
    
    // p != q 为奇数个结点的情况,检测结束时两指针会出现相遇
    // q != p->next 为偶数个结点的情况,检测结束时两指针相邻
    while ((p != q) && (q != p->next)){
        if (p->data != q->data)
            return false;
        else{
            p = p->next;
            q = q->prev;
        }
    }
    
    return true;
}

6.2 判断链表是否有环

单链表有环,是指单链表中某个节点的 next 指针域指向的是链表中在它之前的某一个节点,这样在链表的尾部形成一个环形结构。

条件:带头结点的单链表。

  • 判断是否有环:通常使用快慢指针的方法,也就是说一个每次指针走两步,一个指针每次只走一步,如果链表有环,则两个指针总会在一个节点上相遇,若无环,则不会相遇。
bool Ring (LinkNode &L){
    LinkNode *slow, *fast;
    
    slow = L->next;
    fast = L->next;
    
    while ((fast != NULL) &&  (fast->next != NULL)){
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
            return true;
    }
    
    return false;
}
  • 返回环的开始结点:已知单链表有环的情况下,找到环开始的位置。当快慢指针相遇时,让其中一个指针指向头结点,然后让两个节点以相同的速度前进,再次相遇时所在的节点位置就是环开始的位置。
LNode *Ring (LinkNode &L){
    LinkNode *slow, *fast;
    
    slow = L->next;
    fast = L->next;
    
    while ((fast != NULL) &&  (fast->next != NULL) && (slow != fast)){
        slow = slow->next;
        fast = fast->next->next;
    }
    
    if ((slow == NULL) || (fast->next == NULL))
        return NULL;
    
    fast = L->next;
    
    while (fast != slow){
        fast = fast->next;
        slow = slow->next;
    }
    return fast;
}

标签:结点,NULL,nextNode,next,链表,算法,数据结构,LinkNode
From: https://www.cnblogs.com/Mount256/p/16866467.html

相关文章

  • 基础算法篇——双指针算法
    基础算法篇——双指针算法本次我们介绍基础算法中的双指针算法,我们会从下面几个角度来介绍:双指针简介双指针基本使用最长连续不重复字符列数组元素的目标和判断子序......
  • 粒子群算法
    一种智能算法,其思想就是在一个粒子群中,利用历史最优和当前种群的最优值来在一定程度上影响当前种群的决策对于每个粒子,有2个参数:位置X和速度V每次更新:X=X+V;V=V+r......
  • 扩展欧几里得算法
    题目:#include<bits/stdc++.h>usingnamespacestd;intexgcd(inta,intb,int&x,int&y){if(b==0){x=1,y=0;returna;}intx1,y1......
  • 数据机构 最小生成树(Prim算法(普里姆、Kruskal算法( 克鲁斯卡尔))
    8.8、最小生成树连通图的生成树是包含图中全部顶点的一个极小连通子图;若图中顶点数为n,则它的生成树含有\(n-1\)条边。最小生成树对于一个带权连通无向图G=(V,E),生成树......
  • 非对称加密及RSA算法
    非对称加密及RSA算法 最近在学习区块链相关的知识,发现其保证去中心化的一个重要的手段就是基于密码学中的非对称加密。何为非对称加密?在回答这个问题之前,我觉得有比说一下......
  • 『数据结构与算法』解读递归算法!
    文章目录​​一.什么是递归​​​​1.1.递归的定义​​​​1.2.何时使用递归​​​​1.3.递归模型​​​​二.递归算法的设计​​​​2.1.递归算法设计的步骤​​​​......
  • 数据结构 玩转数据结构 6-7 二分搜索树的中序遍历和后续遍历
    0课程地址https://coding.imooc.com/lesson/207.html#mid=13460 1重点关注1.1什么是中序遍历和后续遍历中序遍历:就是先遍历左节点,再遍历根......
  • 实验二:逻辑回归算法实验
    实验二:逻辑回归算法实验【实验目的】1.理解逻辑回归算法原理,掌握逻辑回归算法框架;2.理解逻辑回归的sigmoid函数;3.理解逻辑回归的损失函数;4.针对特定应用场景及数据,能......
  • 实验二:逻辑回归算法实验
    实验二:逻辑回归算法实验班级:20大数据(3)班学号:201613341【实验目的】理解逻辑回归算法原理,掌握逻辑回归算法框架;理解逻辑回归的sigmoid函数;理解逻辑回归的损失函数;......
  • 实验二:逻辑回归算法实验
    【实验目的】1.理解逻辑回归算法原理,掌握逻辑回归算法框架;2.理解逻辑回归的sigmoid函数;3.理解逻辑回归的损失函数;4.针对特定应用场景及数据,能应用逻辑回归算法解决实际分......