LeetCode初级算法之链表:反转链表和合并两个有序链表

今天记录的这两个是链表操作的两个题目

反转链表

反转一个单链表。

示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

思路一:采用头插法逆置链表

这是我的初版解法,看到这个题目,条件反射想起了头插法,我们知道头插法完成的链表正好是逆序的,正好可以用到这个题目。
具体操作就是,建立一个空链表的头结点,然后遍历一次链表,每一个节点采用头插法的方式插入到空链表。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *dummpyhead = new ListNode(0);
        dummpyhead->next = NULL;

        ListNode *p = head;
        if (p==NULL)   return NULL;
        while(p->next)
        {
            ListNode *q = p->next;
            p->next = dummpyhead->next;
            dummpyhead->next = p;
            p = q;
        }
        p->next = dummpyhead->next;
        dummpyhead->next = p;


        return dummpyhead->next;
    }
};

下面是Python代码的实现,Python代码中利用元组解包赋值的特性可以使得代码更加简洁

"""利用了元组的解包赋值特性"""
class Solution:
    def reverseList(self, head):
        curr, newlist = head, None
        while curr:
            curr.next, newlist, curr = newlist, curr, curr.next     # 这句话是关键
            
        return newlist
"""Python中引入一个头结点"""
class Solution:
    def reverseList(self, head):
        headnode = ListNode(-1)
        while head:
            head.next, headnode.next, head = headnode.next, head, head.next
        
        return headnode.next

时间复杂度O(n),只需要遍历一次
空间复杂度O(1)

思路二: 递归法

这个题可以采用尾递归的方式,对于当前的传进的节点,如果我获得了它后面节点的逆序,即如果我有了head->next的所有逆序了,是不是只需要把head连接到最后面就行了啊,基于这个思想,可以采用尾递归。
看下面的图片:
在这里插入图片描述
考虑一下递归终止的条件:

  • 终止条件是当前节点或者下一个节点==null
  • 在函数内部,改变节点的指向,也就是head的下一个节点指向head 递归函数那句

head->next->next = head

下面看具体代码吧: 或许容易理解一些:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        //当只有一个节点,它的反转就是本身
        if (!head || !head->next)  return head;

        //取出head节点,调用函数自身将head->next为头的链表反转,结果保存在res中
        ListNode *res = reverseList(head->next);

        head->next->next = head;
        head->next = NULL;

        return res;
    }
};

下面是Python代码,Python中使用递归的时候,要注意self.调用自己

class Solution:
    def reverseList(self, head):

        if not head or not head.next:
            return head
        
        res = self.reverseList(head.next)
        

        head.next.next = head
        head.next = None
        
        return res

思路三: 双指针法

这个思路就是遍历一遍,遍历的时候同时逆置链表。
在这里插入图片描述
我们可以申请两个指针,第一个指针叫pre,最初是指向null的。
第二个指针cur指向head,然后不断遍历cur。
每次迭代到cur,都将cur的next指向pre,然后pre和cur前进一位。
都迭代完了(cur变成null了),pre就是最后一个节点了。

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *pre = NULL;
        ListNode *cur = head;
        ListNode *temp = NULL;

        while(cur)
        {
            temp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = temp;
        }

        return pre;
    }
};

合并两个有序链表

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

示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

思路一: 迭代法

这是我初版的思路,拿到这个题,就想起迭代,建立一个只有头结点的空链表, 然后两个指针分别指向这两个链表,然后开始比大小,如果p->valval, 那么把p插入空链表(尾插法), 否则q插入空链表(尾插)。最后看看那个还剩下了,剩下的直接放入新链表的末尾,返回。

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        ListNode *sortList = new ListNode(0);
        sortList->next = NULL;

        ListNode *p = l1;
        ListNode *q = l2;
        ListNode *r = sortList;
        while(p && q)
        {
            if (p->val < q->val)
            {
                r->next = p;
                p = p->next;
                r = r->next;
            }
            else
            {
                r->next = q;
                q = q->next;
                r = r->next;
            }
        }
        if (p)
            r->next = p;
        if (q)
            r->next = q;

        return sortList->next;
    }
};

思路二: 递归法(这个是看的官方的题解)

竟然这个题也可以用递归法,就是两个链表合并,首先看看哪个的首元素比较小,如果链表a的小,那么链表a的首元素首先是第一个,然后就是链表a下面的部分与链表b的一个合并,然后依次类推, 看下面的图片
在这里插入图片描述

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if (l1 == NULL)
            return l2;
        else if (l2==NULL)
            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;
        }
    }
};

递归这方面,确实是有点难度,目前对递归掌握的程度还是远远不够,在学习中,关于递归的一些知识,后期再作补充。

发布了66 篇原创文章 · 获赞 67 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/wuzhongqiang/article/details/103335866