LeetCode链表习题解析

练习一

题目:点击这里
在这里插入图片描述

1、思路:

我们可以看成一个数学问题:如果两个速度不同的人从同一起点开始跑步,路程相同,如果他们相遇了,这就说明这是一个环,如果不相遇,也并不代表就不是环了,假如,甲速度为x,乙速度为2x,当甲到了环的进口点时,乙早已经在环里面了,设乙到甲的距离为n,甲每走一步相当于乙走了两步,所以,甲每走一步,乙就更近甲一步,n逐步减一,直到n为0,则甲乙两人相遇,但是如果甲速度为x,乙速度为3x,当甲到了环的进口点时,乙已经在环里面了,设乙到甲的距离为n,甲每走一步,乙就更近甲两步,则n以逐步减二的趋势递减,如果n为偶数,则他们能相遇,如果n为奇数,乙则会跑到甲的前面一步,设这个环的周长为c,则乙到甲的距离为c-1,如果c-1是偶数,那么还是以减二的趋势递减,肯定会相遇,如果c-1是奇数,那么以减二的趋势递减,一定相遇不了,这里我们得出一个结论:速度相差一倍的两个人一定会相遇。
在这里插入图片描述

再回到这个题目中,我们可以创建两个指针,慢的指针一次走一步,快的指针走两步,这样,如果相遇则一定是环,否则快的指针一定会先走到NULL。

2、代码:

bool hasCycle(struct ListNode *head) {
    struct ListNode* slow = head, *fast = head;
    while(fast && fast->next)
    {
        
        slow = slow->next;//一次走一步
        fast = fast->next->next;//一次走两步
        if(slow == fast)
        {
            return true;
        }
    }
    //走到这里就表示快指针已经走到NULL,表示不是环
    return false;
    
}

练习二

题目:点击这里
在这里插入图片描述

1、思路

这个是练习一的升级版,其实,要想找到环的第一个节点,也就是环的进口点,只需要让甲再从起点出发,而乙从甲乙相遇点出发,再次相遇的点就是环的第一个节点,证明如下:
还是一样,设甲速度为n,乙速度为2n,环的周长为c,这次设环的第一个节点到相遇点的距离为n,直线的距离为l,则甲到相遇点走的路程是l+n,乙走的路程是l+k×c+n(其中k是圈数),时间相同,乙速度是甲的2倍,所以,2(l+n)=l+k×c+n,结果为l=k×c-n,得出,甲从起点出发,乙从相遇点出发,第一次相遇的点就是环的第一个节点。
在这里插入图片描述

2、代码

所以,和练习一一样,用快慢指针来走。

struct ListNode *hasCycle(struct ListNode *head) {
    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* slow = head, *fast = head;
    fast = hasCycle(head);
    if(fast == NULL)
    {
        return NULL;
    }
    else
    {
        while(slow != fast)
        {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
    
}

练习三

题目:点击这里
在这里插入图片描述

1、思路

我们可以创建一个新链表,再用两个指针分别表示给出的这两个链表的头,用这两个指针指向的数据进行比较,小的那个放到新的链表中,再用小的那个指针指向下一个,再进行比较,小的那个再接在新链表的后面,以此类推,直到有一个指针指向空,表示这个指针已经遍历完了,再把没遍历完的接在新链表的后面,最后返回新的链表。

2、代码

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1 == NULL)
    {
        return list2;
    }
    if(list2 == NULL)
    {
        return list1;
    }
    //一个代表遍历,一个表示头指针
    struct ListNode* newnode = NULL,*newnode_head = NULL;
    //一个遍历list1,一个遍历list2
    struct ListNode* list1_tail = list1,* list2_tail = list2;
    //开始遍历list1,list2,直到有一个遍历完,结束遍历
    while(list1_tail && list2_tail)
    {
        if(list1_tail->val < list2_tail->val)
        {
            if(newnode_head == NULL)
            {
                newnode_head = newnode = list1_tail;
            }
            else 
            {
                newnode->next = list1_tail;
                newnode = newnode->next;
            }
            list1_tail = list1_tail->next;
        }
        else 
        {
            if(newnode_head == NULL)
            {
                newnode = newnode_head = list2_tail;
            }
            else 
            {
                newnode->next = list2_tail;
                newnode = newnode->next;
            }
            list2_tail = list2_tail->next;
        }
        
    }
    //如果是list1先走完,就把剩下的list2接上
    if(list1_tail == NULL)
    {
        newnode->next = list2_tail;
    }
    //如果是list2先走完,就把剩下的list1接上
    if(list2_tail == NULL)
    {
        newnode->next = list1_tail;
    }
    return newnode_head;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值