学会了链表还不赶紧来刷题《二》

学会了链表还不赶紧来刷题《二》,第1张


回文链表题目分析:

代码实现:

相交链表的题目分析:

代码实现:

思路一:

思路2:(浪漫又难理解)

环形链表的题目分析:

代码实现:

环形链表 || 的题目解析:

代码实现:

思路1:(示意图如下)

思路2:(示意图如下)

复制带随机指针的链表题目分析:

代码实现:


昨天我们看的是链表比较基础的题,今天我们来看下比较新颖的题。

234. 回文链表

回文链表题目分析:

这道题我们看起来比较简单,就是判断是不是回文,我们用的答题思路是:

先找到链表的中间节点,

然后从中间节点向后的链表逆置,

最后两个,一个头指针,一个中间节点的指针,向后遍历,为空停止。

示意图如下:

代码实现:

首先我们找中间节点,然后逆置,这都已经学过了。

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* Newnode = NULL;
    struct ListNode* cur = head;

    while(cur)
    {
        struct ListNode *tmp = cur->next;
        cur->next = Newnode;
        Newnode = cur;
        cur = tmp;
    }

    return Newnode;
}

struct ListNode* middleNode(struct ListNode*head)
{
    struct ListNode* fast = NULL;
    struct ListNode* slow = NULL;
    fast = slow = head;

    while(fast->next && fast->next->next)
    {
        slow = slow->next;
        fast = fast->next->next;
    }

    return slow;
}

bool isPalindrome(struct ListNode* head)
{
    struct ListNode* mid = middleNode(head);
    struct ListNode* rhead = reverseList(mid);

    while(rhead && head)
    {
        if(rhead->val != head->val)
        {
            return false;
        }
        else
        {      
            rhead = rhead->next;
            head = head->next;
        }
    }

    return true;
}

1.首先找中间节点。

2.逆置中间节点到最后的结点之间的链表。

3.两个指针一个指向头指针,一个指向中间节点的指针,进行遍历,任何一个结点为空停止。

注意:结点数奇数和偶数都是适用的,我们可以通过上述示意图看出,我们还是注意循环的结束条件,我们想的是结束条件,但是写在循环的限定条件处的是继续条件

160. 相交链表

相交链表的题目分析:

 我们看到这个题目样式比较简单,就是两个链表求交点,并且为无环链表,思路如下:

思路一:我们可以算出链表长度,求差值,然后让长的链表先走,然后在一起走,如果相等就返回。

思路二(浪漫又抽象):定义两个指针,一个先走长链表,再走短链表,一个先走短链表,再走长链表,最后会在交点处相遇。

(你走过我走的路,我们最终会相遇,愿天下有情人都返回 true )

思路三:我们可以把其中一个链表链接起来,变成判断链表带环问题(自己实现哦)

代码实现:
思路一:
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
    int lenA = 1, lenB = 1;
    struct ListNode* curA = headA, *curB = headB;
    //计算链表长度
    while(curA)
    {
        ++lenA;
        curA = curA->next;
    }
    
    while(curB) 
    {
        ++lenB;
        curB = curB->next;
    }
    

    int gap = abs(lenA-lenB);
    struct ListNode* longList = headA, *shortList = headB;
    if(lenA < lenB)//假设法,得到长链表
    {
        longList = headB;
        shortList = headA;
    }
    //让长链表先走几步
    while(gap--)
    {
        longList = longList->next;
    }


    //两个链表同时走,直到遇到相同的节点
    while(longList && shortList)
    {
        if(longList == shortList)
        {
            return longList;
        }
        else 
        {
            longList = longList->next;
            shortList = shortList->next;
        }
    }
    
    return NULL;
}

1.首先我们先遍历一遍链表,得到两个链表的长度。

2.算出两个链表的差值。

3.使用假设法,求出链表中较长的,然后让长链表走差值步。

4.让两个链表同时走,节点值相等就返回节点,不相等返回NULL,说明没交点

注意:思路一虽然思想简单,但是实现的过程中很多细节需要注意,比如假设法求较长的链表,比如循环结束条件。

思路2:(浪漫又难理解)
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB)
{
    struct ListNode *pA = headA;
    struct ListNode *pB = headB;

    if(headA == NULL  || headB == NULL)
    {
        return NULL;
    }

    while(pA != pB)
    {
        pA = pA == NULL ? headB : pA->next;
        pB = pB == NULL ? headA : pB->next;
    }    

    return pA;
}

我们看上述图示来理解,就会方面很多了。

 

注意:我们先要判断这两个链表都要不为空,并且在一个链表走到NULL时,要直接走下一个链表。 

141. 环形链表

环形链表的题目分析:

我们依然可以使用快慢指针追赶的形式来进行解答,快指针一次走2步,慢指针一次走一步,然后快指针先入环,然后慢指针在入环,然后让快慢指针再环内追赶,最后相遇。 

代码实现:
bool hasCycle(struct ListNode *head) 
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;

    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            return true;
        }
    }

    return false;
}

代码上没什么难点,我们要注意循环的限定条件是快指针为空或者快指针的next为空。

为什么要走两步呢?

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚 进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。 此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情 况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。

必须快指针走2步才可以,走3步可以吗?

142. 环形链表 II

环形链表 || 的题目解析:

这个题目实际上要我们返回链表的入环点,如果没环就返回NULL。

 

这个题的整体思路是先用快慢指针判断是否链表有环,然后顺带求出快慢指针在环的相遇点,接着我们就分为两种思路:

思路一:

我们把相遇节点保存,在保存它的下一个节点,然后从相遇节点断开,问题转化为求两个链表的相交,求交点。

思路二:我们把相遇点保存,然后遍历,让头结点,和相遇点同时向前走,最后会在入环点相遇。

代码实现:
思路1:(示意图如下)

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(fast == slow)
        {
            struct ListNode* phead = fast->next;
            struct ListNode* p1 = phead;
            struct ListNode* p2 = head;
            fast->next = NULL;
            while(p1 != p2)
            {
                p1 = p1 == NULL? head: p1->next;
                p2 = p2 == NULL? phead: p2->next;
            }
            return p1;
        }
    }   

    return NULL;
}

1.先求相遇点。

2.保存相遇点的next

3.求两个链表相交。

思路2:(示意图如下)

 

 我们根据上图的逻辑,以及结论是可以写出思路二的代码的。

struct ListNode *detectCycle(struct ListNode *head) 
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;

    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;

        if(fast == slow)
        {
            struct ListNode* meet = fast;
            
            while(head != meet)
            {
                meet = meet->next;
                head = head->next;
            }

            return meet;

        }
    } 
    return NULL;    
}

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

复制带随机指针的链表题目分析:

这道题也叫做链表的深拷贝,就是有一个链表,他有两个指针,一个指向随机值,一个指向下一个,要求我们拷贝一份这样的链表。 

我们的答题思路是,先在创建每个节点,链接到原节点的后面,然后它的随机指针就是源节点的随机指针的next,然后把拷贝的节点尾插就好啦。

代码实现:

 

struct Node* copyRandomList(struct Node* head) 
{
    struct Node* cur = head;

    while(cur)//创建结点链接到后面
    {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;

        copy->next = cur->next;
        cur->next = copy;

        cur = copy->next;
    }	

    cur = head;

    //链接随机指针 copy->random = cur->random->next
    while(cur)
    {
        struct Node* copy = cur->next;

        if(cur->random == NULL)
        {
            copy->random = NULL;
        }
        else
        {
            copy->random = cur->random->next;
        }

        cur = copy->next;
    }

    cur = head;
    //尾插
    struct Node* copyhead = NULL;
    struct Node* copytail = NULL;

    while(cur)
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;

        if(copytail == NULL)
        {
            copyhead = copytail = copy;
        }
        else
        {
            copytail->next = copy;
            copytail = copytail->next;
        }

        cur->next = next;
        cur = next;
    }

    return copyhead;
}

1.先创建节点,链接到原链表每个节点的后面。

2.然后就是 copy->random = cur->random->next 的理解到位,如果原节点的 random 指为NULL,则 copy->random 也指为 NULL。

3.把 copy 的节点拿下来尾插一个新链表,返回就可以啦。

我们最重要的是明白 copy->random = cur->random->next,这个的由来。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1324063.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-12
下一篇 2022-06-12

发表评论

登录后才能评论

评论列表(0条)

保存