一、 链表理论基础
建议:了解一下链表基础,以及链表和数组的区别
文章链接:[https://programmercarl.com/%E9%93%BE%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html](https://programmercarl.com/%E9%93%BE%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html)
1.链表的定义:
```
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
//注:结构体里面也可以有构造函数(前面两个是他的成员变量),他主要特点是访问权限是public
};
```
注:
## 1. ListNode线性链表定义(c++自带)
```cpp
struct ListNode{
int val;
ListNode *next;
ListNode() : val(0),next(NULL){}
ListNode(int x) : val(x), next(NULL){}
ListNode(int x, ListNode *next) : val(x), next(next){}
};
```
或者:
通过自己定义构造函数初始化节点:
```
ListNode* head = new ListNode(5);
```
使用默认构造函数初始化节点:
```
ListNode* head = new ListNode();
head->val = 5;
```
具体见
[链表 - OI Wiki (oi-wiki.org)](https://oi-wiki.org/ds/linked-list/)
再把链表的特性和数组的特性进行一个对比,如图所示:

数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。
链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。
### 203.移除链表元素
建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接/文章讲解/视频讲解::[https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html](https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html)
状态:看题目理解移除单链表元素的基本操作
法一(正常思路):
**直接使用原来的链表来进行移除节点操作:**
```
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 删除头结点(特殊情况优先考虑)
while (head != NULL && head->val == val) { // 注意这里不是if
ListNode* tmp = head;
head = head->next;
delete tmp;
}
// 删除非头结点
ListNode* cur = head;
while (cur != NULL && cur->next!= NULL) {//因为下面是cur->next->val,所以要cur->next!= NULL
if (cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else {
cur = cur->next;
}
}
return head;
}
};
```
- 时间复杂度: O(n)
- 空间复杂度: O(1)
法二(新方法):
**设置一个虚拟头结点在进行移除节点操作:**(排除特殊情况)
```
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
//或直接写 ListNode* dummyHead = new ListNode(0,head);
ListNode* cur = dummyHead;
while (cur!=NULL&&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;//记得delete
return head;
}
};
```
### 707.设计链表
建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
题目链接/文章讲解/视频讲解:[https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html](https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html)
状态:未做出
题目代码就不附了,写几个易错点吧:
(1)struct中也可以有构造函数,默认访问权限是public
(2)类要先声明成员,再写函数
(3)函数返回值是void时,不符合的情况直接写return即可
(4)**尤其注意**```
delete tmp;
//delete命令指示释放了tmp指针原本所指的那部分内存,
//被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
tmp=nullptr;
```
先做到这,未完待续