【算法】链表常见算法1

【算法】—— 链表常见算法1

1. 链表原地删除结点

1.1 问题描述

​ 请编写一个函数,用于删除单链表中某个特定节点 。在设计函数时需要注意,你无法访问链表的头节点 head ,只能直接访问要被删除的节点 。

​ 题目数据保证需要删除的节点不是末尾节点 。

示例

输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
输入:head = [4,5,1,9], node = 1
输出:[4,5,9]

1.2 实现思路

​ 由于参数中没有头结点,所以无法通过头结点遍历找到pos结点的上一个,所以我们将pos结点的下一个结点的值赋给pos,删除pos的下一个结点

在这里插入图片描述

1.3 代码实现

void deleteNode(struct ListNode* pos)
{
    struct ListNode* posnext = pos->next;
    pos->val = posnext->val;
    pos->next = posnext->next;
    free(posnext);
}

2. 链表原地插入结点

2.1 问题描述

​ 设计一个函数,能够在特定结点前插入结点,无法访问链表头结点,只能访问该特定结点

​ 题目保证该特定节点不是最后一个结点

2.2 实现思路

​ 由于无法访问头结点,所以也不能遍历找到它的下一个节点,依然使用替换法将pos结点和被插结点交换数值,将pos结点作为被插结点插入原被插结点的后面

链表插入结点

2.3 代码实现

//链表的结点声明
struct ListNode
{
    int val;
    struct ListNode* next;
}
void InsertNode(struct ListNode* pos, struct ListNode* newnode)
{
    int temp = pos->val;
    pos->val = newnode->val;
    newnode->val = temp;
    
    newnode->next = pos->next;
    pos->next = newnode;
}

3. 移除链表元素

3.1 问题描述

​ 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回新的头节点

​ 示例:

img

3.2 实现思路

方法一(原地删除法)

​ 遍历链表,将val值的结点删除,为了方便删除,我们加入临时的头结点

原地删除1

原地删除2

代码实现

//链表的结点声明
struct ListNode
{
    int val;
    struct ListNode* next;
}
struct ListNode* removeElements(struct ListNode* head, int val)
{
    if (head == NULL)
    {
        return head;
    }
    
    struct ListNode temp = {0};
    temp.next = head;
    struct ListNode* prev = &temp;
    struct ListNode* cur = head;
    while (cur != NULL)
    {
        if (cur->val == val)
        {
            prev->next = cur->next;
            free(cur);
            cur = prev->next;
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }
    
    head = temp.next;
    return head;
}

方法二(重组链表法)

  1. 创建一个头指针,将原链表中值不为val的结点放在新头指针上
    重组链表1

  2. 遍历链表,若是该值不为1,则将其插入到新链表上
    重组链表2

重组链表3

  1. 链表遍历完毕将新链表返回即可
    重组链表4
  2. 原链表在遍历过程中可以将其值为1的结点释放掉

代码实现

//链表的结点声明
struct ListNode
{
    int val;
    struct ListNode* next;
}
struct ListNode* removeElements(struct ListNode* head, int val)
{
    if (head == NULL)
    {
        return head;
    }
    
    struct ListNode* newhead = NULL;
    struct ListNode* tail = newhead;
    struct ListNode* cur = head;
    while (cur != NULL)
    {
        if (cur->val != val)
        {
            if (newhead == NULL)
            {
                newhead = cur;
                tail = cur;
            }
            else
            {
                tail->next = cur;
                tail = tail->next;
            }
            cur = cur->next;
        }
        else
        {
            head = cur->next;
            free(cur);
            cur = head;
        }
    }
    
    if (tail != NULL)
    {
        tail->next = NULL;
    }
    return newhead;
}

4. 合并两个有序链表

4.1 问题描述

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

img

示例 1:

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

示例 2:

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

4.2 实现思路

​ 以其中一个链表l1作为最终返回链表,依次遍历,与另一条链表l2的第一个结点比较,若是大于l1当前结点,就将这个结点插入到l1结点之前

  1. l1链表上创建一个头结点,方便插入

合并1

  1. 此时3大于2,将值为2的结点插入值为3的结点之前,然后继续遍历

合并2

合并3

  1. 继续遍历,将l2的值插入到l1

合并4

  1. l1中没有比l2大的值时,将l2剩余的链表插入到l1的末尾,并将结果返回

合并5

4.3 代码实现

//链表的结点声明
struct ListNode
{
    int val;
    struct ListNode* next;
}
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    struct ListNode temp = {0};
    temp.next = list1;
    struct ListNode* prev = &temp;
    struct ListNode* cur1 = list1;
    struct ListNode* cur2 = list2;
    
    while (cur1 != NULL && cur2 != NULL)
    {
        if (cur1->val > cur2->val)
        {
            list2 = cur2->next;
            cur2->next = cur1;
            prev->next = cur2;
            prev = prev->next;
            cur2 = list2;
        }
        else
        {
            prev = cur1;
            cur1 = cur1->next;
        }
    }
    
    if (list2 != NULL)
    {
        prev->next = list2;
    }
    
    list1 = temp.next;
    return list1;
}

5. 反转单链表

5.1 问题描述

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

示例

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

5.2 实现思路

方法一(重组新链表)

​ 创建新头指针,遍历链表,依次将链表结点头插到新链表上

  1. cur结点的下一位指向newhead,此时cur成为头指针

反转1

反转2

  1. newhead指向cur,此时newhead成为头指针

反转3

  1. cur继续遍历链表,next记录下一个位置

反转4

  1. 返回新链表

代码实现

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

    while (cur != NULL)
    {
        next = cur->next;
        cur->next = newhead;
        newhead = cur;
        cur = next;
    }
    return newhead;
}

方法二(反转链表指向)

​ 依次遍历链表,将链表的指向依次反转过来

  1. 定义prevcurnext指针分别指向前一个、当前、下一个结点

反转二1

  1. 将当前结点的下一个指针指向前一个结点,完成反转

反转二2

  1. 将三个结点依次向后移动一位

反转二3

  1. 循环结束时当前结点为NULL,前一个结点为最后一个遍历的结点,也是反转后链表的第一个结点,将前一个结点返回

代码实现

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

    while (cur != NULL)
    {
        next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }
    return prev;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值