LeetCode & 牛客 | 单链表相关习题

N0.203 删除链表元素

题目链接:移除链表元素

题目描述: 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回新的头节点

示例 1:

在这里插入图片描述

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [ ], val = 1
输出:[ ]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[ ]

提示:

列表中的节点数目在范围 [0, 104] 内
1 <= Node.val <= 50
0 <= val <= 50

解法1:暴力求解

思路:

  • 遍历整个数组
  • 找到与val值相同的节点
  • 将该节点的前一个节点与后一个节点连接起来

1.定义一个局部结构体类型的变量,利用此变量遍历数组,直到遇到NULL停止循环
2.如果遍历到的值与val相同,分两种情况:(1)头删(2)其他位置删除
3.如果遍历到的值与val不同,继续向下遍历
注意:1.要额外考虑头删的情况
2.判断是头删的条件是cur == head
3.要注意释放要删除的节点
4.还需要找到前一个节点

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode* prev = NULL;
    struct ListNode* cur = head;

    while (cur)
    {
        if (cur->val == val)
        {
            if (cur == head)//头删
            {
                head = head->next;
                free(cur);
                cur = head;
            }
            else
            {
                prev->next = cur->next;
                free(cur);
                cur = prev->next;
            }

        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }
    return head;
 }

解法2:创建一个新的节点

思路:

  • 遍历整个数组
  • 找到与val值不同的节点,链接在新节点上
  • 如果与val值相同跳过该节点

1.定义一个局部结构体类型的变量cur,利用此变量遍历数组,直到遇到NULL停止循环
2.定义前一个节点prev,方便链接,并且不断迭代
2.如果遍历到的值与val不同,将该节点链接在新的节点后面
3.如果遍历到的值与val相同,跳过该节点,并释放
注意:1.需要定义三个新的结构体类型指针变量,一个用来存头部位置,方便遍历,一个用来创建新节点,最后一个用来记录当前节点的前一个节点,方便链接
2.第一次将不等于val的节点链接需要特殊考虑
3.要注意将是val的值进行释放
4.若最后一个值是val,要特别注意要将最后一个节点的next置为空,若不是val,它会自己迭代为空

在这里插入图片描述

struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode* cur = head;
    struct ListNode* newhead = NULL;
    struct ListNode* prev = NULL;


    while (cur)
    {
        if (cur->val != val)
        {
            if (newhead == NULL)
            {
                newhead = cur;
                //cur = cur->next;
                prev = newhead;
            }
            else
            {
                prev->next = cur;
                //cur = cur->next;
                prev = prev->next;
            }
            cur = cur->next;

        }
        else
        {
            struct ListNode* del = cur;
            cur = cur->next;
            free(del);
        }
    }
    if (prev != NULL)
        prev->next = NULL;
    return newhead;

}

解法3:利用哨兵节点

哨兵节点:创建一个新的节点,不存储有效数据
若没有哨兵节点,开始的head是指向NULL,有了哨兵节点,head指向哨兵节点

思路:

  • 创建哨兵节点
  • 将哨兵节点的next与下一个节点链接

1.定义一个局部结构体类型的变量,利用此变量遍历数组,直到遇到NULL停止循环
2.如果遍历到的值与val不同,链接到哨兵节点
3.如果遍历到的值与val相同,释放该节点,继续向下遍历
注意:1.此时不需要判断是否为第一次链接,因为已经有一个节点
2.要注意释放要删除的节点
3.可以返回guard->next,也可以将头指向哨兵节点的下一个位置,释放哨兵节点,返回头节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode* cur = head;
    struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* tail = guard;
    while (cur)
    {
        if (cur->val != val)
        {
            tail->next = cur;
            tail = tail->next;
            cur = cur->next;
        }
        else
        {
            struct ListNode* del = cur;
            cur = cur->next;
            free(del);
        }

    }
    if (tail != NULL)
        tail->next = NULL;
    return guard->next;
    //head = guard->next;
    //free(guard);
    //return head;
}

N0.21 合并两个有序链表

题目链接:合并两个有序链表

题目描述: 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的

示例 1:
在这里插入图片描述

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [ ], l2 = [ ]
输出:[ ]

示例 3:

输入:l1 = [ ], l2 = [0]
输出:[0]

提示:

两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列

解法1:暴力求解

思路:

  • 只要cur1与cur2不等于NULL就继续遍历
  • 判断大小值,小的链接在后面
  • 要考虑到cur1或cur2先结束的情况

1.定义变量cur1和cur2,利用此变量遍历数组,直到其中之一或同时遇到NULL停止循环,定义新链表节点newlist,定义prev不断迭代指向前一个节点
2.判断大小值,谁小谁链接在后面
3.当短的执行完成,长的链表要继续链接在后面
注意:1.插入的时候要考虑是否第一次为空的情况
2.cur1和cur2可能长短不一,短的执行完,要利用if将长的继续链接,可直接链接在后面,不用迭代
3.注意对代码进行优化

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* cur1 = list1;
    struct ListNode* cur2 = list2;
    struct ListNode* newlist = NULL;
    struct ListNode* prev = NULL;

    
    while(cur1 && cur2)
    {
        if(cur1->val<=cur2->val)
        {
            if(newlist==NULL)
            {
                newlist = cur1;
                //cur1 = cur1->next;
                prev = newlist;
            }
            else
            {
                prev->next = cur1;
                //cur1 = cur1->next;
                prev = prev->next;
            }
            cur1 = cur1->next;
        }
        else
        {
            if(newlist==NULL)
            {
                newlist = cur2;
                //cur2 = cur2->next;
                prev = newlist;
            }
            else
            {
                prev->next = cur2;
                //cur2 = cur2->next;
                prev = prev->next;
            }
            cur2 = cur2->next;
        }
    }
    if(cur1 != NULL)
    {
        if(newlist==NULL)
        {
            newlist = cur1;
            //cur1 = cur1->next;
            //prev = newlist;
        }
        else
        {
            prev->next = cur1;
            //cur1=cur1->next;
            //prev = prev->next;
        }

    }
    if(cur2 != NULL)
    {
        if(newlist==NULL)
        {
            newlist = cur2;
            //cur2 = cur2->next;
            //prev = newlist;
        }
        else
        {
            prev->next = cur2;
            //cur2=cur2->next;
            //prev = prev->next;
        }
    }    
    return newlist;
}

解法2:利用哨兵节点

思路:

  • 创建哨兵节点
  • 将val较小的节点链接在后面
  • 两个链表有可能长短不一

1.定义变量cur1和cur2,利用此变量遍历数组,直到其中之一或同时遇到NULL停止循环,定义哨兵节点guard,定义tail不断迭代
2.判断大小值,谁小谁链接在哨兵节点后面
3.当短的执行完成,长的链表要继续链接在后面
注意:1.不用考虑节点第一次是否为空的情况
2.cur1和cur2可能长短不一,短的执行完,要利用if将长的继续链接,可直接链接在后面,不用迭代
3.注意对代码进行优化
4.也可以将前两个if注释,用注释的部分返回head

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* cur1 = list1;
    struct ListNode* cur2 = list2;
    struct ListNode* guard = (struct ListNo0de*)malloc(sizeof(struct ListNode));
    struct ListNode* tail = guard;
     //guard->next  = NULL;
    
    if(list1==NULL)
        return list2;
    if(list2==NULL)
        return list1;
    
    while(cur1 && cur2)
    {
        if(cur1->val<=cur2->val)
        {
            tail->next = cur1;
            cur1 = cur1->next;
        }
        else
        {
            tail->next = cur2;
            cur2 = cur2->next;
        }
        tail = tail->next;
    }
    if(cur1 != NULL)
    {
        tail->next  =cur1;
    }
    if(cur2 != NULL)
    {
        tail->next  =cur2;
    }
    
//    struct ListNode* head =  guard->next;
//    free(guard);
//    return head;
    return guard->next;
}

N0.206 反转链表

题目链接:反转链表

题目描述: 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:
在这里插入图片描述

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = [ ]
输出:[ ]

提示:

链表中节点的数目范围是 [0, 5000]
-5000 <= Node.val <= 5000

解法1:迭代法

思路:

  • 遍历
  • 从头将每个节点链接到新head上

1.定义cur遍历所有节点,将节点逐个链接到newhead上
2.定义next保存下一个节点的位置
1.要不断的修改cur->next的位置,与以前的断开,不断链接到新的节点位置,cur->next = newhead 相当于把链接关系反过来,由->变为<-

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* cur  =head;
    struct ListNode* newhead = NULL;
    struct ListNode* next =NULL;
    while(cur)
    {
            next = cur->next;
            cur->next = newhead;
            newhead = cur;
            cur = next;     
    }
    
    return newhead;
}

解法2:双指针法

思路:

与解法1的方法本质相同,不过换了个思路,本质还是改变了指针的指向

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head){
    
    //双指针法
    struct ListNode* next = NULL;
    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    
    while(cur)
    {
        next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }
    
    return prev;
}

N0.206 链表的中间节点

题目链接:链表的中间节点

**题目描述:**给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

提示:

给定链表的结点数介于 1 和 100 之间。

解法1:求长度

思路:

  • 定义长度len,遍历所有节点求出总长度
  • len/2,再从头递减,找到中间节点并返回

1.要定义两个头节点,方便两次对节点进行遍历
2.第一次遍历求出总的长度,第二次遍历求出中间位置的节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* middleNode(struct ListNode* head){
    
    struct ListNode * cur1 = head;
    struct ListNode * cur2 = head;
    int len = 0;
    while(cur1)
    {
        len ++;
        cur1 = cur1->next;
    }
    len/=2;
    while(len--)
    {
        cur2 = cur2->next;
    }
    return cur2;
}

解法2:快慢指针

若只能遍历链表一遍
思路:

  • 快指针一次走2步
  • 慢指针一次走1步
  • 这样保证快指针走到最后,慢指针走到中间

节点分为奇数个和偶数个,偶数个fast->next=NULL停止,奇数个时fast=NULL停止
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* middleNode(struct ListNode* head){
    //快慢指针
    struct ListNode* fast  = head;
    struct ListNode* slow = head;
    
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}

链表中倒数第K个节点

题目链接:链表中倒数第k个节点

题目描述: 输入一个链表,输出该链表中倒数第k个结点。

示例 1:

输入:1,{1,2,3,4,5}
返回值:{5}

解法 :快慢指针

思路:

  • 利用快慢指针

1.定义fast和slow两个变量,fast比slow多走k步,然后再同时走,直到fast为空
2.有n个节点时,要考虑k>n,k<n,k=n,三种情况
在这里插入图片描述

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 * @param pListHead ListNode类 
 * @param k int整型 
 * @return ListNode类
 */
struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) {
    struct ListNode* fast ,*slow;
    fast = slow = pListHead;
    
    while(k--)
    {
        if(fast == NULL)//只有k>n需要特殊判断
            return NULL;
        fast = fast->next;
    }
 
    while(fast)
    {
        slow = slow->next;
        fast = fast->next;
    }
    return slow;
}

链表分割

题目链接:链表分割

题目描述: 现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

解法:哨兵节点

思路:

  • 哨兵节点
  • 遍历

1.创建两个哨兵节点,这样插入的时候就不用判读是否为空
2.需要两个变量记录尾部
3.要注意特殊情况,所有值大于x,所有小于x,空链表,最后需要将great->next置NULL

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
        struct ListNode* lessMove ,*greaterMove,*lessHeadGuard,*greaterHeadGuard;
        lessMove = lessHeadGuard = (struct ListNode*)malloc(sizeof(struct ListNode));
        greaterMove = greaterHeadGuard =(struct ListNode*)malloc(sizeof(struct ListNode));
        struct ListNode* cur = pHead;
        
        lessHeadGuard->next = NULL;
        greaterHeadGuard->next = NULL;
        while(cur)
        {
            if(cur->val < x)
            { 
                lessMove->next = cur;
                lessMove = lessMove->next;
            }
            else
            {
                greaterMove->next = cur;
                greaterMove = greaterMove->next;
            }
            cur = cur->next;
        }
        
        lessMove->next = greaterHeadGuard->next;
        greaterMove->next = NULL;
        
        return lessHeadGuard->next;
    }
};

链表的回文结构

题目链接:链表的回文结构

题目描述: 对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。
给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900

示例 1:

1->2->2->1
返回:true

思路:

  • 先找到中间节点
  • 逆置
  • 判断

1.使用上述题目中的求中间节点和反转链表函数
2.找到中间节点后逆置,再比较

在这里插入图片描述

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    
    struct ListNode* middleNode(struct ListNode* head){
    //快慢指针
    struct ListNode* fast  = head;
    struct ListNode* slow = head;
    
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}
    
    struct ListNode* reverseList(struct ListNode* head){
    
    //双指针法
    struct ListNode* next = NULL;
    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    
    while(cur)
    {
        next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }
    
    return prev;
}
    
    bool chkPalindrome(ListNode* A) {
        // write code here
        struct ListNode* mid = middleNode(A);
        struct ListNode* rmid = reverseList(mid);
        
        while(A && rmid)
        {
            if(A->val != rmid->val)
                return false;
            A=A->next;
            rmid = rmid->next;
        }
        return true;
        
    }
};

相交链表

题目链接:相交链表

题目描述: 给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
在这里插入图片描述
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。

示例 1:
在这里插入图片描述

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:
在这里插入图片描述

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at ‘2’
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:
在这里插入图片描述

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。

提示:

listA 中节点数目为 m
listB 中节点数目为 n
1 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 没有交点,intersectVal 为 0
如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]

解法:快慢指针

1.求两个节点的长度,根据长度做差
2.让长的链表先走差值步,然后再同时走,直到相遇,相遇点就是链表相交的节点

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headA==NULL || headB==NULL)
    	return NULL;
    	
    struct ListNode * curA = headA;
    struct ListNode * curB = headB;
    
    //求长度
    int lenA = 1;
    while(curA->next)
    {
        curA = curA->next;
        lenA++;
    }
    int lenB = 1;
    while(curB->next)
    {
        curB = curB->next;
        lenB++;
    }
    if(curA != curB)
    {
        return NULL;
    }
    //求差值
    int len = abs(lenA-lenB);
    
    struct ListNode * longlist = headA;
    struct ListNode * shortlist = headB;
    
    if(lenA<lenB)
    {
        longlist = headB;
        shortlist = headA;
    }
    //
    while(len--)
    {
        longlist = longlist->next;
    }
    while(longlist != shortlist)
    {
        longlist = longlist->next;
        shortlist = shortlist->next;
    }
    
    return longlist;
}

环形链表

题目链接:环形链表

题目描述: 给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:
在这里插入图片描述

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

示例 2:
在这里插入图片描述

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点

示例 3:
在这里插入图片描述

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:
链表中节点的数目范围是 [0, 104]
-105 <= Node.val <= 105
pos 为 -1 或者链表中的一个 有效索引 。

解法:快慢指针

1.对于环形链表,是无法判断其长度的,只会陷入死循环
2.利用快慢指针,同时走,不过快指针一次走两步,慢指针一次走一步,当慢指针进环后,转变成快指针追赶慢指针,每次减少一步,最后追赶上
3.如果没有环,分为奇数和偶数,比如 1 2 3 4 ,利用fast==NULL即可判断,如果是奇数,比如 1 2 3,则需要利用fas->next 判断

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    
    struct ListNode *fast=head;
    struct ListNode *slow=head;
    
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        
        if(fast==slow)
        {
            return true;
        }
    }

        return false;
}

问题扩展

为什么快指针一次走两步,慢指针一次走一步能追上?
假设链表带环,两个指针最后都会进入环中。当慢指针进入环中,最坏的情况是快指针在慢指针前面,两个指针挨着。此时,指针每移动一次,两个指针之间的距离就缩小一步,假设之间差的是n步,n-1,n-2……,2,1,0,由于每次减小一步,最后会减小至0相遇

在这里插入图片描述
如果慢指针一次走1步,快指针一次走3步,能追上吗?
答案是不一定

在这里插入图片描述
因此,每次只要缩小1步,不用管奇偶,便可相遇

环形链表Ⅱ

题目链接:环形链表Ⅱ
题目描述: 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表

示例 1:
在这里插入图片描述

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

示例 2:
在这里插入图片描述

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

示例 3:
在这里插入图片描述

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:
链表中节点的数目范围在范围 [0, 104] 内
-105 <= Node.val <= 105
pos 的值为 -1 或者链表中的一个有效索引

解法 1:公式法

在这里插入图片描述
根据距离,fast走的距离=2*slow走的距离
假设进环前走的距离是L,环的节点长度是C,入口点到相遇点的距离是x,
slow走的距离是L+x,(为什么不是L+n * C,因为当slow入环时,与fast之间的距离最大就是c-1,slow和fast每次走,就会减少1步,距离逐渐减小,肯定在1圈内追上),fast走的距离是L+n * C+x(n是圈数)
根据公式L+n * C+x=2(L+x),最后化简得L=(N-1)*C+C-X
结论:A指针从头走,B指针从相遇点走,会在环的入口点相遇

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    
    if(head==NULL)
    	return NULL;
    struct ListNode *fast = head;
    struct ListNode *slow =head;
    
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        
        if(slow == fast)
        {
            struct ListNode *meet  = slow;
            while(meet != head)
            {
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    return NULL;
    
}

解法 2:转换成相交问题

将相遇点记录后,把相遇点与下一节点断开,将下一节点作为链表B的头,链表A从头开始,通过相交链表的方法来做
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    if(headA==NULL || headB==NULL)
    	return NULL;
    	
    struct ListNode * curA = headA;
    struct ListNode * curB = headB;
    
    //求长度
    int lenA = 1;
    while(curA->next)
    {
        curA = curA->next;
        lenA++;
    }
    int lenB = 1;
    while(curB->next)
    {
        curB = curB->next;
        lenB++;
    }
    if(curA != curB)
    {
        return NULL;
    }
    //求差值
    int len = abs(lenA-lenB);
    
    struct ListNode * longlist = headA;
    struct ListNode * shortlist = headB;
    
    if(lenA<lenB)
    {
        longlist = headB;
        shortlist = headA;
    }
    //
    while(len--)
    {
        longlist = longlist->next;
    }
    while(longlist != shortlist)
    {
        longlist = longlist->next;
        shortlist = shortlist->next;
    }
    
    return longlist;
}
struct ListNode *detectCycle(struct ListNode *head) {
    //相交法
    struct ListNode *fast = head;
    struct ListNode *slow =head;
    
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        
        if(slow == fast)
        {
            struct ListNode *meet  = slow;
             struct ListNode *next = meet->next;
            meet->next = NULL;
            return getIntersectionNode(head,next);
            /*struct ListNode * entryNode = getIntersectionNode(head,next);
            //恢复节点
            meet->next = next;
            return entryNode*/
         }
        }
    return NULL;
}

复制带随机指针的链表

题目链接:复制带随机指针的链表

题目描述: 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
 例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
 返回复制链表的头节点。
 用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。

  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。

你的代码 只 接受原链表的头节点 head 作为传入参数。

示例 1:
在这里插入图片描述

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:
在这里插入图片描述

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]

示例 3:
在这里插入图片描述

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]

提示:

  • 0 <= n <= 1000
  • -104 <= Node.val <= 104
  • Node.random 为 null 或指向链表中的节点。

解法:拷贝

1.先将原节点拷贝出来,链接在其后面
2.将新拷贝的节点的random更新,copy->random = cur->random->next
3.将拷贝的节点解除,并链接在一起形成新链表,恢复原链表

在这里插入图片描述

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */

struct Node* copyRandomList(struct Node* head) {
	
    struct Node* cur = head;
    struct Node* copy =NULL;
    struct Node* next = NULL;
     //复制-链接节点
    while(cur)
    {
        next = cur->next;
        copy = (struct Node*)malloc(sizeof(struct Node));
        
        copy->val = cur->val;
        cur->next = copy;
        copy->next = next;
        
        cur = next;
    }
    //更新random
    cur = head;
    while(cur)
    {
        copy = cur->next;
        if(cur->random==NULL)
            copy->random=NULL;
        else
        {
            copy->random = cur->random->next;
        }
        cur = copy->next;
    }
    
    //解除链接
    struct Node* guardHead = (struct Node*)malloc(sizeof(struct Node));
     guardHead->next = NULL;
    struct Node* guardtail = guardHead;
    cur = head;
    while(cur)
    {
        copy = cur->next;
        next = copy->next;
        
        //链接新链表
        guardtail->next = copy;
        guardtail = guardtail->next;
        
        //恢复原链表
        cur->next = next;
        //迭代
        cur = next;
    }
    return guardHead->next;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值