[数据结构与算法刷题]合并两个有序链表&反转链表

72 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第24天,点击查看活动详情

前言

        数据结构与算法刷题专栏第三篇,本文是基于C语言实现的。

        本文就来分享一波作者对单链表的一些习题的学习心得与见解。

        笔者水平有限,难免存在纰漏,欢迎指正交流。

Leetcode21——合并两个有序链表

题目描述

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

链接Leetcode21

示例 1:

输入: l1 = [1,2,4], l2 = [1,3,4] 输出: [1,1,2,3,4,4]

示例 2:

输入: l1 = [ ], l2 = [ ] 输出: [ ]

示例 3:

输入: l1 = [ ], l2 = [0] 输出: [0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

核心代码模式

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{

}

思路分析和代码实现(C语言)

        思路其实很简单,这里用了哨兵结点作为新链表的头结点,用两个指针把两个链表中的结点互相比较,小的先尾插到新链表中,总会有一条链表先插完,没插完的链表把剩下的结点一次尾插到新链表。

        有一些要注意的点,首先创建了哨兵结点,定义了guard指针指向哨兵结点,这时应该立即给哨兵结点的next指针初始化为NULL,不然有野指针的潜在风险。定义两个指针cur1和cur2分别对应list1链表和list2链表,while循环的条件应当是cur1&&cur2,为什么呢?这样的话只要有一个链表先插完就立马跳出循环。在循环结束后要根据哪个链表还没插完,把剩余结点全部尾插到新链表。我们这里使用的辅助结点——哨兵结点是题目没提供的,我们应当在最后将其释放,不过释放前得先把哨兵结点的next指针的内容取出来,作为头指针返回。

代码实现

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    struct ListNode* cur1 = list1;
    struct ListNode* cur2 = list2;
    struct ListNode* guard = (struct ListNode*)malloc(sizeof(struct ListNode));
    guard->next = NULL;
    struct ListNode* tail = guard;
    
    while(cur1 && cur2)
    {
        if(cur1->val > cur2->val)
        {
            tail->next = cur2;
            tail = tail->next;
            cur2 = cur2->next;
        }
        else
        {
            tail->next = cur1;
            tail = tail->next;
            cur1 = cur1->next;
        }
    }

    if(cur1)
        tail->next = cur1;
    if(cur2)
        tail->next = cur2;

    struct ListNode* head = guard->next;
    free(guard);
    return head;
}

Leetcode206——反转链表

题目描述

        给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

链接Leetcode206

示例

示例 1:

输入: head = [1,2,3,4,5] 输出: [5,4,3,2,1]

示例 2:

输入: head = [1,2] 输出: [2,1]

示例 3:

输入: head = [] 输出: []

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

核心代码模式

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* reverseList(struct ListNode* head)
{
    
}

思路分析与代码实现(C语言)

1.新链表头插法

        实际上有一道题和这个基本一样,只是要求的是使用数组,而这里使用的是单链表。思路也是可以借鉴互通的,不妨创建一个新的链表,不过相比于数组,这里不需要开辟新结点,只是把结点间的指向关系进行修改。定义一个新的头指针newHead,定义一个查找当前结点的cur指针,查找下一结点的next指针,只要cur不指向NULL,就把cur指向的结点放到新链表去,怎么放呢?把newHead的内容给cur指向结点的next指针,让它指向newHead原来指向的东西,再把cur指向结点的地址给newHead,让newHead指向该结点,不过要注意在执行这些操作之前要先把cur->next的值放到next去,在执行完后要把next的值再放到cur去。其实也就是让原来的结点从前向后一个一个头插到新链表中。

代码实现

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* cur = head;
    struct ListNode* newHead = NULL;

    while(cur)
    {
        struct ListNode* next = cur->next;
        cur->next = newHead;
        newHead = cur;
        cur = next;
    }

    return newHead;
}

2.三指针逆置法

        有没有想过这样一种方法,既然链表是通过指针联系的,那把指针指向关系逆转了不就能实现反转了吗,用三个指针,分别指向前一结点、当前结点和下一结点(prev、cur、next),只要cur指向不为NULL,就说还有结点没有反转,就把前一结点的地址赋给cur指向的当前结点的next指针,让它指向前一结点,然后三个指针都向后移动一位,不断循环至cur指向NULL。反转后的新链表的头结点由prev指向,返回prev即可。

代码实现

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* prev = NULL;
    struct ListNode* cur = head;
    struct ListNode* next = NULL;

    while(cur)
    {
        next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }
    return prev;
}

以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~

src=http___c-ssl.duitang.com_uploads_item_201708_07_20170807082850_kGsQF.thumb.400_0.gif&refer=http___c-ssl.duitang.gif