算法练习 - 合并两个有序链表

1,935 阅读2分钟

原题

这是LeetCode上的第21号问题,请看leetcode上的题目描述:

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

示例:

输入:1->2->4, 1->3->4

输出:1->1->2->3->4->4

题目意思很清楚,将两个有序链表合并成一个有序链表并返回。

解题思路

涉及到合并的操作,学习过归并排序应该很容易想到使用归并的方法进行合并。如果不了解的话,可以参考下我写的一篇介绍归并排序的文章,了解了归并的思想,解决这道题并不难。

代码实现

注意两个细节

  • 是否需要开辟新的内存空间来存放排好序的结点,我使用的方法并没有开辟新的内存空间,因为无论是否开辟,其时间复杂度都是一样的,同为0(n + m),n为l1的结点个数,m为l2的结点个数。不开辟新的内存空间怎么做呢?这里我是直接在l1或者l2的链表上进行操作,如何选择l1或者l2取决于l1和l2的头结点的大小,l1小,则在l1上操作,l2小,则在l2上操作。
  • 是否使用虚拟头结点,如果使用的话,就不用判断l1与l2的头结点大小来作为合并后的链表的头结点了。这里我没有使用。

具体的解释见代码中的注释。

class Solution{
    
public:
    ListNode *mergeTwoLists(ListNode *l1, ListNode *l2){
        
        // 处理特殊情况 当l1 == NULL 时, 直接返回l2就好了.
        if (!l1) return l2;
        
        // 同理,当l2 == NULL时,直接返回l1就好了,当l1也等于NULL时,返回l1就相当于返回NULL,也没什么问题。
        if (!l2) return l1;
       
        // 这里不使用虚拟头结点
        ListNode *head;
        ListNode *tail;
        
        // 比较L1与l2的大小,确定返回链表的头结点
        if (l1->val <= l2->val) {
            head = l1;
            l1 = l1->next;
        } else {
            head = l2;
            l2 = l2->next;
        }
        
        // 使用tail开始进行`合并`的操作
        tail = head;
        
        // 往后遍历时,如果l1与l2 都还存在结点,则进行比较结点值得大小,小的则拼接在tail的后面。
        while (l1 && l2) {
            if (l1->val <= l2->val) {
                tail->next = l1;
                l1 = l1->next;
            } else {
                tail->next = l2;
                l2 = l2->next;
            }
            
            // 拼接完后,结点后移
            tail = tail->next;
        }
        
        // 在l1 == NULL,或者l2 == NULL 时,拼接剩余的结点。
        tail->next = l1 ? l1 :l2;
        
        // 返回头结点
        return head;
    }
};

如果大家对有些leetcode上的题目有疑问的话,可以参考下我的github:github.com/xiaoswu/Lee…, 上面有一些leetcode上的题目的解答过程。希望能帮到你!