【LeetCode】【牛客】链表刷题(二)

目录

一、回文链表

 二、链表分割

 三、相交链表

 四、判断有没有环状链表


一、回文链表

力扣

给定一个链表的 头节点 head ,请判断其是否为回文链表。

如果一个链表是回文,那么链表节点序列从前往后看和从后往前看是相同的。

对于回文链表,我们可能会想到的是创建一个数组将我们回文链表中的所有数据拷贝到数组中,然后对我们的数组用双指针首尾判断是不是回文链表。但是这种写法虽然能够通过,但所需要创建的数组非常巨大,对于本道题的测试用例来说,大概需要十万个元素的数组才能够完成。

bool isPalindrome(struct ListNode* head){
    int count=0;
    long long int all=0;
    struct ListNode*p=head;
    int arr[100000]={};
    while(p)
    {
        arr[count]=p->val;
        count++;
        p=p->next;
    }
    int a1=0;
    int a2=count-1;
    int flag=1;
    for(int i=0;i<count/2;i++)
    {
        if(arr[a1]!=arr[a2])
        {
            return false;
        }
        a1++;
        a2--;
    }
    return true;
}

可以看出,上述代码并不是一个优秀的写法,所以我们可以换一种思路来写这道题。 在我们的【LeetCode】【牛客】链表刷题(一)_wolfwalker的博客-CSDN博客

中,我们已经知道了反转链表和快慢结点的方法,接下来我们就可以调用我们之前学过的方法来做我们这一道题。

我们的核心思路就是先用快慢指针的方法找到我们链表的中间结点,然后我们将我们链表的后半段指针反转,再同时用两个头指针来遍历我们的前半段链表和逆转的后半段链表。

如果我们链表的结点个数时偶数,我们的查找中间结点所找到的是第二个中间结点,这时,我们链表的前半段和逆转的后半段是数量相同,是能够一一对应来判断的。

如果我们链表的结点个数是奇数,我们的查找中间结点所找到的就是中间结点。逆转之后,我们后面那段逆转的链表是要比我们没有逆转的前半段链表多出一个中间结点的。但是由于我们在下面的代码中有while(head&&rhead),也就是说当我们的前半条链表判断到NULL而我们的后半条链表还剩下一个中间结点没有被判断时,就会跳出循环并且返回TRUE,这正好是我们所需要的。

bool isPalindrome(struct ListNode* head){
    //这里调用了我们快慢指针法寻找中间结点的方法。

    struct ListNode* middleNode(struct ListNode* head){
    struct ListNode* slow, *fast;
    slow = fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }

    return slow;
    }
    
    //这里调用了我们反转链表指针的方法
    struct ListNode* reverseList(struct ListNode* head){
    if(head == NULL)
        return NULL;
        
    struct ListNode* n1,*n2,*n3;
    n1 = NULL;
    n2 = head;
    n3 = n2->next;
    while(n2)
    {
        // 倒指向
        n2->next = n1;

        // 迭代
        n1 = n2;
        n2 = n3;
        if(n3)
            n3 = n3->next;
    }

    return n1;
    }
    
    //创建一个ListNode结点类型的中间结点(通过调用我们middleNode的方法来实现)
        struct ListNode* mid = middleNode(head);
    //将我们刚刚找到的中间的结点所对应的后半张链表全部反转(通过调用我们的逆转指针的函数)
        struct ListNode* rhead = reverseList(mid);
    
    //当我们的头结点和右边的头结点都存在的时候,我们进入下面的while循环
        while(head && rhead)
        {
    //如果我们的两个头指针不相等,我们就直接返回false,也就是说我们的这个链表不是回文链表
            if(head->val != rhead->val)
            {
                return false;
            }
    //如果两个头指针所指的数据相同,我们就将我们的链表的两个指针逐个依次向后移动,来查看回文链表对应位置的元素是否相同
            else
            {
                head = head->next;
                rhead = rhead->next;
            }
        }
    //当我们的链表查找到这里的时候,就已经表示我们的链表的前半段和逆转后半段是相同的,也就是说我们的链表是回文链表
        return true;
}

 二、链表分割

链表分割_牛客题霸_牛客网​​​​​​​

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

这道题我们的核心思想就是创建两个新的链表,将所有小于x的结点接在其中一条链表上,将所有大于x的结点接在另一条链表上,最后我们再将这两条链表拼接起来,就可以了。

/*
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
//在这里我们创建四个结点的指针,两个指向比x大的元素的链表的首尾指针,两个指向比x小的元素的收尾指针
        struct ListNode* greaterhead,*greatertail,*lesshead,*lesstail;
//为我们两条链表分别开辟哨兵位
        greaterhead=greatertail=(struct ListNode*)malloc(sizeof(struct ListNode));
        lesshead=lesstail=(struct ListNode*)malloc(sizeof(struct ListNode));
//分别将我们两条链表的尾部置空
        greatertail->next=NULL;
        lesstail->next=NULL;
//我们创建一个临时的指针,用来遍历我们的原来的链表。(尽量不要直接改动题目给我们的头结点)
        struct ListNode* cur =pHead;
        while(cur)
        {
//如果当前原来链表的结点的数据小于x
            if(cur->val<x)
            {
//我们就将我们cur指向的结点接在我们的lesstail也就是存储小于x的结点元素的链表后面
                lesstail->next=cur;
                lesstail=lesstail->next;
            }
//如果不是小于x的结点,也就是说大于等于x的结点我们将其排在greatertail也就是存储大于x的结点元素的链表后面
            else
            {
                greatertail->next=cur;
                greatertail=greatertail->next;
            }
//将我们临时指针不断后移来遍历整张链表
            cur=cur->next;
        }
//将我们的存储大于等于x的链表连接在存储小于x的元素的链表的后面,再将我们合并后链表的结尾置空
        lesstail->next=greaterhead->next;
        greatertail->next=NULL;

//程序的最后不要忘了将我们的头指针从我们哨兵位的头结点指向我们真正的头结点
//然后我们需要将我们的两个哨兵位所占的空间释放
        struct ListNode* head=lesshead->next;
        free(greaterhead);
        free(lesshead);
//将我们的头结点返回
        return head;
    }
};

 

 三、相交链表

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

力扣

对于相交链表我们给出两个思路:

一个是将我们的两条链表分别用我们之前写的倒转指向的函数将我们两条链表倒置, 再同时对应遍历两条链表,当我们遍历到的下一个结点是不同的时候,我们就知道当前结点是两个链表相交的起始结点。(这个思路是由于当我们的链表相交之后,我们两条链表的结点的地址一定都是一样的,因此从后往前找到下一个结点不是相同地址的时候,当前所指向的结点就是我们链表相交的起始节点)

另一个是分别遍历我们的两张链表,分别计算出我们两张链表所含有的结点个数,然后我们让我们长的链表先走这个相差的步数,然后再让我们两个链表上的两个结点同时对应向后查找第一个地址相同的结点,这就找到了我们题目所要求的结点。

下面的代码为我们的第二种思路的具体实现。

/**
 * 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,*curB=headB;
    int lenA=1,lenB=1;
    while(curA->next)
    {
        curA=curA->next;
        lenA++;
    }

    while(curB->next)
    {
        curB=curB->next;
        lenB++;
    }
//此时我们的链表的两个指针都遍历到了链表的最后一个元素,如果此时链表的最后一个元素都不相同,那么链表根本就没有相同的结点,直接返回NULL
    if(curA!=curB)
    {
        return NULL;
    }

    //求第一个交点
    struct ListNode*shortList=headA,*longList=headB;
    if(lenA>lenB)
    {
        shortList=headB;
        longList=headA;
    }
//计算我们两条结点之间相差的绝对值
    int gap=abs(lenA-lenB);
    
    //长的先走差距步gap
    while(gap--)
    {
        longList=longList->next;
    }
//当我们指向我们两条链表的对应结点不相同的时候,也就是说我们还没有找到我们的公共结点,我们还需要继续向后找
    while(shortList!=longList)
    {
        shortList=shortList->next;
        longList=longList->next;
    }
//我们跳出循环时的结点就是我们的公共结点,返回shortList或者是longList都是可以的。

    return shortList;
}

 

 四、判断有没有环状链表

力扣

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

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

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

对于这道题我们的思路是用快慢指针的方法。如果我们的链表中存在环,那我们的两个指针都会进入环,并且快指针在某一时候会从后面追上快指针。

如果没有环,那我们的快指针会顺利到达我们的链表尾。

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

        if(slow==fast)
        {
            return true;
        }
    }

    return false;
}

 

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

桜キャンドル淵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值