几道链表经典算法题

一、移除链表元素

(Leetcode 203)

1.1 题目
1.2 解题思路

思路一:可以遍历原来的链表,将值为val的节点释放掉。

可以定义一个指针变量pcur,让它从头节点开始遍历链表,如果节点存储的数据值不为val,就将这个值尾插到新链表中。由于我们要把删除的节点的前后两个节点连起来,所以我们还要定义变量prev来标记pcur前面的那个节点。

思路二:可以找值不为val的节点,尾插到新链表中。

由于思路二比较简洁,我们按照思路二来进行代码实现。

1.3 代码实现

首先我们创建两个结构体指针newHead和newTail,分别代表新链表的头节点和尾结点。把它们先都置为空,就相当于创建好了一个新的链表。

接着我们创建一个指针变量pcur来遍历原来链表中的所有节点,判断该节点存储的数据不为空时就将这个节点插入新链表中。

注意此时新链表可能为空,为空的情况需要拿出来特别处理一下:只需让newHead和newTail都指向pcur所在的这个节点即可。

不为空,我们就让newtail的next指向pcur所在的节点,相当于把这个节点尾插到了新链表中。

完成判断和尾插之后我们让pcur沿着原链表往后走一个节点。遍历了所有元素后,即pcur为空的时候,循环结束。

但此时还需要注意,如果原链表的最后一个节点存储的数据恰好是需要被删除的,那么newTail的next仍然指向这个节点,也就是新链表中包括了这个节点。为了避免这种情况,我们需要在遍历结束后把newTail的next置为空。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

 typedef struct ListNode ListNode;
struct ListNode* removeElements(struct ListNode* head, int val)
{
    //创建一个空链表
    ListNode* newHead,*newTail;
    newHead = newTail = NULL;
   
    //遍历原链表
    ListNode* pcur = head;
    while(pcur)
    {
        //找值不为value的节点,尾插到新链表中
        if(pcur->val != val)
        {
            //链表为空
            if(newHead == NULL)
            {
                newHead = newTail = pcur;
            }
           else
            {
                //链表不为空
                newTail->next = pcur;
                newTail = newTail->next;
            }
        }
        pcur = pcur->next;
    }
    if(newTail)
     newTail->next = NULL;
    return newHead;
}

二、反转链表

(Leetcode 206)

2.1 题目
2.2 解题思路

思路一: 创建新链表,将原链表中的节点拿过来头插。比较麻烦。

思路二:用三个指针变量解决问题。

首先创建三个指针n1、n2和n3,n1先置为空,n2指向头结点,n3指向第二个节点。

此时我们让n2的next指针不再指向n3,而是指向n1。

然后我们让n1、n2、n3都往后走一个节点。

此后再重复以上的步骤,直到完成链表的反转。

2.3 代码实现

按照上述思路,代码写起来顺理成章。需要注意的是,如果原链表为空,定义n3的过程会涉及对空指针的解引用。为了避免,我们要特别处理链表为空的情况:此时直接返回head即可。

以及,我们让n3向后移动的时候,如果n3已经走到链表末尾,也会导致对空指针的解引用。那我们就加个判断,确保n3的next指针不为空再让n3向后移动。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* reverseList(struct ListNode* head)
{
   //判空
   if(head == NULL)
   {
    return head;
   }
   //创建三个指针
   ListNode*n1,*n2,*n3;
   n1 = NULL,n2 = head,n3 = n2->next;
   while(n2)
   {
    n2->next = n1;
    n1 = n2;
    n2 = n3;
    if(n3)
        n3 = n3->next;
   }
   return n1;
}

三、链表的中间节点

(Leetcode 876)

3.1 题目

3.2 解题思路

思路一:遍历节点,创建变量count来记节点数,返回count/2节点的next节点。

思路二(快慢指针):创建两个指针变量slow和fast,刚开始让两个指针都指向头节点,让后让两个指针以不同的速度沿着链表向前移动,slow每移动一个节点,fast移动两个节点。当fast指针为空(链表节点个数为偶数)或者fast->next为空(链表节点个数为奇数)时,slow指针指向的节点就恰好是所要求的中间节点。

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode* middleNode(struct ListNode* head)
{
    //创建快慢指针
    ListNode* slow = head;
    ListNode* fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        //此时slow正好指向中间节点
    }
    return slow;
}

此处注意,while括号中的两个条件顺序不能交换。因为如果链表节点个数为偶数,最后一次fast会直接指向空,此时对fast进行解引用会报错。目前顺序下,如果fast为空,fast->next会被短路掉,不会执行,也就不会报错。

END

相关推荐
一去不复返的通信er5 小时前
力扣-图论-岛屿数量
算法·leetcode·图论
程序 代码狂人8 小时前
切片表与分区表的区别(数据开发面试版)
java·大数据·开发语言·数据仓库·算法
HDU-Dade9 小时前
【校招笔试真题】简要介绍 Self-Attention 的前沿算法
人工智能·算法
Zwawa10 小时前
编译器工具链:GCC、Clang、MSVC、ICC、armclang
c语言·嵌入式·编译器
学逆向的10 小时前
C 指针常量与常量指针
c语言·开发语言·网络安全
水饺编程11 小时前
第2章:Linux 基础,exit 命令
linux·c语言·汇编
小郎君65811 小时前
Vue keep-alive 切标签后滚动位置回顶?因为 onDeactivated 里读到的 scrollTop 是 0
算法
邵奈一11 小时前
GPT-6.1 Terra速度是Sol的3倍,价格减半
算法
RaaS10011 小时前
Node.js Brotli RCE 漏洞自查指南:fetch、undici、axios 都在影响面,三分钟确认你的版本
人工智能·深度学习·算法