大数据最全【数据结构与算法】链表2W字终极无敌总结,大数据开发程序员春招三面蚂蚁金服

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

  • l1l2 均按 非递减顺序 排列

思路: 采用归并的思想,新建一个头结点,两个链表节点依次比较,哪个小哪个连接,并将小的对应的链表向下一个节点。

struct ListNode\* mergeTwoLists(struct ListNode\* list1, struct ListNode\* list2){
    if(list1 == NULL)
        return list2;
    if(list2 == NULL)
        return list1;
    struct ListNode\* cur1 = list1;
    struct ListNode\* cur2 = list2;
    struct ListNode\* newHead = (struct ListNode\*)malloc(sizeof(struct ListNode));
    newHead->next = NULL;
    struct ListNode\* tail = newHead;
    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)
        tail->next = cur1;
    if(cur2)
        tail->next  =cur2;
    return newHead->next;
}

3.7 分割链表

面试题 02.04. 分割链表

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你需要 保留 每个分区中各节点的初始相对位置。(将题干改成了需要保留,原题为不需要保留)

示例 1:

img

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

示例 2:

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

提示:

  • 链表中节点的数目在范围 [0, 200]
  • -100 <= Node.val <= 100
  • -200 <= x <= 200

思路:通过创建两个新的头节点(哨兵位)将此链表进行按照条件进行归类,并分别记录尾部,归类之后将两个链表进行连接,最后free掉两个节点

struct ListNode\* partition(struct ListNode\* head, int x){
    struct ListNode\* less = (struct ListNode\*)malloc(sizeof(struct ListNode));
    struct ListNode\* large = (struct ListNode\*)malloc(sizeof(struct ListNode));
    less->next=NULL;
    large->next=NULL;
    struct ListNode\* lesstail=less;
    struct ListNode\* largetail=large;
    struct ListNode\* cur=head;
    while(cur)
    {
        if(cur->val<x)
        {
            lesstail->next=cur;
            lesstail=lesstail->next;
        }
        else
        {
            largetail->next=cur;
            largetail=largetail->next;
        }
        cur=cur->next;
    }
    largetail->next=NULL;
    lesstail->next=large->next;
    head=less->next;
    free(less);
    free(large);
    large=less=NULL;
    return head;
}

3.8 回文链表

234. 回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false

示例 1:

img

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

示例 2:

img

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

提示:

  • 链表中节点数目在范围[1, 105]
  • 0 <= Node.val <= 9

进阶: 你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

解题步骤:对此,总结的一句话就是,不能重复造轮子(能偷懒就偷懒),我们可以利用上面的中间节点+反转链表进行操作:

即: 找到中间节点之后将前后分割开并对后半部分进行反转,对这两个链表节点的数据域的val进行一一对比,不管原本链表是奇数还是偶数长度,分割开即便是一奇一偶,判断时若有一个链表迭代到空(此时一奇一偶),即为回文链表,因为中间的数本来也只有一个。

注:即便分割成两个链表后不将前半部分的链表最后指向空,一样可以判断,因为前半部分最后指向的肯定是原本中间的那个,即后半链表反转之后的最后一个。此代码即为此。

在这里插入图片描述

//反转链表:
struct ListNode\* reverseList(struct ListNode\* head) {
    if (head == NULL || head->next == NULL)
        return head;
    struct ListNode\* prev = NULL, \* cur = head, \* next = head->next;
    while (cur)
    {
        //反转链表
        cur->next = prev;
        //迭代
        prev = cur;
        cur = next;
        if (next)
            next = next->next;
    }
    return prev;
}
//链表的中间节点:
struct ListNode\* middleNode(struct ListNode\* head){
    if(head->next == NULL)
        return head;
    struct ListNode\* slow = head;
    struct ListNode\* fast = head;
    while(fast&&fast->next)//&&的特点,上一个不成立,下一个条件不判断,不会出现野指针的情况
    {
        slow = slow->next;
        fast = fast->next->next;
    }
    return slow;
}
bool isPalindrome(struct ListNode\* head){
    struct ListNode\* mid = middleNode(head);
    struct ListNode\* rmid = reverseList(mid);
    while(head && rmid)
    {
        if(head->val != rmid->val)
            return false;

        head = head->next;
        rmid = rmid->next;

    }
    return true;
}

在这里插入图片描述

但这时会有小伙伴们想到,直接反转链表并将此链表与反转后的一一对比是不是也可以?答案是否定的,因为反转此链表之后,原本的链表内部同样会反转,相当于自己与自己比较,无论是不是回文结构,都会返回true,因此,若想用这个方法,必须在通过开辟节点或者用数组存储原本数据与其反转之后的进行比较,但不论是开辟新节点还是用数组,都是很挫的方式,若有别的方法解决,就不要用此方法。

3.9 相交链表

160. 相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

图示两个链表在节点 c1 开始相交

img

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headAheadB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案

示例 1:

img

输入: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:

img

输入: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:

img

输入: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
  • 如果 listAlistB 没有交点,intersectVal0
  • 如果 listAlistB 有交点,intersectVal == listA[skipA] == listB[skipB]

进阶: 你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

对于此题,直接想到的方法就是暴力求解,即每个节点都用一个完整的链表扫描,必然会扫描到(这种方式过于麻烦呜呜呜)。但,由于作者本着:没有优解,博客不写的原则,那么开始介绍优解的方法:

思路: 通过题干不难发现,由于不知道两个链表到相交位置的距离,我们无法利用两个指针去寻找,但是如果两个链表的起始位置到相交节点的距离相同,我们就可以用两个指针进行一起一步一步的前进,他们必然会相遇。可惜的是,两个链表的起始位置到相交节点的距离也不一定就相同呀,那么现在的问题就变成了如何让两个指针到交点的距离相同

在这里插入图片描述

当我们从后往前看的时候,不难发现,两个链表的长度差1,并且,我们发现,若是让下面的链表的指针从b2开始扫描,那么就符合我们上述所提到的:距离相同 。这个时候,你一定会发觉并且猜想到,让长的链表先移动两个链表长度之差的长度 ,就可以让其并行,即到相交位置的距离相同。想到这里,这道题目,也就迎刃而解了。(恭喜恭喜)

经过一系列的揣测与分析加上有点蒙题的猜想,那就……可以上代码了:

struct ListNode \*getIntersectionNode(struct ListNode \*headA, struct ListNode \*headB) {
    if(headA == NULL ||headB == NULL)
        return NULL;

    struct ListNode\* curA = headA, \*curB = headB;
    int lenA = 1;
    //找尾节点
    while(curA)
    {
        curA = curA->next;
        ++lenA;
    }
    int lenB = 1;
    while(curB)
    {
        curB = curB->next;
        ++lenB;
    }
    struct ListNode\* longList = headA, \*shortList = headB;
    if(lenA<lenB)
    {
        longList = headB;
        shortList = headA;
    }
    //长的链表先走差距步
    int gap = abs(lenA-lenB);
    while(gap--)
    {
        longList = longList->next;
    }
    while(longList!=shortList)
    {
        longList = longList->next;
        shortList  = shortList->next;
    }
    return longList;
}

那么对其进行相交与不相交的情况的归类,发现最后一个例子仅仅是一个链表也可以看成为相交链表(不禁让想象变得丰富起来)

在这里插入图片描述

4. 链表成环问题

对此,也有相应的oj题,但为什么还要单拿出来进行描述呢,由于这里的推导较多,为了让文章看着不混乱,选择给他一个大标题进行单独描述。

4.1 给定一个链表,判断链表中是否有环

由于有扩展问题,我们先解决题目:

LeetCode.41. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

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

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

img

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

示例 2:

img

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

示例 3:

img

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

提示:

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

进阶: 你能用 O(1)(即,常量)内存解决此问题吗?

对于成环问题,如果是环,那么链表迭代的过程中不会截止,但是我们不能根据是否截止进行判断是不是环,这样只会运行超时,因此,需要采用一定的特殊技巧:

  • 利用快慢指针,即快指针一次走两步,慢指针一次走一步,当慢指针进入环后,转化思想为快指针追赶慢指针:根据相对运动,每次移动快指针都会离慢指针更进一步,这就使得二者一定会在圈中相遇。即为真。
  • 如果不是环,快指针一定先走到末端。
bool hasCycle(struct ListNode \*head) {
    if(head == NULL)
        return false;
    struct ListNode\* slow = head,\*fast = head;
    while(fast && fast->next)//一次走两步,防止出现野指针需要判断两个条件
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            return true;
        }
    }
    return false;
}

【扩展问题】

  • 为什么快指针每次走两步,慢指针走一步可以?
    假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。
    在这里插入图片描述
  • 快指针一次走3步行吗?
    • 不一定。慢指针在进圈后,快指针以相对慢指针两个单位两个单位的追赶,如果N为奇数(N代表两个指针之间的距离),距离就会变成:N-2,N-4,……3,1,-1。当变成-1时,又是一个新的开始,此时二者相距C-1个长度,C为环的周长,如果c-1是奇数,那么就永远不会相遇,因此不一定。
  • 快指针一次走M步行吗?
    • 对此,可与一次走三步的类似,需要看N与C的关系。
  • 慢指针一次走N步,快指针一次走M步行吗?(M>N)
    • 只要是在环中,并且M比N大1个单位,那么就可以认为快指针相对慢指针靠近一步,这样相当于遍历所有可能性,一定会相遇。

总结:只要fast比slow快一步,无论他们一次走几个单位,都一定可以相遇。

4.2 返回入环的第一个结点

对于这种类型的,先证明一下无疑是最好的学习方式:

在这里插入图片描述

假设进环前的长度是L,假设环的长度是C,假设入口点到相遇点距离为X

  1. 公式推导:

fast走的距离 = 2*slow走的距离;

slow走的距离:L+X;

fast走的距离:L+N*C+X;(fast转了N圈,N>=1)

注: 为什么slow走的不是L+n*C+X呢? 即为什么slow在圈里一定走了不到一圈就相遇了呢?我们知道当slow刚刚进圈时slow与fast之间的距离一定小于C-1,fast一次走两步,slow一次走一步,距离逐渐减小,即一定走了小于C-1次就会相遇,因此推出此时slow走了不到一圈。

即:根据二倍关系:2(L+X) = L+X+N*C,即L = N * C - X;进一步得出:

L

=

(

N

1

)

C

C

X

L = (N-1)*C+C-X

L=(N−1)∗C+C−X
结论:一个指针A从头开始走,一个指针B从相遇点开始走,他们会在入口点相遇

  1. 转化成相交问题

当我们通过快慢指针找到相遇点记录下来以后,可以想象把此相遇节点与下一节点断开,记录下一个节点为链表B的头,并记录起始位置为链表A的头,这样通过相交链表的方法,就能求得入环的第一个节点,也就是链表的第一个交点

在这里插入图片描述

那么我们可以尝试解决这道题目:

142. 环形链表 II

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

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

不允许修改 链表。

示例 1:

img

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

示例 2:

img

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

示例 3:

img

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

提示:

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

进阶: 你是否可以使用 O(1) 空间解决此题?

  • 公式推导法:
struct ListNode\* hasCycle(struct ListNode \*head) {
    if(head == NULL)
        return NULL;
    struct ListNode\* slow = head,\*fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            return slow;
        }
    }
    return NULL;
}
struct ListNode \*detectCycle(struct ListNode \*head) {
    struct ListNode\* meet = hasCycle(head);
    if(meet == NULL)
        return NULL;
    struct ListNode\* begin = head;
    while(1)
    {
        if(begin == meet)
        {
            return begin;
        }
        begin = begin->next;
        meet = meet->next;
    }
    return NULL;
}

在这里插入图片描述

  • 相交法:
struct ListNode\* hasCycle(struct ListNode \*head) {
    if(head == NULL)
        return NULL;
    struct ListNode\* slow = head,\*fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            return slow;
        }
    }
    return NULL;
}
struct ListNode \*getIntersectionNode(struct ListNode \*headA, struct ListNode \*headB) {
    if(headA == NULL ||headB == NULL)
        return NULL;

    struct ListNode\* curA = headA, \*curB = headB;
    int lenA = 1;
    //找尾节点
    while(curA)
    {
        curA = curA->next;
        ++lenA;
    }
    int lenB = 1;
    while(curB)
    {
        curB = curB->next;
        ++lenB;
    }
    struct ListNode\* longList = headA, \*shortList = headB;
    if(lenA<lenB)
    {
        longList = headB;
        shortList = headA;
    }
    //长的链表先走差距步
    int gap = abs(lenA-lenB);
    while(gap--)
    {
        longList = longList->next;
    }
    while(longList!=shortList)
    {
        longList = longList->next;
        shortList  = shortList->next;
    }
    return longList;
}

struct ListNode \*detectCycle(struct ListNode \*head) {
    struct ListNode\* meet = hasCycle(head);
    if(meet == NULL)
        return NULL;
    struct ListNode\* newheadB = meet->next;
    meet->next = NULL;//必须断开,否则在求相交链表在求长度时会死循环。
    struct ListNode\* newheadA = head;
    struct ListNode\* newmeet = getIntersectionNode(newheadA,newheadB);
    
    meet->next = newheadB;//恢复环
    return newmeet;
}

在这里插入图片描述

相信这个代码大家已经看出来了,复用的两个函数不正是判断是否有环的函数和相交链表的函数吗?只不过判断是否有环的函数的返回值稍微改了一下,因此,只要掌握思路,写出的代码一定是有联系的。

5. 复制带随机指针的链表

138. 复制带随机指针的链表

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点

例如,如果原链表中有 XY 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 xy ,同样有 x.random --> y

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0n-1);如果不指向任何节点,则为 null

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

示例 1:

img

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

示例 2:

img

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

示例 3:

img

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

提示:

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

这是一道有挑战性的题目。因此,我把他单拿出来放在这里。这道题本身的难度在于random指针,通过常规暴力的方法,我们需要的是将每一个random指针的位置给记录下来,从而当处理拷贝链表的过程中,再利用双层循环将每个特定的位置的random指向这个拷贝链表相对应的位置,但是为什么不能根据val值从而链接呢?因为val的值本身是允许重复出现的,只有通过具体位置才能锁定,因此需要创建数组来记录位置(思路清晰,实现起来繁琐,自己也是想了好久),下面的代码实现就是这样的思路:

1.暴力解决

/\*\*
 \* Definition for a Node.
 \* struct Node {
 \* int val;
 \* struct Node \*next;
 \* struct Node \*random;
 \* };
 \*/
struct Node\* BuySLTNode(struct Node\* node)
{
    struct Node\* newnode = (struct Node\*)malloc(sizeof(struct Node));
    newnode->next = NULL;
    newnode->random = NULL;
    newnode->val = node->val;
    return newnode;
}
struct Node\* copyRandomList(struct Node\* head) {

    //创建复制的链表
    struct Node\* newhead = (struct Node\*)malloc(sizeof(struct Node));
    newhead->next = NULL;
    struct Node\* ntail = newhead;
	struct Node\* cur = head;
    while(cur)
    {
        ntail->next = BuySLTNode(cur);
        ntail = ntail->next;
        cur = cur->next;
    }
   // 记录节点个数,
    cur = head;
    int n = 1;
    while(cur)
    {
        n++;
        cur = cur->next;
    }
    cur = head;

    //将位置记录到count数组里,count数组的每一个元素记录该节点random指针指向的位置
    struct Node\* repcur = cur;
    int\* count = (int\*)calloc(n,sizeof(int));//开辟数组记录每一个random指向的数据的位置
    int i = 0;
    while(cur)
    {
        int order = 0;
        while(repcur)
        {
            if(cur->random == NULL)
            {
                count[i++] = n-1;
                break;
            }
            if(cur->random == repcur)
            {
                count[i++] = order;
                break;
            }
            order++;
            repcur = repcur->next;
        }
        repcur = head;
        cur = cur->next;
    }
    i = 0;

    //通过之前的数组找到复制之后的位置
    struct Node\* newcur = newhead->next;
    struct Node\* newcur1 = newhead->next;
    while(newcur)
    {
        int j=0;
        while(newcur1)
        {
           if(j == count[i])
           {
               newcur->random = newcur1;
               break;
           }
           j++;
           newcur1 = newcur1->next;
        }
        newcur1 = newhead->next;
        newcur = newcur->next;
        i++;
    }
    free(count);
    return newhead->next;
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y6RrJJjs-1659441839958)(C:\Users\Dell\AppData\Roaming\Typora\typora-user-images\image-20220802171350667.png)]

2.在链表本身进行拷贝

再次本着无优解博客不写的原则,既然暴力的解决了,那为什么还要有优解呢? emm……这是一个很痴呆的问题,有好的方法,谁会不愿意用呢?那下面,我们就来说说这个美妙的方法:

上文提到了,难点在于random指针的拷贝,但我们又根据原链表很清晰的知道random指向的位置,因此,我们就要靠着原链表,在原链表的基础上进行改造:

img
在此基础上进行改造:
在这里插入图片描述
在每一个节点的后方,拷贝一个与该节点一模一样的节点(当然地址肯定不一样喽)即图中的copy节点插入到原链表,这样就可以对random指针进行下面操作:

copy->random = cur->random->next;//后者代表着仍然是copy的节点,因为有指向next

这样就可以完美的解决random指针的问题啦。
因此应该进行以下步骤:

  • 1.复制节点插入原链表中,并对copy的random进行赋值(关键操作)。
  • 2.将copy的节点拿出来尾插编程新的链表。
  • 3.在第二步的同时将原链表恢复原状。
/\*\*
 \* Definition for a Node.
 \* struct Node {
 \* int val;
 \* struct Node \*next;
 \* struct Node \*random;
 \* };
 \*/

struct Node\* copyRandomList(struct Node\* head) {
    //1.插入copy节点
    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;
    }
    //2.更新copy->random
    cur = head;
    while(cur)
    {
        copy = cur->next;
        if(cur->random == NULL)
            copy->random = NULL;
        else
            copy->random = cur->random->next;

        //迭代往后走
        cur = cur->next->next;
    }

    //将其copy的节点尾插,并还原原链表
    cur = head;
    struct Node\* copyhead = NULL;
    struct Node\* copytail = copyhead;
    while(cur)
    {
        copy = cur->next;
        next = copy->next;
        if(copyhead == NULL)
        {
            copyhead = copytail = copy;
        }
        else
        {
            copytail->next = copy;
            copytail = copytail->next;
        }
        //重新还原原链表
        cur->next = next;
        //迭代
        cur = next;
    }

    return copyhead;
}

在这里插入图片描述

6. 双向带头循环链表

6.1 函数实现

// 2、带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
    LTDataType _data;
    struct ListNode\* next;
    struct ListNode\* prev;
}ListNode;
// 创建返回链表的头结点.
ListNode\* ListCreate();
// 双向链表销毁
void ListDestory(ListNode\* plist);
// 双向链表打印
void ListPrint(ListNode\* plist);
// 双向链表尾插
void ListPushBack(ListNode\* plist, LTDataType x);


![img](https://img-blog.csdnimg.cn/img_convert/d6cb9fbe91cadd1e4421bf193b6a8e41.png)
![img](https://img-blog.csdnimg.cn/img_convert/b5b0aed96b7ec4e01bebe5416d20e8bf.png)
![img](https://img-blog.csdnimg.cn/img_convert/87a7f4aec1aa783530d02170dece6db1.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**

ext;
        next = copy->next;
        if(copyhead == NULL)
        {
            copyhead = copytail = copy;
        }
        else
        {
            copytail->next = copy;
            copytail = copytail->next;
        }
        //重新还原原链表
        cur->next = next;
        //迭代
        cur = next;
    }

    return copyhead;
}

在这里插入图片描述

6. 双向带头循环链表

6.1 函数实现

// 2、带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
    LTDataType _data;
    struct ListNode\* next;
    struct ListNode\* prev;
}ListNode;
// 创建返回链表的头结点.
ListNode\* ListCreate();
// 双向链表销毁
void ListDestory(ListNode\* plist);
// 双向链表打印
void ListPrint(ListNode\* plist);
// 双向链表尾插
void ListPushBack(ListNode\* plist, LTDataType x);


[外链图片转存中...(img-ZxyxrQbX-1715755523381)]
[外链图片转存中...(img-0H00x5Hf-1715755523382)]
[外链图片转存中...(img-CjnLiqCM-1715755523382)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值