单链表习题(3)(超详细)

前言:

  这篇文章将会是小编最近做过的习题总结的最后一篇,这一篇有些习题颇有一些难度,不过小编将会带领读者朋友们一起克服难关,下面废话不多说,开启今天的习题之旅!

目录:

1.链表分割

2.相交链表

3.随机链表的复制(本文最具有难度系数的一道题) 

正文:

1.链表分割

  老规矩,小编先给上链接:链表分割_牛客题霸_牛客网 (nowcoder.com) 

   这个题目给的描述也算是比较直白的,小编概括一下:就是给定一个链表,并且给定一个数,如果链表中的数据小于给定的这个数,那么就把这个链表放在前面,如果结点的数据大于给定的数,那么这个结点就往后放,小编在第一次看到这个题目的时候,脑子里是一点思路也没有,如果这是个数组那么这个题目可以相当于送分题,可以新设置一个数组来存放数据,把比较好的数据直接放到新数组里面就好了,我们以此类推,那么我们可不可以也新设置一个链表来进行同样的操作呢?答案是显然可以的,不过此时我们需要设置两个链表分别存数据,一个存比给定值小的,一个存比给定值大的,我们在分别存放完以后,可以把第一个链表的尾结点直接连接到第二个链表的头结点,此时我们就可以实现对于链表进行分割,下面小编给出更详细的解释:

  首先我们需要新建立两个链表,第一个链表我们暂且叫做newphead,是用来存放比给定值小的结点,第二个链表我们叫做newpphead,当然两个链表也分别有着newplist,newpplist来代替它们进行链表向后移动,此时我们用原链表进行循环,循环条件就是该链表的结点不为空,此时我们通过比较的方式来判断该结点是要放入哪个链表,在进行循环完以后,我们直接让newplist的next结点直接指向newpplist,此时原链表的数据也没有改动,我们也完成了链表的分割,此时直接返回newphead就完成了这个题目,不过我们这个代码的开头先要判断一下给定我们的链表是不是空的,如果是空的,直接返回NULL就好,不然我们可能造成对于空结点的引用,下面小编给出该题目的图文解释来帮助各位理解:

  我们需要先设置好两个链表:

  此时我们开始进行循环比较,在这里我们给定x是5,所以2比5小放入第一个链表中 :

 

  继续往后走,6比5要大,所以放入到第二个链表中:

 

  之后我们继续往后走,4要比5小,所以放入第一个链表:

  再往后循环的话原来的链表直接循环倒空了,所以此时停止循环我们把两个链表接起来:

  此时我们已经完成了链表的分割,下面小编给出代码和运行图:

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) {
        // write code here
        if (pHead == NULL)
        {
            return NULL;
        }
        ListNode * newphead,* newplist,*newpphead,*newpplist,*pour = pHead;
         newplist = newphead = (ListNode*)malloc(sizeof(ListNode));
        newpplist =  newpphead = (ListNode*)malloc(sizeof(ListNode));
        while(pour)
        {
            if(pour -> val < x)
            {
                newplist -> next = pour;
                newplist = newplist -> next;
            }
            else {
            newpplist -> next = pour;
            newpplist = newpplist -> next;
            }
            pour = pour -> next;
        }
        newpplist -> next = NULL;
        newplist -> next = newpphead -> next;   //要首尾相连,我这里弄错了
        ListNode * p = newphead -> next;
        free(newphead);
        free(newpphead);
        newphead = newpphead = NULL;
        return p;

    }
};

   这个题目其实难度不算太大仔细看起来,只要我们想到可以新建一个链表来进行分割操作,那么剩下的操作就是非常容易,写起来那是行云流水,各位读者朋友一定要好好的了解这个题目是怎么做的,下面紧跟小编的步伐,开始下个题目之旅~~

2.相交链表

  老规矩,小编先给大家题目链接:160. 相交链表 - 力扣(LeetCode)

  这个题目乍一看是有一点难度,其实难度并没有很大,这个题目是让我们寻找中间节点,从例题中我们知道我们想要找到中间节点,就要让两个链表长度一样,这样让它们同时进行向下查找,就可能会找到中间节点,小编当时也是这么想着做的,只不过当时想的没有那么到位,下面小编将要带领各位读者朋友们去解决这个问题。

  首先正如小编之前说的,我们需要让两个链表的长度保持一致,所以小编的建议就是先找到最长的链表,先让这个最长的链表向后走,走的长度就是长的链表减去短的链表的长度,所以为了这么做,我们需要先判断链表的长短,小编的做法就是直接先让第一个链表是长的,第二个链表是短的,我们在使用循环来分别计算两个链表的长度以后,我们在判断一下哪一个到底是长链表,如果第一个长,那么就保持不变,反之直接让长链表是第二个,短是第一个就好了,此时我们先让长链表往后走,走完两个链表之差以后,我们在使用一个循环,让两个链表一起往后走,如果两个链表的元素相同的话,那么就遇到了相交结点,我们直接返回就好,如果循环结束还没找到,直接返回空就好了在循环结束以后,此时这个题目算是解答完了,小编还是通过图文让各位读者朋友有着更好的理解(以例题为例)。

  首先我们先确认长短链表,这里小编就不展示了,下面直接看最后结果:

  之后我们先让long1先走6 - 5 == 1步:

   此时二者处于同一起跑线,让这俩同时往后走来找相交结点,这里往后走一步:

  此时没有找到,继续往后走:

   此时我们已经找到了相交的结点,此时我们直接返回long1或者short1就好了,此时我们这个题目已经完成了图文描述,下面小编给大家展示一下这个题目的代码以及提交图:

 typedef struct ListNode ListNode;
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    ListNode * p1 = headA,* p2 = headB;
    int count1 = 0,count2 = 0;
    while(p1)
    {
        count1++;
        p1 = p1 -> next;
    }
    while(p2)
    {
        count2++;
        p2 = p2 -> next;
    }
    int c = abs(count1 - count2);
    ListNode * short1 = headA, * long1 = headB;
    if(count1 > count2)
    {
        long1 = headA;
        short1 = headB;
    }
    while(c--)
    {
        long1 = long1 -> next;
    }
    while(long1)
    {
        if(long1 == short1)
        return short1;
        short1 = short1 -> next;    //long 和 short 都属于关键字,不可以作为变量名
        long1 = long1 -> next;
    }
    return NULL;
}

 

3.随机链表的复制

  先给上链接:138. 随机链表的复制 - 力扣(LeetCode) 

  这个题目是最近小编做过的难度系数最大的题目了(可能是我太菜),这个题目我当时看的时候,真的就一点思路也没有,脑子里空空如也,如果只是一个单纯单链表复制的话,那么这个题目难度还没有那么夸张,但是问题就是这个题目的链表,已经不是一个单纯的单链表了,这个链表比起一般的链表,它多了一个random,这是个随机指针,这个题目最大头的部分就是解决这个随机指针的问题,下面小编先来讲述一下,我们首先除去随机指针以外链表的复制,至于随机指针,我们在复制完一次链表以后难度系数就下降了。

  首先,我们可以先新建立一个函数,这个函数的作用是用来创立新节点的,我们在每一个结点后面都插入一个结点,这个结点完完全全除去随机指针以外全部复制上一个结点的,我们就拿第一个结点为例,我们让新建的结点的数据是第一个结点的数据,让它的下一个结点指向第二个结点,让第一个结点的下一个结点指向新建结点,此时我们就建立了一个类似第一个结点的结点,此后我们在经历一次次的循环后,最后会呈现下图的效果:

  之后我们就要处理随机指针的问题了,经过上述那一步以后其实这个随机指针就好处理很多,首先我们要先确保此时随机指针指向的并不是空,所以此时我们可以拿原链表第二个链表为例子(13)。此时我们让复制好的结点的随机指针指向原链表第二个链表的随机指针的下一个链表,此时我们便可以建立起新链表之间的联系,因为原链表的下一个链表都是新建立的复制原链表的链表,所以我们在经过循环操作以后,便可以实现随机指针的指向,下面看图:

  画的略微有点潦草,各位读者朋友见谅,我们最后的操作就是断开与原链表的联系,让复制好的结点们组成新的链表,这个操作算是最为简单的操作,小编也不多加赘述了,各位读者朋友看完代码就会明白这一步,下面直接展示代码图:

typedef struct Node Node;
Node * Slistbuycode(int x)
{
    Node * p1 = (Node * )malloc(sizeof(Node));
    p1 -> next = NULL;
    p1 -> val = x;
    p1 -> random = NULL;
    return p1;

}
void fuzhijiedian(Node * head)
{
    Node * pour = head;
    while(pour)
    {
        Node * p1 = Slistbuycode(pour -> val);
        p1 -> next = pour -> next;
        pour -> next = p1;
        pour = pour -> next -> next;
    }
}

void suijijiedian(Node * head)
{
    Node * pour = head;
    while(pour)
    {
        if(pour -> random != NULL)
        {
        pour -> next -> random = pour -> random -> next;
        }
        pour = pour -> next -> next;
    }
}
struct Node* copyRandomList(struct Node* head) {
    if(head == NULL)
    return NULL;
	//首先要先复制一遍结点
    fuzhijiedian(head);
    //这里开始定义随机结点
    suijijiedian(head);
    //断开链接,完成复制
    Node * newhead = head -> next,*newlist = head -> next;
    Node * pour = head;
    while(pour -> next -> next)
    {
        pour = pour -> next -> next;
        newlist -> next = pour -> next;;
        newlist = newlist -> next;
    }
    return newhead;
}

  小编虽然这个题目解释是最少的,但是不代表这个题目简单,别看小编说的那么容易,实际伤害代码的实现还是考虑一定的基本功的,以及这个题目实现的思想,这个题目如果想明白了,难度系数会变小,这个题目就是难在这个如何实现的思想,希望读者朋友们可以好好的去理解~ 

 

总结:

  可算结束完习题篇的讲解了,小编讲述习题还是感觉是很生疏的,所以此篇文章感觉写的不算太过完美,小编上个月本来想写十篇的,但是还是暑假偷懒了,小编决定这个月完成上个月的遗憾,要写11篇!如果文章出现错误,请在评论区指出,小编一定会及时听大家给的意见,那么我们下篇文章见啦!

 

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值