203. 移除链表元素
描述:
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1 输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7 输出:[]
思路:
- 题目本身很简单, 了解链表的知识可以很快写出来, 下边简单见一下自己了解的链表相关知识.
首先, 需要忘了链表这个名称, 在我刚开始学习链表的时候, 觉得这个非常高大上, 于是望而生畏, 但实际上链表不过是一个基础的数据结构, 在项目中, 有很多容器或者自定义的数据结构, 其内部就是用链表实现的, 不如先忽略这个名字.
在忽略掉这个名字之后, 我们先来考虑一个结构体struct A, 这个结构体的成员就两个int n和void *p.也就是说, 这个结构体, 包含一个整型变量和一个void指针, 当我们创建了这个结构体struct A, 也就是自定义了一个类型, 这个类型名就是struct A, 那么现在这样写, 让这个指针更改为指向自定义的类型struct A, 那么struct A可以修改一下(见代码);
struct A
{
int n;
void *p;
};
// 修改后
struct A
{
int n;
struct A *p;
};
其实现在这个结构体struct A就是链表的基础结构, 重新给这个结构体里面的变量取一个正规的名字, 结构体名字就叫做链表的某一个节点, 即ListNode; 里面的整型变量叫做val, 指向struct ListNode类型变量的指针叫做next.
struct ListNode {
int val;
struct ListNode *next;
}
现在可以用更为正式的名词来称呼这个结构体的各个成员了, 这整个结构体ListNode代表链表的一个子节点, 变量val是这个链表的子节点所代表的一个值(即子节点的数据域), 指针next(即链表的指针域)指向另一个(下一个)链表子节点, 下一个也有val和指向下一个节点的指针, 这样一个子节点有保存的值, 同时指向下一个节点, 下一个也指向下下一个, 就像一个链条一样, 一个链着一个, 由若干个这样的子节点链接而成的整个结构, 可以将其称之为: 链表.
如上图, 就是链表的物理结构图, 更准确的说, 这是一个单向链表, 如果再在结构体中添加一个同样指向struct ListNode类型变量的指针, 称为pre, 指向当前字节点的前一个节点, 那么就形成了双向链表.
在单链表中, 最后一个节点的指针域指向空, 如果将最后一个节点的指针指向头节点, 那么就形成了循环链表.
简单的介绍了一下链表的组成, 可以开始做题了, 删除指定节点, 直接遍历, 然后删除指定的节点就ok了, 删除操作如图,
需要考虑一个点, 如果头节点就是需要删除的节点, 由于头节点前面没有节点, 所以操作有所不同, 即需要将头指针指向头节点指向的下一个节点.(头指针: 指向头节点的指针), 但在代码中, 可以自己创建一个虚拟头节点, 让头指针指向虚拟头节点, 虚拟头节点指向真实的头节点, 那么可以将头节点和其他节点一样处理了, 下面的代码使用了虚拟头节点的方法.
代码:
/*
* @lc app=leetcode.cn id=203 lang=cpp
*
* [203] 移除链表元素
*/
// @lc code=start
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution
{
public:
ListNode *removeElements(ListNode *head, int val)
{
ListNode *dummyHead = new ListNode(0, head); // 虚拟头节点
ListNode *temp = dummyHead;
while (temp->next != nullptr)
{
if (temp->next->val == val)
{
ListNode *ttemp = temp->next;
temp->next = temp->next->next;
delete ttemp;
ttemp = nullptr;
}
else
{
temp = temp->next;
}
}
head = dummyHead->next;
delete dummyHead;
dummyHead = nullptr;
return head;
}
};
需要注意, 在C/C++中, 内存相关操作遵循谁申请谁释放的原则, 需要注意内存释放.
- 时间复杂度:O(n),其中 n是链表的长度。需要遍历链表一次。
- 空间复杂度:O(1)。
参考:
[1] 链表理论基础