【LeetCode easy系列】leetcode 21 Merge Two Sorted Lists【附c++ ->和. 的区别以及递归简介】

leetcode 专栏收录该内容
6 篇文章 0 订阅

   昨天写了做了几个list相关的leetcode的练习,今天再记录一个和list有关的题目,顺便练习一下递归操作。

目录

1、题目描述如下:

1.1 c++ -> 和. 操作符

1.2 算法思路(新建list,不递归) 

2、递归简介

2.1 递归思路


1、题目描述如下:

 

 

 

 

 

 

 

 

 

在介绍代码之前先来看一下leetcode中的单链表是怎么定义的。


// Definition for singly-linked list.
   struct ListNode {
       int val;
       ListNode *next;
       ListNode(int x) : val(x), next(NULL) {}
   };
 

单链表的定义方式如上述结构体所示。其主要定义了节点(node)的值val,以及该节点处(指向下一个位置)的指针next。代码第三行表示,可以采用初始化列表的方式来初始化一个链表,其形式如下:

ListNode dummy = ListNode(0);

利用这种方式进行初始化的时候,可以指定val的值,并且自动将next指向NULL。(在第一次做这个题的时候就因为这个返回值一直都是NULL,因为dummy是个空节点,其后指针也是NULL。我定义一个ListNode的指针,其指向了dummy.next,返回值也是dummy.next,,所以一直是空。)在做题的时候,还遇到了->操作符和.操作符。现先在总结其用法再对该题目进行讲解。 

1.1 c++ -> 和. 操作符

      

   ListNode dummy = ListNode(0);
/* 此处不能 ListNode *l3 = dummy.next;
 * 因为在初始化dummy的时候,dummy.next指向的是空指针,
 * 这样return的时候就只能得到空 */     
   ListNode *l3 = &dummy; 

      第一句,声明了一个ListNode的对象 dummy,并用初始化列表的方式对其进行初始化,然后又定义了一个ListNode类型的指针l3,指向了dummy的首地址。 dummy和l3都可以用来操纵val的值,但是两者操纵的方式不一样。

      dummy.next 合法,l3.next不合法。

      dummy是定义的一个结构体,对其成员变量的访问采用 . 操作符来进行,而l3是定义的一个指针,其对成员变量的访问需要采用 ->操作符来进行。网上有人总结说:

箭头(->):左边必须为指针; 
点号(.):左边必须为实体。

网上有同学摘自c++ primer中的讲解如下:https://blog.csdn.net/fulima007/article/details/6327067

 

1.2 算法思路(新建list,不递归) 

      这种方法的思路比价简单,比较第一个list中的元素和第二个list中元素的大小,新建的list每次指向比较小的节点即可。

注意:1、题目中要求不能采用新建节点的形式。

           2、指针的操作,需要自行检查空指针的情况!(一般的编译器,空指针会报错,越界不能)

代码如下:

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode dummy = ListNode(0);
        //此处不能 ListNode *l3 = dummy.next; 因为在初始化dummy的时候,dummy.next指向的是空指针,这样return的时候就只能得到空
        ListNode *l3 = &dummy; 
        while(l2 && l1) {
            if(l1->val < l2->val) {
                //l3->val = l1->val;
                l3 ->next = l1;
                l1 = l1->next;
            }
            else {
                //l3->val = l2->val;
                l3 -> next = l2;
                l2 = l2->next;
            }
                l3 = l3->next;
        }
        if(l1) {
            l3->next = l1;
        }
        if(l2) {
            l3->next = l2;
        }
        return dummy.next;
    }
};

  

2、递归简介

      递归最简单的理解就是自己调用自己。有一篇简单介绍递归的博文,写的比较好,很适合初学者。

       https://blog.csdn.net/hustyangju/article/details/22758637 

       递归是一个比较常用的工具,但是因为递归要进行大量的堆栈操作,使用递归一般会进行优化,以后有时间专门出一个尾递归相关的专题,顺便提高一下自己对递归的理解程度。      

      本题目也可以用递归来实现,虽然在实现的时候递归操作不是最快的(递归8ms,不递归4ms),但是因为本题是对链表进行操作,所以递归的时间复杂度也是O(n)。因为链表不需要对递归后的结果进行重新排列。

2.1 递归思路

     1、找到list 1 和 list 2 中比较小的节点,假如list 1 的第一个节点的值小于 list 2 中第一个节点的值,那么就将 list 1 和 list 2 都合并到list 1,其首节点就是list 1 的首节点,然后list 1 的next中存放 list 1 的第二个节点和list 2首节点中较小的那个,然后指针继续向后移动一直到有一个列表为空(就像穿糖葫芦一样,将两串排列好的糖葫芦重新从小到大依次穿起来)

     2、当有一个list 为空以后,将重新排列的list的指针指向另一个非空的list即可,排列结束。

结合代码更容易理解,如下所示:

class Solution {
  public:
    ListNode* mergeTwoLists(ListNode *l1, ListNode *l2) {
        if(!l1 or !l2) return l1 ? l1 : l2; //必不可少,递归的过程中,如果某个列表空了,那/么返回没有空的那个列表剩余元素的首地址
        if(l1->val < l2->val) {
            l1->next = mergeTwoLists(l1->next,l2);
            return l1;
        }
        else {
            l2->next = mergeTwoLists(l1,l2->next);
            return l2;
        }
    }
};

注意:1、上述代码中,递归的终止条件为其中有一个list为空。此时返回那个非空列表的首地址即可(递归结束以后的首地址)。

           2、每一层递归都能得到一个结果,都得有一个返回值,因此不论是if中还是在else中,都得返回本次递归得到的结果,即两个list中较小的那个,可以这样理解,每次的比较(即每一层递归),都相当于剔除一个最小的节点,这样两个list中剩余的节点就会越来越少。一直剔除到一个list中没有节点的时候,递归结束。剔除掉的元素都通过指针串接起来,得到了一个新的长list。递归的终止条件就表示,将那个还有节点的list一起串到新的list中。

也有人写了如下的递归代码:(整体思想都是一样的,只不过是又新建了一个list而已)

class Solution {
  public:
    ListNode* mergeTwoLists(ListNode *l1, ListNode *l2) {
        if(l1 && l2) {
            if(l1->val < l2->val) {
                ListNode* l3 = l1;
                l3->next = mergeTwoLists(l1->next,l2);
                    return l3;   
            }
            else {
                ListNode* l3 = l2;
                l3->next = mergeTwoLists(l1,l2->next);
                return l3;
            }
        }
        if(l1) return l1;
        if(l2) return l2;
        return NULL;
    }
};

 

文中如果有不对的专业名词,恳请大家批评指教。       

 

   

  • 1
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页

打赏作者

mzqolive

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值