【数据结构】简单链表OJ 第二弹!

 

目录

 

[leetcode]21.合并两个有序链表

[牛客]链表分割

[牛客]链表的回文结构

[leetcode]160.相交链表


[leetcode]21.合并两个有序链表

 思路:依次比较,尾插较小的结点

要尾插我们一般需要一个head用于返回,一个tail用于方便找尾。

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    if(list1==NULL)
    return list2;

    if(list2==NULL)
    return list1;

    struct ListNode* cur1=list1,*cur2=list2;
    struct ListNode* head=NULL,*tail=NULL;
    while(cur1&&cur2)
    {
        if(cur1->val < cur2->val)
        {
            if(head==NULL)
            {
                head=tail=cur1;
            }
            else
            {
                tail->next=cur1;
                tail=tail->next;
            }
            cur1=cur1->next;
        }
        else
        {
            if(head==NULL)
            {
                head=tail=cur2;
                
            }
            else
            {
                tail->next=cur2;
                tail=tail->next;
            }
            cur2=cur2->next;
        }
    }
    if(cur1)
        tail->next=cur1;
    if(cur2)
        tail->next=cur2;

    return head;

}

 那么这段代码还可以怎么简化呢?我们引入一个新的概念就是带哨兵位的头结点,意思就是在头结点之前有一个结点,这个结点不存储内容,这样的好处就是可以不用上述代码最开始的非空判断,那么我们这里就再用这个思路优化一下代码。

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){
    struct ListNode* cur1=list1,*cur2=list2;
    struct ListNode* guard=NULL,*tail=NULL;
    guard=tail=(struct ListNode*)malloc(sizeof(struct ListNode));
    tail->next=NULL;
    while(cur1&&cur2)
    {
        if(cur1->val < cur2->val)
        {
            tail->next=cur1;
            tail=tail->next;
            cur1=cur1->next;
        }
        else
        {
            tail->next=cur2;
            tail=tail->next;
            cur2=cur2->next;
        }
    }
    if(cur1)
        tail->next=cur1;
    if(cur2)
        tail->next=cur2;
    struct ListNode* head=guard->next;
    free(guard);
    return head;

}

[牛客]链表分割

 思路:用两个链表,小于x的尾插到一个链表,大于等于x的尾插到另一个链表,然后再链接起来

(最好使用哨兵位,因为可以避免很多非空处理)

 ListNode* partition(ListNode* pHead, int x) {
        struct ListNode* gGuard,*gTail,*lGuard,*lTail;
        gGuard=gTail=(struct ListNode*)malloc(sizeof(struct ListNode));
        lGuard=lTail=(struct ListNode*)malloc(sizeof(struct ListNode));
        gTail->next=lTail->next=NULL;
        struct ListNode* cur=pHead;
        while(cur)
        {
            if(cur->val<x)
            {
                lTail->next=cur;
                lTail=lTail->next;
            }
            else
            {
                gTail->next=cur;
                gTail=gTail->next;
            }
            cur=cur->next;
        }
        lTail->next=gGuard->next;
        pHead=lGuard->next;
        free(lGuard);
        free(gGuard);
        return pHead;
    }

按照思路我们把代码写好后提交,我们会得到下面的提示信息:内存超限

哪里出问题了呢,我们自测一组数据找一下问题:

我们发现很多数据重复输出,出现死循环,说明什么?链表带环。根据自测数据仔细分析上边代码 ,我们分开两个链表尾插时,就拿{1,5,2,7,3,4}举例,两个链表尾插结束后,其实我们的7的next仍然指向3,因为我们在尾插时,是让前一个结点的next指向要尾插的结点,并没有改变要尾插的这个结点的next(反复体会这句话,转折点),所以我们分析出原因后,只需要简单修改就可以了,就是将我们的gTail->next=NULL就可以了。

ListNode* partition(ListNode* pHead, int x) {
        struct ListNode* gGuard,*gTail,*lGuard,*lTail;
        gGuard=gTail=(struct ListNode*)malloc(sizeof(struct ListNode));
        lGuard=lTail=(struct ListNode*)malloc(sizeof(struct ListNode));
        gTail->next=lTail->next=NULL;
        struct ListNode* cur=pHead;
        while(cur)
        {
            if(cur->val<x)
            {
                lTail->next=cur;
                lTail=lTail->next;
            }
            else
            {
                gTail->next=cur;
                gTail=gTail->next;
            }
            cur=cur->next;
        }
        lTail->next=gGuard->next;
        gTail->next=NULL;
        pHead=lGuard->next;
        free(lGuard);
        free(gGuard);
        return pHead;
    }

[牛客]链表的回文结构

 思路:找到中间结点,从中间结点开始,对后半段逆置,然后比较前半段和后半段。

你可能会说需要分奇数个和偶数个进行判断,但其实,这里很妙的地方就是你把后半段逆置之后,前半段最后一个结点还是指向逆置到最后的那个结点(如下图),所以这个思路十分简单。

 

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* cur = head,*newhead=NULL;
    while(cur)
    {
         struct ListNode*next=cur->next;
         cur->next=newhead;
         newhead=cur;
         cur=next;
    }
    return newhead;

}
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;
}
    bool chkPalindrome(ListNode* head) {
        struct ListNode* mid=middleNode(head);
        struct ListNode* rhead=reverseList(mid);
        while(head&&rhead)
        {
            if(head->val!=rhead->val)
                return false;
            head=head->next;
            rhead=rhead->next;
        }
        return true;
    }

  这里的代码复用了上一篇文章中的找中间结点和逆置对应的代码,所以实现起来很快,想了解的也可以再看看上一篇文章。 

[leetcode]160.相交链表

 思路:判断两个链表是否相交,只需要判断尾结点地址是否相同,但由于两个链表的长度不一定长,所以需要先求出两个链表的长度,然后让两个指针分别指向两个链表的头,长的链表对应的指针先走长度之差步,然后两个指针再同时走,第一个地址相同的就是交点。

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* tailA=headA,*tailB=headB;
    int lenA=1,lenB=1;
    while(tailA->next)
    {
        tailA=tailA->next;
        lenA++;
    }
    while(tailB->next)
    {
        tailB=tailB->next;
        lenB++;
    }
    if(tailA!=tailB)
        return NULL;
    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)
    {
        longList=longList->next;
        shortList=shortList->next;
    }
    return longList;

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值