记录力扣刷题第六题:
本文包含C++三种解法和Python与Java的主观认为的最优解法
题目描述如下
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
如图
来源:LeetCode
思路:
其实就是个单链表的删除操作,对数据结构有一些了解的都知道该怎么做吧。关键的一点是要注意对头节点的删除操作,如果用传统方法,只需要把head向后移即可,可是这样就需要单独写对头节点的删除函数。代码如下:
/**
* 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) {
//首先写对头节点的操作
//注意也要判断head不为空,否则当head为空时运行会出错
while(head != nullptr && head->val == val) {
//注意这里要用while不是if,以防出现连续值等于val的节点都为头节点
ListNode* temp = head;
head = head->next;
//对删除的节点进行释放
delete temp;
}
cur = head;
//再写对非头节点的操作,注意while里要先判断cur不为空
//如果cur为空的话也就没有next,执行就会出错
while(cur != nullptr && cur->next != nullptr) {
if(cur->next->val == val) {
ListNode* temp = cur->next;
cur->next = temp->next;
//对删除的节点进行释放
delete temp;
}
else {
cur = cur->next;
}
}
return head;
}
};
时间消耗主要用于查找到待删除节点的前一个节点,因此时间复杂度为O(n),没有额外的空间消耗,空间复杂度为O(1)。
但是对头节点和非头节点分开操作真的好麻烦呀,有没有能够将两者统一的方式呢?显然是有的,只需要在头节点前面再加一个头节点
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//创建一个虚拟头节点
ListNode* VHead = new ListNode(-1);
VHead->next = head;
ListNode* cur = VHead;
//cur必定不为空
while(cur->next != nullptr) {
if(cur->next->val == val) {
ListNode* temp = cur->next;
cur->next = temp->next;
//对删除的节点进行释放
delete temp;
}else {
cur = cur->next;
}
}
head = VHead->next;
//注意对自己创建的节点也要进行释放
delete VHead;
return head;
}
};
时间复杂度为O(n),空间复杂度为O(1)。
这样是不是简洁美观多啦,运行时间也减少了,其实下面还有更简洁的方法,但我主观认为这是最好的解法。
下面介绍一个更简洁的方法:递归写法
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if(head == nullptr) {
return head;
}
//递归调用removeElements
head->next = removeElements(head->next, val);
if(head->val == val) {
ListNode* temp = head;
head = head->next;
delete temp;
}
return head;
}
};
时间复杂度:O(n)。
空间复杂度:由于递归调用栈,栈的层数最多不超过n层,因此为O(n)。
但链表很长时会有爆栈的风险,而且这种方法空间复杂度比较大,因此不推荐用。
所以我主观认为最好的解法还是添加头节点法。
下面放上Java和Python的添加头节点法
Java
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode VHead = new ListNode(-1);
VHead.next = head;
ListNode cur = VHead;
while(cur.next != null) {
if(cur.next.val == val) {
cur.next = cur.next.next;
}
else {
cur = cur.next;
}
}
head = VHead.next;
return head;
}
}
Python
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def removeElements(self, head: ListNode, val: int) -> ListNode:
VHead = ListNode()
VHead.next = head
cur = VHead
while cur.next != None:
if cur.next.val == val:
cur.next = cur.next.next
else:
cur = cur.next
head = VHead.next
return head