单向链表的学习笔记2

链表的反转

LeetCode的刷题有一道链表的反转,操作要求如下:
在这里插入图片描述
代码如下:

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* cur = head;
    struct ListNode* prev = NULL;
    while(cur)
    {
        struct ListNode* next = cur->next;      //将cur的下一个地址记录起来
        cur->next = prev;                       //将当前cur里面的指向改为指向前一个
        prev = cur;                             //将前一个指针置为当前
        cur = next;                             //将当前指针置为下一个
    }
    return prev;
}

解题思路:反转就是让原链表的头指向空,然后让下一个地址全部指向前一个地址,但是链表的单向性,如果我们只是单纯的将下一个地址指向前一个地址,那么在下一个地址我们就找不到,所以我们可以事先将下一个地址存起来,所以这里我们用到三个指针,分别记录当前、前一个和下一个来解决这道题。

链表的中间节点

同样是来自LeetCode的一道题,操作要求如下:
在这里插入图片描述
代码如下:

struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        fast = fast->next->next;        //fast一次走两步
        slow = slow->next;              //slow一次走一步
    }
    return slow;
}

这题可以有两个解法,一种是暴力遍历,创建一个计数变量i,遍历整个链表有多少元素,然后将结果除以2即为中间节点的位置,。第二种就是比较巧妙的方法,通过申请一对快慢指针,快指针每次走两步,而慢指针一次走一步,这样当快指针走到尾时,慢指针就刚刚好在中间。不过这题有一个地方是需要注意的,如果我将while里面的条件改成(fast->next && fast)可不可以呢?大家可以思考一下并在评论区讨论哦😄

回文链表

同样是来自LeetCode的一道题,操作要求如下:
在这里插入图片描述
代码如下:

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

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

bool isPalindrome(struct ListNode* head){
    if(head->next == NULL)
    return true;
    struct ListNode* cur = head;
    //先找中间将链表分割为两部分
    struct ListNode* mid = FindListMid(head);
    //将后半部分逆置
    struct ListNode* Rhead = ReverseList(mid);
    //将前半部分和后半部分进行判断
    while(Rhead && cur)
    {
        if(cur->val != Rhead->val)
        {
            return false;
        }
        else
        {
            cur = cur->next;
            Rhead = Rhead->next;
        }
    }

    return true;

}

这道题目的思路就结合了上两道题的思路了,首先我们先获取链表的中间节点,然后根据中间节点将后半段链表进行反转,反转之后在跟前半段进行比较即可。

相交链表

要求如下:
在这里插入图片描述
代码如下:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* curA = headA;
    struct ListNode* curB = headB; 
    int countA = 0;
    int countB = 0;
    //先判断两个链表是否相交,根据他们的尾巴是否相同
    while(curA->next)
    {
        curA = curA->next;
        countA++;
    }
    while(curB->next)
    {
        curB = curB->next;
        countB++;
    }
    //若尾巴相同,证明相交,接着找出第一个节点
    if(curA == curB)
    {
        curA = headA;
        curB = headB;
        //根据长度判断,链表长的先走相差步
        if(countA > countB)
        {
            int dec = countA-countB;
            while(dec--)
            {
                curA = curA->next;
            }
        }
        else
        {
            int dec = countB-countA;
            while(dec--)
            {
                curB = curB->next;
            }
        }
        //走完相差步后同步走,当两个链表地址相同时,则为交点
        while(curA != curB)
        {
            curA = curA->next;
            curB = curB->next;
        }
        return curA;
    }
    return  NULL;
}

首先我们可以将这道题拆分为两步,判断两个链表是否相交,若相交再找交点,那么判断是否相交的依据就是判断他们的尾巴是否相同,所以使用遍历判断尾巴是否相同,接下来就是找交点,在这里交点的思路是通过让长的链表先走相差的长度,然后让两个链表在一起走,第一个相同的位置即为我们的交点,借助图片会更好理解:
在这里插入图片描述
这里是B会长一点,那么B先走一步,然后在跟A一起走,他们相遇的第一个点即地址相同的点就是我们的交点。

环形链表

要求如下:
在这里插入图片描述
代码如下:

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(slow == fast)
        return true;
    }
    return false;
}

环形链表的思路比较简单,通过定义快慢指针,一个走两步,一个走一步,当快指针能够等于慢指针的时候则证明有环,不能则无环,在这一块有一个思考,如果有环,快指针走两步一定能追到慢指针吗?如果快指针走的是3步,4步或者N步(N>2)能追上吗?
第一个问题的答案是一定,用下图来方便理解:
在这里插入图片描述
假设当fast和slow都进环了,那么此时fast开始追slow,假定他们之间的距离为N,那么根据步长fast每次走两步,slow每次走一步,每走一次他们之间的距离就会缩短1,所以距离会变成N-1,N-2,N-3,…,1,0,最终是一定会追上的。
第二个问题相同的思路,用3步来举例子,每走一次他们之间的距离就会缩短2,所以距离会变成N-2,N-4,N-6,…,最终的情况就要根据环的长度来决定了,如果环的长度刚好是偶数则会追上,如果是奇数则一定追不上,所以走N步(N>2)的情况则是不一定会追上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

葛叶灬

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

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

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

打赏作者

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

抵扣说明:

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

余额充值