链表OJ(下)

目录

1、链表分割

2、链表的回文结构

3、相交链表

4、环形链表

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


前言:上次链表OJ开了个素,这次直接上荤菜,让你回味无穷!

1、链表分割

链接:牛客:链表分割

题目:

题解:

/*
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* lessHead,*lessTail,*greaterHead,*greaterTail;
        lessHead=lessTail=(struct ListNode*)malloc(sizeof(struct ListNode));
        greaterHead=greaterTail=(struct ListNode*)malloc(sizeof(struct ListNode));
        lessTail->next=greaterTail->next=NULL;
        
        struct ListNode* cur=pHead;
        while(cur)
        {
            //1、将小于x的放在less链表中
            if(cur->val<x)
            {
                lessTail->next=cur;
                lessTail=lessTail->next;
            }
            //2、将大于或等于x放在greater链表中
            else{
                greaterTail->next=cur;
                greaterTail=greaterTail->next;
            }
            
            cur=cur->next;
        }
        //3、连接两个链表
        lessTail->next=greaterHead->next;
        greaterTail->next=NULL;
        
        struct ListNode* list=lessHead->next;
        free(lessHead);
        free(greaterHead);
        
        return list;
    }
};

 讲解:

首先我们重新创建两个链表,一个放小于x的值,一个放大于或等于x的值,然后连接两个链表。

2、链表的回文结构

链接:链表的回文结构

题目:

题解:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) : val(x), next(NULL) {}
};*/
class PalindromeList {
public:
    //找链表中间值(如果mid有两个则取后面那个)
    struct ListNode* Findmid(struct ListNode* head)
    {
        struct ListNode* slow=head;
        struct ListNode* fast=head;
        while(fast && fast->next)
        {
            slow=slow->next;
            fast=fast->next->next;
        }
        return slow;
    }
    
    struct ListNode* reverlist(struct ListNode* head)
    {
        struct ListNode* newHead=NULL;
        struct ListNode* cur=head;
        while(cur)
        {
            struct ListNode* next=cur->next;
            cur->next=newHead;
            newHead=cur;
            cur=next;
        }
        return newHead;
    }
    bool chkPalindrome(ListNode* A) {
        // write code here
        struct ListNode* mid=Findmid(A);
        struct ListNode* rHead=reverlist(mid);
        while(A&&rHead)
        {
            if(A->val==rHead->val)
            {
                A=A->next;
                rHead=rHead->next;
            }
            else
            {
                return false;
            }
        }
        return true;
    }
};

 讲解:

题目要求我们返回的是一个Bool值,也就是让我们判断这个链表是不是回文结构。

那么什么是回文结构?

比如:1->2->3->3->2->1是回文结构

1->2->3->4->3->2->1不是回文结构,就相当于这个链表要对称。

想要判断是不是回文结构也好办,先找到链表的中间值,然后再把中间值作为新的头,将后部分的链表倒置,再一个一个判断是否相等,如果中间有一个不相等,那么就返回false,等成功走完循环以后,就返回true。

3、相交链表

链接:相交链表

题目:

 题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    int lenA=0,lenB=0;
    struct ListNode *tailA=headA;
    struct ListNode *tailB=headB;

    while(tailA)
    {
        lenA++;
        tailA=tailA->next;
    }
    while(tailB)
    {
        lenB++;
        tailB=tailB->next;
    }

    if(tailA!=tailB)
    {
        return NULL;
    }

    int gap=abs(lenA-lenB);
    struct ListNode *longList=headA,*shortList=headB;
    if(lenA<lenB)
    {
        shortList=headA;
        longList=headB;
    }
    while(gap--)
    {
        longList=longList->next;
    }
    while(shortList&&longList)
    {
        if(shortList==longList)
            return shortList;
        shortList=shortList->next;
        longList=longList->next;
    }
    return NULL;
}

讲解:

首先,我们要先了解一下相交链表,我们先来判断一下下面这个属于相交链表吗?

答案是显而易见的,一个结点不可能 指向两个结点,所以,我们判断的时候,这种情况一定要考虑到,也就是说,两个链表从头开始走,走到尾部,两个结点不一样,就肯定不是相交链表。

题目中需要我们返回相交的结点,所以也就是说,两个链表第一个相等的结点就是相交结点,如果两个链表长度相等倒好判断,但如果不相等呢,我们就可以采用快慢指针,先求出两个链表的长度差gap,再让长的先走gap步,再两个一起走,走到相等的时候就可以返回了。

4、环形链表

链接:环形链表

题目:

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
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;
            while(meet!=head)
            {
                meet=meet->next;
                head=head->next;
            }
            return meet;
        }
    }
    return NULL;

}

 讲解:

题目要求是返回入环的起始结点,首先,我们还要判断的是这个链表是不是环形链表,然后再去求入环的起始结点。

那么我们怎样来判断链表是否带环呢?这里我们还是用到快慢指针,slow一次走一步,fast一次走两步,最后,fast肯定能追上slow,所以带环的结果就是fast=slow。我们来推理一下为什么slow一次走一步,fast一次走两步就一定会相遇。

那如果说,slow每次走一步,fast每次走三步呢?fast每次走四步呢?最后fast一定会追上slow吗?答案是不确定。

当fast每次走三步的时候,进环后,slow每走一步,两个的距离减少2,也就是N-2,下一次是N-4,当N为偶数时,会减到0,但N为奇数时,会减到1,再到-1,刚好又错过,所以是不确定的。同理,fast每次走四步也是同样的道理。 

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

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

题目:

题解:

/**
 * 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* copyHead=NULL;
    struct Node* copyTail=NULL;

    //1、把结点拷贝下来链接到原结点
    while(cur)
    {
        struct Node* copy=(struct Node*)malloc((sizeof(struct Node)));
        copy->val=cur->val;
        copy->next=cur->next;
        cur->next=copy;

        cur=cur->next->next;
    }

    //2、更新拷贝结点的random
    cur=head;
    while(cur)
    {
        struct Node* copy=cur->next;
        if(cur->random==NULL)
        {
            copy->random=NULL;
        }
        else
        {
            copy->random=cur->random->next;
        }
        cur=cur->next->next;
    }

    //3、将拷贝结点解下来,连在一起
    cur=head;
    while(cur)
    {
        struct Node* copy=cur->next;
        struct Node* next=copy->next;

        cur->next=next;//恢复原链表

        if(copyTail==NULL)
        {
            copyHead=copyTail=copy;
        }
        else
        {
            copyTail->next=copy;
            copyTail=copy;
        }
        cur=next;
    }
    return copyHead;
}

 讲解:
这个题的关键就在于如何处理新链表的random指针,random是随机的,所以很难定,这里我们有个方法,就是将原来的结点拷贝下来,一次接在原结点的后面,然后我们发现,新拷贝后的random指向的就是原结点的random的下一个,但当原结点的random指向的是NULL时,拷贝结点的random指向的也是NULL(这一点要注意一下)。最后再把拷贝结点解下来,链接在一起,这样就很好的解决了新链表的random指针了。

今天的荤菜到此结束,希望你可以收货满满!!!

### 关于 XMUOJ 平台上的链表加法题目及其解题思路 #### 链表加法的核心概念 链表加法是一种常见的算法问题,通常涉及两个存储数字的单向链表相加,并返回一个新的表示其和的链表。这类问题的关键在于处理进位逻辑以及链表节点的操作[^1]。 #### 数据结构的选择 在解决此类问题时,可以选用单向链表作为数据结构的基础形式。每个节点包含两部分:`val` 表示当前节点所存数值,`next` 指针指向下一个节点。例如,在 Python 中定义如下: ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next ``` #### 进位机制实现 当执行链表加法时,需特别注意进位操作。如果某一位的结果大于等于 10,则应将其减去 10 后放入该位置的新节点中,并将进位值传递到下一次计算中。 以下是基于上述描述的一个通用解决方案框架: ```python def addTwoNumbers(l1: ListNode, l2: ListNode) -> ListNode: dummy_head = ListNode(0) current_node = dummy_head carry_over = 0 while l1 or l2 or carry_over != 0: value_l1 = (l1.val if l1 else 0) value_l2 = (l2.val if l2 else 0) total_sum = value_l1 + value_l2 + carry_over carry_over = total_sum // 10 new_digit = total_sum % 10 current_node.next = ListNode(new_digit) current_node = current_node.next if l1 is not None: l1 = l1.next if l2 is not None: l2 = l2.next return dummy_head.next ``` 此函数通过遍历输入链表 `l1` 和 `l2` 的每一个节点来完成逐位求和的过程,同时考虑到了可能存在的额外进位情况。 #### 测试用例设计 为了验证程序正确性,可构建几个典型测试场景,比如正常长度匹配、不同长度列表间的运算等情形下的表现评估。 #### 总结 综上所述,针对 XMUOJ 上有关链表加法类别的竞赛题目解答策略主要围绕着如何有效管理指针移动与进位控制展开讨论。
评论 64
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可爱豆麻薯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值