【26】合并两个有序链表 | 删除排序链表中的重复元素(LeetCode 21 | 83)

合并两个有序链表

问题描述

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

解题思路

首先想到的是逐个扫描L1的结点,对于每个结点,在L2中找到前者小于等于它,后者大于它的位置,把L1的结点插入进入。为此,需要给L2加一个头结点,头结点的值赋为-101,即题目给出的结点的最小值-1。但是没能用代码实现…

以下来自官方题解:

迭代法:

设定一个哨兵节点 prehead 用于最后返回结果。维护一个 prev 指针,每次将其next指向当前l1和l2的结点值小的一个,重复,直到 l1 或者 l2 指向了 null 。结束时再把未遍历完的链表接在最后。

class Solution {
    
    
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    
    
        ListNode* preHead = new ListNode(-1);

        ListNode* prev = preHead;
        while (l1 != nullptr && l2 != nullptr) {
    
    
            if (l1->val < l2->val) {
    
    
                prev->next = l1;
                l1 = l1->next;
            } else {
    
    
                prev->next = l2;
                l2 = l2->next;
            }
            prev = prev->next;
        }
        // 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev->next = l1 == nullptr ? l2 : l1;

        return preHead->next;
    }
};

递归:

在这里先分享一个有意思的东西,公众号看到的大佬分享的递归中debug的方法,虽然在这题当中没用到,不过不影响我想安利它——递归debug小技巧

本题的递归思想:

如果 l1 或者 l2 为空,则无需合并, 直接返回非空链表。否则,若l1当前的值小于l2,则将l1的next指向l1的next和l2合并后的链表(即把大的值接在小的值后面);反之则是把l2的next指向l2的next和l1合并后的链表。

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

删除排序链表中的重复元素

问题描述

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

解题思路

想到的是用cur指针指向当前结点,用一个temp存下当前结点的值,然后用下一结点值与其比较,若相当则删除下一结点,若不相等则cur指针前进一。

class Solution {
    
    
public:
    ListNode* deleteDuplicates(ListNode* head) {
    
    
        if(!head)   return head; //链表为空
        ListNode* cur = head;
        int temp;//存储当前结点的值
        while(cur->next){
    
    
            temp = cur->val;
            if(cur->next->val == temp){
    
    //若下一个结点的值与当前结点值相等则删除下一结点
                ListNode* p = cur->next;
                cur->next = p->next;
                delete p;
            }else{
    
    //否则cur前进一个结点
                cur = cur->next;
            }
        }
        return head;
    }
};

时间复杂度:O(n) 把所有的结点都遍历一遍。
空间复杂度:O(1)

心得

555…链表好难…感觉还没有找到链表的做题感觉,尤其在做合并两个有序链表的时候,图画出来手动把流程走了一遍,最后编译的结果还是有错,考验耐心呀。然后,链表题的递归解法果然很难想出来!主要是用递归在做其他类型的题的时候可以抛开数据结构的细节,直接理解递归的含义,但是面对链表,不太能想清楚它是怎么递归的,向下递归和向上返回时链表是怎样被断开和合并的?

猜你喜欢

转载自blog.csdn.net/qq_43424037/article/details/113405911