题目建议:本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接/文章讲解/视频讲解
/**
* 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();
dummyHead->next = head;
ListNode* cur = dummyHead;
while (cur->next != NULL) {
if (cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else {
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};
虚拟头结点,没啥难度。leetcode里默认原结点都是用new创建的,所以都可以用delete删除。
时间复杂度O(n)
空间复杂度O(1)
创建虚拟头结点记得new,删除节点 记得delete。
建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
题目链接/文章讲解/视频讲解
class MyLinkedList {
public:
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode() : val(0), next(NULL) {}
LinkedNode(int x) : val(x), next(NULL) {}
LinkedNode(int x, LinkedNode* next) : val(x), next(next) {}
};
MyLinkedList() {
_dummyHead = new LinkedNode(0);
_size = 0;
}
int get(int index) {
if (index > (_size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = _dummyHead;
for (int i = 0; i <= index; i++) {
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
LinkedNode* temp = new LinkedNode(val, _dummyHead->next);
_dummyHead->next = temp;
_size++;
}
void addAtTail(int val) {
LinkedNode* cur = _dummyHead;
while(cur->next != NULL){
cur = cur->next;
}
LinkedNode* tmp = new LinkedNode(val);
cur->next = tmp;
_size++;
}
void addAtIndex(int index, int val) {
if (index <= 0) addAtHead(val);
else if (index > _size) return;
else if (index == _size) addAtTail(val);
else if (index <= _size - 1) {
LinkedNode* cur = _dummyHead;
for (int i = 0; i < index; i++) {
cur = cur->next;
}
LinkedNode* tmp = new LinkedNode(val);
tmp->next = cur->next;
cur->next = tmp;
_size++;
}
}
void deleteAtIndex(int index) {
if (index >= 0 && index < _size) {
LinkedNode* cur = _dummyHead;
for (int i = 0; i < index; i++) {
cur = cur->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
_size--;
}
}
private:
int _size;
LinkedNode* _dummyHead;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
先建立节点结构体,一步步设计,思想上不难。
时间复杂度O()
空间复杂度O()
有很多小坑需要注意,五分钟写完了代码,结果调报错挑了一个小时几乎,最后发现是多写了一个大括号,非常的崩溃。还是对代码不够敏感和熟练的原因。记得第一次写这题时也是类似的非常小的错误,导致排查了非常久,实在是很不应该,也实在是需要更注意更留心,尤其要着重培养根据报错思考错误的可能性原因,不能只往代码逻辑上排查问题。
python写太多了,尤其要注意其中与C++的语法区别,包括声明,顺序等等。
首先是private部分里有用到结构体,所以必须放到后面写。
括号一定要一一对应好。
一些明显不合理的报错大概率都是不小心的拼写错误,或者语法、符号标点问题,要详细检查。
建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。
题目链接/文章讲解/视频讲解
递归法
/**
* 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* reverse(ListNode* p, ListNode*q) {
if (q == NULL) return p;
ListNode* qq = q->next;
q->next = p;
return reverse(q, qq);
}
ListNode* reverseList(ListNode* head) {
return reverse(NULL, head);
}
};
迭代法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* p = NULL;
ListNode* cur = head;
ListNode* q;
while (cur != NULL) {
q = cur->next;
cur->next = p;
p = cur;
cur = q;
}
return p;
}
};
第一想法是三个指针,p, cur, q 分别保存结点的前,自身,后。然后一个个遍历过去,会稍微麻烦一点。
后来瞥到一眼之前的答案,是递归,就写成了递归的形式,毕竟写起来比较简单。
思路上并没有第一时间想到递归。对于相同操作的遍历等,都应到第一时间联想到递归。
时间复杂度O(n)
空间复杂度O(n)
迭代法
时间复杂度O(n)
空间复杂度O(1)
注意三指针的迭代法与递归法都是O(n)的时间复杂度,但是迭代法空间复杂度为O(1),更优。迭代法要注意最后返回的是p结点,因为当前所在的cur结点已经是空节点。