(C语言)干货满满!!!面试必备OJ题:链表篇(二)

目录

  • 前言:
  • 题目1.链表的回文结构
  • 题目2.相交链表
  • 题目3.环形链表
  • 题目4.环形链表II
  • 题目5.复制带随机指针的链表

前言:

我们接着前面的面试必备OJ题:链表篇(一)这次我们再来看几道链表oj题

题目1.链表的回文结构

题目链接

思路:
1、先找到整段链表的中间结点,奇数个就是最中间的那个结点,偶数个的话就是中间两个的第二个。
2、然后从中间结点开始逆置结点,
3、最后同时从头结点head和中间结点rmid开始比对他们的值
由于中间结点之前的那个结点的next我们从始至终都没有改变 , 所以说就能同时从头结点和中间结点开始比对。
什么时候停下来呢?这需要分情况处理
偶数个结点:若到rmid到NULL停止,head和rmid的值依然都相等,那么就是回文
奇数个结点:若head和rmid都到NULL停止,head和rmid的值依然都相等,那么就是回文

(C语言)干货满满!!!面试必备OJ题:链表篇(二)_第1张图片
实现代码:

class PalindromeList {
  public:
    struct ListNode* reverseList(struct ListNode* head) { //反转链表
        struct ListNode* n1 = NULL;
        struct ListNode* n2 = head;
        struct ListNode* n3 = NULL;
        while (n2) {
            n3 = n2->next;

            n2->next = n1;

            n1 = n2;
            n2 = n3;
        }
        return n1;
    }

    struct ListNode* middleNode(struct ListNode* head) { //找中间结点
        //奇数个和偶数个
        //快慢指针
        struct ListNode* fast = head;
        struct ListNode* slow = head;
        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }

    bool chkPalindrome(ListNode* A) {
   
        struct ListNode* head = A;
        struct ListNode* mid = middleNode(A);

        struct ListNode* rmid = reverseList(mid);


        while (head && rmid) {

            if (head->val != rmid->val) {
                return false;
            }
            head = head->next;
            rmid = rmid->next;
        }
        return true;
    }
};

题目2.相交链表

题目链接

思路:
定义两个指针head1和head2分别记录headA和headB的值,先让他们分别走到链表的最后,并记录各自的长度,看是否有交点,没有交点直接返回NULL,
然后再定义两个指针longlist和shortlist再次分别记录headA和headB;通过计算A链表和B链表的差值的绝对值,让长的那个链表先走差值的绝对值步,最后两个链表再同时往前遍历,判断longlist和shortlist是否的地址相同,一旦相同就说明当前地址是两个单链表相交的起始节点,返回此时的地址即可。

(C语言)干货满满!!!面试必备OJ题:链表篇(二)_第2张图片
实现代码:

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode*Head1=headA;
    struct ListNode*Head2=headB;//题目说  函数返回结果后,链表必须 保持其原始结构 。

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

    int length1=1;//两个链表同时往后走,都走到最后看是否“相交”,并且记录长度
    while(Head1->next)
    {
        Head1=Head1->next;
        length1++;
    }
    int length2=1;
    while(Head2->next)
    {
        Head2=Head2->next;
        length2++;
    }

    if(Head1!=Head2)//都走到最后不相交直接返回NULL
    {
        return NULL;
    }

    struct ListNode*longlist=headA;
    struct ListNode*shortlist=headB;
    if(length1<length2)               //把长的那个链表的头指针赋值给longlist
    {
        shortlist=headA;
        longlist=headB;        
    }
    

    int gap=abs(length2-length1);//求绝对值函数
    //长的链表先走距离步
    while(gap)
    {
        longlist=longlist->next;//长链表往后走距离的差之后
        gap--;
    }
    
    while(longlist!=shortlist)//再同时往后走,找出并返回两个单链表相交的起始节点
    {
        longlist=longlist->next;
        shortlist=shortlist->next;
    }
    return longlist;
}

题目3.环形链表

题目链接
思路:定义一个快指针fast再定义一个慢指针slow,快指针一次走两步,慢指针一次走一步,如果有环的情况下,快指针一定会通过环的结构,与慢指针相遇。下面画了一幅动图供大家参考。
为什么快指针每次走两步,慢指针走一步可以?
假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚
进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。
此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情
况,因此:在满指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。

如果没有环的情况下,那么就是前面链表经典oj题(一)里链表的中间结点问题了。

(C语言)干货满满!!!面试必备OJ题:链表篇(二)_第3张图片
实现代码:

bool hasCycle(struct ListNode *head) {
    struct ListNode *slow=head;
    struct ListNode *fast=head;
    while(fast&&fast->next)
    //因为快指针一次走两步,如果当前链表没有环的时候,
    //偶数个走到fast==NULL的时候停止,
    //奇数个的话走到fast->next==NULL的时候停止
    {
        slow=slow->next;
        fast=fast->next->next;
        if(fast==slow)//在环中快指针一定有一个时刻能和慢指针重合
        {
            return true;
        }
    }

    return false;
    
}

题目4.环形链表II

题目链接
思路:
解法一:普通解法,如果有环的情况下,定义一个快慢指针,从头开始往后先找在环中相遇的结点,然后将此处的结点的next置为NULL,相当于断开当前的环,那么就是前面的相交链表问题了。最后要注意恢复原来的链表。但是此方法工程量比较大。需要把前面的相交问题的代码加以完善才能解决此题。
(C语言)干货满满!!!面试必备OJ题:链表篇(二)_第4张图片
解法二:公式推导解法,我们知道fast走的距离=2 * slow走的距离,假设进环前的长度是L,假设环的长度是C,假设入口点到相遇点距离是X。slow走的距离是L+X,fast走的距离是L+N * C+X,设slow进环前,fast在环里面转了N圈N>=1。
可得: 2(L+X)=L+X+N*C
( L+X)=N * C
L=N * C - X
化简得:L=(N-1)*C+C-X
结论:一个指针A从头开始走,一个指针B从相遇点开始走他们会在入口点相遇。
(C语言)干货满满!!!面试必备OJ题:链表篇(二)_第5张图片

实现代码:

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*meet=fast;
            struct ListNode*myhead=head;
            while(meet!=myhead)
            {
                myhead=myhead->next;
                meet=meet->next;
            }
            return myhead;
        }

    }
    return NULL;
}

题目5.复制带随机指针的链表

题目链接
思路:
1、拷贝原结点,并链接在原结点的后面。使原结点和拷贝结点建立一个链接关系,找到原结点,,就可以找到拷贝结点。
(C语言)干货满满!!!面试必备OJ题:链表篇(二)_第6张图片

2、更新每个拷贝结点的random(关键步骤)

if(cur->random)
copy->random = cur->random->next;
(C语言)干货满满!!!面试必备OJ题:链表篇(二)_第7张图片3、将拷贝结点解下来,链接成新链表
在这里插入图片描述

struct Node* copyRandomList(struct Node* head) {
    struct Node* cur=head;
    struct Node* back=NULL;
    struct Node* copy=NULL;
    while(cur)//1.拷贝原结点,链接在原结点后面
    {
        copy=(struct Node*)malloc(sizeof(struct Node));
       
        back=cur->next;
        cur->next=copy;

        copy->val=cur->val;
        
        copy->next=back;
        cur=back;
    }//此时原链表的各个元素已经断开

    cur=head;
    while(cur)//2.更新每个拷贝结点的random
    {
        copy=cur->next;

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

    cur=head;
    struct Node* copyhead=NULL;
    struct Node* copytail=NULL;
    while(cur)//3.将拷贝结点解下来,链接成新链表
    {
        copy=cur->next;
        back=cur->next->next;
        cur->next=back;//
        if(copyhead==NULL)
        {
            copyhead=copytail=copy;//头插,直接连上去不用更新结点
        }
        else
        {
            copytail->next=copy;
            copytail=copytail->next;//尾插,需不需要更新尾结点,画图就可以知道//

        }
        // copytail=copytail->next;犯错之处
        cur=back;
    }
	
    return copyhead;   

}

总结:
1、链表题要多多画图,代码要跟着图走。
2、注意指针下一步要走向哪里?是不动?还是要跳到下一个结点上?每次循环过后要注意更新结点。
3、多做题,积累经验和方法,有些题目不是不会,只是从来都不知道有这样的方法。

你可能感兴趣的:(经验分享,数据结构,链表,数据结构)