移除链表元素:
移除链表中某一元素的值,先遍历链表的值,但是存在头节点和后续节点值表示方式不一样的情况,堆区新开一个虚拟头节点,使得遍历后续节点具有一致性;此外,删除一个节点最简单,直接跳过即可,但是循环判断条件需要思考一下;
总结:理解错链表节点的意思了,起初不理解为什么在跳过目标节点会定义一个新节点,这个节点用来指向目标节点相同位置,利用其找到其next后释放该节点;不要忘记如果没有找到也要一直指向下一个节点,新定义的节点cur用于遍历,满足的删。
关键点:虚拟头节点便于遍历、查到后如何释放、堆空间的释放
```cpp
/**
* 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);
DummyHead->next = head;
ListNode* cur = DummyHead;
while(cur != nullptr && cur->next != nullptr) {
if(cur->next->val == val) {
ListNode* temp = cur->next;
cur->next = cur->next->next;
delete temp;
}
else cur = cur->next;
}
head = DummyHead->next;
delete DummyHead;
return head;
}
};
```
链表设计:
链表设计太多细节了,耐心快被磨掉,debug30分钟才改好,有很多细节之处;
总结:在写ACM时,先定义节点的结构体;注意区分index值和链表长度值的关系,index指的是链表下标,从0到链表长度减一;指针本质上存储一个地址,所以一个指针只能指向一个地址,虽然代码中没有显式 “删除” 原有连接,但通过指针赋值,原有连接会被自动覆盖,函数如果有要求删除该节点,是需要手动delete释放内存的。
关键:虚拟头节点、ACM链表结构体定义、遍历用cur、新加节点用new且记得链表长度加一
```cpp
class MyLinkedList {
public:
// 定义链表节点结构体
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val): val(val), next(nullptr) {}
};
// 初始化链表
MyLinkedList() {
_size = 0;
_dummyHead = new LinkedNode(0);
}
// 获取链表下标为index节点的值
int get(int index) {
if(index < 0 || index >= _size) return -1;
LinkedNode* cur = _dummyHead->next;
while(index--) {
cur = cur->next;
}
return cur->val;
}
// 在链表第一个节点前加一个节点
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
// 在链表最后一个元素加一个值为val的节点
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
// 正确遍历到链表末尾
while(cur->next != nullptr) {
cur = cur->next;
}
cur->next = newNode;
_size++;
}
// 将一个值为val的节点插入到链表中下标为index的节点之前
void addAtIndex(int index, int val) {
if(index < 0 || index > _size) return;
// 直接处理尾部情况,避免递归
LinkedNode* cur = _dummyHead;
LinkedNode* newNode = new LinkedNode(val);
// 找到index的前一个节点
for(int i = 0; i < index; i++) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
// 如果下标有效,删除链表中下标为index的节点
void deleteAtIndex(int index) {
if(index < 0 || index >= _size) return;
LinkedNode* cur = _dummyHead;
// 找到index的前一个节点
for(int i = 0; i < index; i++) {
cur = cur->next;
}
LinkedNode* temp = cur->next;
cur->next = temp->next; // 避免双重解引用
delete temp;
_size--;
}
private:
int _size;
LinkedNode* _dummyHead;
};
```
反转链表:
看到这道题我想到了双指针,一快一慢,主要思考一小对中是如何翻转链表的,以及头尾空指针如何翻转;
总结:快慢指针的应用,慢指针初始赋值为NULL,快指针定义为头节点,由于快指针后面一个节点会被覆盖,所以需要定义一个temp提前保存,一直这样迭代循环;循环终止条件带入特殊条件,最后返回fast还是slow作为新的头节点也用特殊情况带入。
关键点:双指针、容器暂时保存、终止条件
```cpp
/**
* 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* reverseList(ListNode* head) {
ListNode* slow = nullptr;
ListNode* fast = head;
while(fast != nullptr) {
ListNode* temp ;
temp = fast->next;
fast->next = slow;
slow = fast;
fast = temp;
}
return slow;
}
};
```

575

被折叠的 条评论
为什么被折叠?



