几道链表经典算法题

一、移除链表元素

(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

相关推荐
米粒11 天前
力扣算法刷题 Day 42(股票问题总结)
算法·leetcode·职场和发展
zopple1 天前
汇编、C、C++和Java核心技术对比
c语言·汇编·c++
leaves falling1 天前
C/C++ 的内存管理,函数栈帧详讲
java·c语言·c++
浅念-1 天前
从LeetCode入门位运算:常见技巧与实战题目全解析
数据结构·数据库·c++·笔记·算法·leetcode·牛客
CoovallyAIHub1 天前
无人机拍叶片→AI找缺陷:CEA-DETR改进RT-DETR做风电叶片表面缺陷检测,mAP50达89.4%
算法·架构·github
CoovallyAIHub1 天前
混合训练反而更差?VLM Agent在训练前协调跨数据集标注,文档布局检测F-score从0.860提升至0.883
算法·架构·github
鸿途优学-UU教育1 天前
教材质量——法考培训的根基与底气
算法
_深海凉_1 天前
LeetCode热题100-最大数(179)
算法·leetcode·职场和发展
剑挑星河月1 天前
763.划分字母区间
数据结构·算法·leetcode
XY_墨莲伊1 天前
【编译原理】实验二:基于有穷自动机FA词法分析器设计与实现
c语言·开发语言·c++·python