程序笔记   发布时间:2022-07-14  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了通过这6道初级链表题检验你的链表知识是否合格大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

文章目录

  • 前言
  • 01-删除链表元素
  • 02-反转链表
  • 03-查找链表中间结点
  • 04-链表倒数第K个结点
  • 05-合并两个有序链表
  • 06-链表分割

前言


学习完毕链表(单双链表)后,博主更新了6道比较有意思的链表题,并且全部配以图解文字说明,对于大家有一定的帮助,谢谢支持哦~


01-删除链表元素

给你一个链表的头节点 head 和一个整数 valc;请你删除链表中所有满足 Node.val == val 的节点࿰c;并返新的头节点

通过这6道初级链表题检验你的链表知识是否合格

示例:1

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例:2

输入:head = [], val = 1
输出:[]

示例:3

输入:head = [7,7,7,7], val = 7
输出:[]

迭代法(这种操作和我们学习链表增删查改时候一样):


比较明显,这是一个链表的增删改查里面的删除操作.大家还记得在单链表里面我们要删除一个结点的步骤是什么吗? 没错:

  • 第一步: 找到需要删除的结点前一个结点,即如果cur->next->val等于val,说明cur就是需要删除结点的前一个结点
  • 第二步: 让需要删除结点的前一个结点 与 需要删除结点的后一个结点 连接,即cur->next = cur->next->next
  • 第三步: 释放需要删除的结点.(博主知道不释放内存是一个很糟糕的习惯,但是这里只是在讲解算法题,所以博主就偷懒了)

而大致的迭达方法如下:


如果 cur的下一个节点的节点值不等于给定的 valc;则保留下一个节点࿰c;val移动到下一个节点即可。

cur的下一个节点为空时࿰c;链表遍历结束࿰c;此时所有节点值等于val 的节点都被删除

但是我们还记得吗?在单链表的头删操作中,我们是分了情况进行讨论的,即只有一个结点和多个结点时候,这就会比较麻烦,有没有什么好的操作省去它呢? 有,这个操作就是给原来链表增加一个哑结点


示意图(假设有下面一个链表,需要删除的val是6,绿色移动点代表在判断是否等于val,橙色代表val结点后面一个结点):

通过这6道初级链表题检验你的链表知识是否合格

题解:

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* dummynode = (struct ListNode*)@H_870_169@malloc(sizeof(struct ListNode));  //创建哑结点
    dummynode->next = head; //连接链表
    struct ListNode* cur = dummynode; //迭代结点
    
    while(cur->next) //当下一个为空指针就结束
    {
        if(cur->next->val == val) //如果下一个结点的值等于val,就连接橙色结点
        {
            cur->next = cur->next->next;
        }
        else
        {
            cur = cur->next;
        }
    }
    return dummynode->next;//返回修改后的结点(不要哑结点哦~~)
}

02-反转链表

给你单链表的头节点 headc;请你反转链表࿰c;并返回反转后的链表

通过这6道初级链表题检验你的链表知识是否合格

示例1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例2:

输入:head = [1,2]
输出:[2,1]

示例3:

输入:head = []
输出:[]

我们的正常想法是,先给一个prev指针置为NULL,再给个cur指针赋值head,然后cur指向prev,最后prev和cur依次像下图这样迭代:

通过这6道初级链表题检验你的链表知识是否合格

有问题吗?问题很大,问题出在哪里了?那就是当cur指向prev后,等下一次迭代时,cur就找不到他原来后面的结点,所以说,我们还差一个指针用来保存下一个结点,上代码:

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

03-查找链表中间结点

给定一个头结点为 head 的非空单链表࿰c;返回链表的中间结点。

如果有两个中间结点࿰c;则返回第二个中间结点。

示例1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])

示例2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])

博主这里介绍的是快慢指针法则,即快指针比慢指针多走一步,这样当快指针走完整个链表时候,慢指针就刚好是中间结点.

但有个情况是快指针的结束条件受到结点的奇偶影响.

下面我们分开进行演示:

  • 当结点数是偶数时候,发现fast为空:

通过这6道初级链表题检验你的链表知识是否合格

  • 当结点数是奇数时候,发现fast为尾结点,即fast->next等于NULL:

通过这6道初级链表题检验你的链表知识是否合格

代码:

struct ListNode* @H_870_169@middleNode(struct ListNode* head)
{
    struct ListNode* fast,*slow; //定义快慢指针
    fast = slow = head;
    while(fast && (fast->next))  // 当fast为空,或者fast->next为空,就结束循环,说明此时slow已经走到了中间结点
    {
        fast = fast->next->next;//fast走两步
        slow = slow->next; //slow走一步
    }
    return slow;
}

04-链表倒数第K个结点

实现一种算法࿰c;找出单向链表中倒数第 k 个节点。返回该节点的值。

示例1:

输入: 1->2->3->4->5 和 k = 2
输出: 4

博主这里介绍的还是快慢指针法,但是这里就需要一个变化了~~~,什么变化呢?大家想想上一个题目的快慢指针的差异是什么?

没错,上个题的差异是fast的速度是slow的两倍,而我们这个题目怎么弄呢?没错,可以先让fast走K步,然后slow与fast速度一样

fast走到末尾时候结束(fast->next等于NULL)

如图:

通过这6道初级链表题检验你的链表知识是否合格

代码:

struct ListNode* FindKth@R_152_10586@il(struct ListNode* pListHead, int k ) 
{
    struct ListNode* slow,*fast;
    slow = fast = pListHead;   //创建快慢指针
    
    while(k--)   //fast先走K步
    {
        if(fast)        //注意!!!!这里必须要判断,为什么呢?因为题目并没有规定k是小于等于链表长度的哦
            fast = fast->next;
        else
            return NULL;
    }
    while(fast)  //同步
    {
        fast = fast->next;
        slow = slow->next;
    }
    return slow;
}

05-合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

通过这6道初级链表题检验你的链表知识是否合格

示例1

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例2

输入:l1 = [], l2 = []
输出:[]

示例3

输入:l1 = [], l2 = [0]
输出:[0]

如果这个题是两个数组,大家有没有啥想法? 对,估计大部分的想法都是 新建立一个数组,然后数组上下两两比较,放进新数组.

而我们链表其实也可以这样,我们可以建立一个哑结点,然后让链表1和链表2每个结点进行比较,小的就依次放在哑结点后,如图:

通过这6道初级链表题检验你的链表知识是否合格

代码:

struct ListNode* @H_870_169@mergeTwoLists(struct ListNode* l1, struct ListNode* l2){

    struct ListNode* p1 = l1,*p2 = l2;
    struct ListNode* newhead = NULL,*tail = NULL;

    p1 == NULL ? (return p2):(;); //后面括号是空语句
    p2 == NULL ? (return p1):(;);
    
    newhead = tail = (struct ListNode* )@H_870_169@malloc(sizeof(struct ListNode)); //创建新结点

    while(p1 && p2)  //开始一一比较并进行连接
    {
        (p1-> val) <= (p2->val) ?  //比较谁小
        (tail->next = p1,p1 = p1->next,tail = tail->next) :  //p1的值更小,则连接p1,然后tail和p1迭代
        (tail->next = p2,p2 = p2->next,tail = tail->next);   //p2的值更小,则连接p2,然后tail和p2迭代
    }

    p1 ? (tail->next = p1):(p1);  //如果p1还有值,则把剩下的p1连接
    p2 ? (tail->next = p2):(p2);  //如果p2还有值,则把剩下的p2连接

    struct ListNode* pp = newhead->next;  //保存哑结点后面的结点
    free(newhead); //释放哑结点
    newhead = NULL;
    return pp;
}

06-链表分割

现有一链表,给一定值x࿰c;编写一段代码将所有小于x的结点排在其余结点之前࿰c;且不能改变原来的数据顺序࿰c;返回重新排列后的链表的头指针。

思路:

同时创建两个哑结点,值小于x的结点放在第一个结点后,值大于等于x的结点放在第二个结点后,等原链表放完后,再把第二个哑结点链表后面的数据连接在第一个哑结点链表上,具体过程如图:

通过这6道初级链表题检验你的链表知识是否合格

代码:

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) 
    {
        if(pHead == NULL) return NULL;  //如果原链表是空,就返回空
        
        ListNode* cur = pHead,*dummyone,*dummyTwo,*tailone,*tailtwo,;
        tailone = dummyone = (ListNode*)@H_870_169@malloc(sizeof(ListNode));//创建哑结点
        tailtwo = dummyTwo = (ListNode*)@H_870_169@malloc(sizeof(ListNode));
        
        while(cur) //进行迭代
        {
            cur->val < x ?
            (tailone->next = cur,tailone = tailone->next,cur = cur->next) : //小值放在dummyone链表
            (tailtwo->next = cur,tailtwo = tailtwo->next,cur = cur->next) ; //大值放在dummyTwo链表
        }
        
        tailone->next = dummyTwo->next;//连接
        tailtwo->next = NULL;
        
        return dummyone->next;
    }
};

大佬总结

以上是大佬教程为你收集整理的通过这6道初级链表题检验你的链表知识是否合格全部内容,希望文章能够帮你解决通过这6道初级链表题检验你的链表知识是否合格所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。