• 【算法专题--链表】合并两个有序链表--高频面试题(图文详解,小白一看就会!!)


    目录

    一、前言

    二、题目描述 

     三、解题方法

     ⭐迭代法 --- 带哨兵位(头节点)

     ⭐递归法

     四、总结与提炼

     五、共勉


    一、前言

            合并两个有序链表这道题,可以说是--链表专题--,比较经典的一道题,也是在面试中频率较高的一道题目,通常在面试中,面试官可能会要求我们写出多种解法来实现这道题目,所以大家需要对这道题目非常熟悉哦!!
            本片博客就来详细的讲讲解一下 合并两个有序链表 的多种实现方法,让我们的面试变的更加顺利!!!

    二、题目描述 

     题目链接:21. 合并两个有序链表 - 力扣(LeetCode)

     三、解题方法

     ⭐迭代法 --- 带哨兵位(头节点)

     首先,先来了解一下什么是  哨兵位---头节点

    • 它是一个附加的链表结点,该 结点 作为第一个节点,它的数据域不存储任何东西,只是为了操作的方便而引入的。
    • 也就是说,如果一个链表有哨兵节点的话,那么链表表的第一个元素应该是链表的第二个节点。 

     哨兵位---头节点的作用:

    •  比如向链表中插入一个节点,对于没有哨兵位单链表当待插入的节点为链表的第一个节点,由于没有前驱,需要进行特殊处理,从而代码的复杂性增加。
    • 如果有哨兵位头节点,则第一个节点的处理方式与其它节点相同,可以统一进行处理

     解题思路:

    •  首先设置一个新的节点 pre_head (哨兵节点) 赋值为-1默认为无数据存在
    • 其次,需要维护一个 cur 指针,即 调整 它的 next指针(cur->next),确保它指向当前新链表 值 较小的节点,初始 cur = pre_head
    • 两个链表的头节点,存放在 list1 list2 ,然后重复操作:如果 list1 所在的节点值 小于 list2 所在节点的值,我们就把 list1 当前的节点接在 cur 节点的后面,同时将 list1 指针向后移。否则 对 list2 做同样的操作。然后将 cur 指针后移一位
    • 当其中一条链表终止的时候, list1list2 至多有一个是非空的。由于两个链表都是有序的,所以无论那个链表非空,它剩下部分包含的所有元素都比前面已经 合并链表中的所有元素都要大。所以我们只需要将非空链表接在合并链表的后面,并返回合并链表。

    图例:       list1: 1 , 2,  4           list2: 3 ,5 , 6

    代码: 

    1. class Solution {
    2. public:
    3. ListNode* mergeTwoLists(ListNode* list1, ListNode* list2)
    4. {
    5. // 创建一个新的链表 ,并初始化头节点 为-1 ---- 这是一个 哨兵位
    6. // 哨兵位,不存储数据
    7. ListNode* pre_head = new ListNode(-1);
    8. // 维护一个 cur 指针,指向当前较小的节点
    9. ListNode* cur = pre_head;
    10. // 注意巧妙的利用 哨兵位的头节点
    11. while(list1!=nullptr && list2!=nullptr)
    12. {
    13. // 将较小的节点进行 尾插
    14. if(list1->val < list2->val)
    15. {
    16. cur->next = list1;
    17. list1 = list1->next;
    18. }
    19. else
    20. {
    21. cur->next = list2;
    22. list2 = list2->next;
    23. }
    24. cur = cur->next;
    25. }
    26. // 判断谁没结束,将它直接连接在 cur->next
    27. if(list1!=nullptr)
    28. {
    29. cur->next = list1;
    30. }
    31. else
    32. {
    33. cur->next = list2;
    34. }
    35. // 注意返回 链表的头节点,而不是 哨兵节点
    36. return pre_head->next;
    37. }
    38. };

     ⭐递归法

     解题思路:

    我们记两个链表的头节点分别为 list1 和 list2,在 合并两个链表 的时候会遇到以下三种情况: 

    • list1 为空,直接返回 list2
    • list2 为空,直接返回 list1;

    两节点都不为空,那么又会分为两种情况: 

    • list1 节点值小于 list2 节点值,那么 list1 节点将会是合并后的节点新的头节点,剩下的部分是 list1->next 和 list2 合并的节点,而合并 list1->next 和 list2 是合并 list1 和 list2 的子问题,也可以使用 mergeTwoLists 函数来解答,于是有 list1->next = mergeTwoLists(list1->next, list2),并返回 list1;
    • 同理,list2 节点值小于 list1 节点值时,有 list2->next = mergeTwoLists(list1, list2->next),并返回 list1

    以上这种将问题转换为原问题的子问题的方法,称为递归方法。递归方法是一种边调用边填充的方法。

    代码: 

    1. /**
    2. * Definition for singly-linked list.
    3. * struct ListNode {
    4. * int val;
    5. * ListNode *next;
    6. * ListNode() : val(0), next(nullptr) {}
    7. * ListNode(int x) : val(x), next(nullptr) {}
    8. * ListNode(int x, ListNode *next) : val(x), next(next) {}
    9. * };
    10. */
    11. class Solution {
    12. public:
    13. ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
    14. if (list1 == nullptr) {
    15. return list2;
    16. }
    17. else if (list2 == nullptr) {
    18. return list1;
    19. }
    20. else if (list1->val < list2->val) {
    21. list1->next = mergeTwoLists(list1->next, list2);
    22. return list1;
    23. }
    24. else {
    25. list2->next = mergeTwoLists(list1, list2->next);
    26. return list2;
    27. }
    28. }
    29. };

     四、总结与提炼

          最后我们来总结一下本文所介绍的内容,本文讲解来一道力扣中有关链表合并的题目,这道题目是校招笔试面试中有关链表章节非常高频的一道题目大家下去一定要自己再画画图,分析一下,把这段代码逻辑自己实现一遍,才能更好地掌握

     五、共勉

          以下就是我对 合并有序链表 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对 链表专题 的理解,请持续关注我哦!!! 

  • 相关阅读:
    技术分享 | 接口自动化测试如何进行认证?
    嵌入式学习笔记(40)看门狗定时器
    【微服务】Ribbon负载均衡
    UML建模语言分析和设计
    jar包中取消乱码的配置
    Js逆向教程-06js逆向断点的种类及介绍
    C/C++ 新生入学管理系统
    Spring Cloud Alibaba-Sentinel规则
    三种常见的存储类型
    Golang 协程、主线程
  • 原文地址:https://blog.csdn.net/weixin_45031801/article/details/139626670