LeetCode 203. 移除链表元素
解题思路
移除链表元素主要是让节点next指针直接指向下 下一个节点就可以了,这边需要注意的是如果是头结点,直接向后移动一位就可以了。
如果直接在原来的链表上进行操作,就需要对头节点和中间节点分开讨论,所以一般会选择设置一个虚拟头节点。这样便于操作
注意事项:
- cur 指向的永远是符合条件的指针,我们需要对cur->next 进行讨论;
- 如果cur->next 中的val值 等于 指定值 和不等于指定值 所执行的操作是不一样的;
代码解析
/**
* 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 *cur = dummyHead;
while(cur->next !=nullptr)
{
if(cur->next->val == val)
{
// 注意此时cur->next的节点变了 还需要继续判断
cur->next = cur->next->next;
}
else
{
cur = cur->next;
}
}
return dummyHead->next;
}
};
LeetCode 707. 设计链表
解题思路
为了方便增加、删除节点,我们需要定义一个虚拟头结点。同时注意到几个函数和序号有关,可以定义一个记录链表长度的变量,另外注意的是链表的序号从0开始。
int get(int index): 获取链表中下标为index 的节点的值。如果下标无效,返回-1:
- 下标无效的情况分为:index < 0 或者 index >_size-1(因为_size长度的链表最大序号为_size -1);
- 获取下标为index 节点的值,需要考虑好指针移动的次数(确保移动到index的节点处)。
void addAtIndex(int index, int val): 将一个值为val的节点插入到链表中下标为index的节点之前
void deleteAtIndex(int index): 如果下标有效,删除链表中下标为index 的节点
- 下标有效:在[0, _size-1]之间;
- 删除操作和删除链表元素原理相似,注意移动到前一个节点的位置即可。
- 链表长度需要减1;
这边归纳链表移动的规律:
假设每次移动都从头结点开始,移动n次(假设链表长度远远大于n),此时节点移动到链表第 n+1 个节点的位置。
注意本文中链表的序号从0开始。因此,下标为index 的节点其实是链表中第index +1 的节点,移动到该节点需要走index步;
而无论是增加节点或者删除节点,只需要移动到其前一个节点即可,所以可以从虚拟头结点出发。
void addAtHead(int val): 将一个值为val的节点插入到链表中作为第一个元素
- 比较简单,直接修改dummyHead的next 即可;
- 链表长度需要加1;
void addAtTail(int val): 将一个值为val的节点追加到链表中作为链表的最后一个元素
- 移动节点到空节点的前一个位置,直接添加;
- 链表长度需要减1;
代码解析
class MyLinkedList {
public:
// 定义链表节点结构体
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
// 初始化链表
MyLinkedList() {
_dummyHead = new LinkedNode(0); // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
_size = 0;
}
// 获取到第index个节点数值,如果index是非法数值直接返回-1, 注意index是从0开始的,第0个节点就是头结点
int get(int index) {
if (index > (_size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = _dummyHead->next;
while(index--){ // 如果--index 就会陷入死循环
cur = cur->next;
}
return cur->val;
}
// 在链表最前面插入一个节点,插入完成后,新插入的节点为链表的新的头结点
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
// 在链表最后面添加一个节点
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
// 在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
// 如果index 等于链表的长度,则说明是新插入的节点为链表的尾结点
// 如果index大于链表的长度,则返回空
// 如果index小于0,则在头部插入节点
void addAtIndex(int index, int val) {
if(index > _size) return;
if(index < 0) index = 0;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
// 删除第index个节点,如果index 大于等于链表的长度,直接return,注意index是从0开始的
void deleteAtIndex(int index) {
if (index >= _size || index < 0) {
return;
}
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur ->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
//delete命令指示释放了tmp指针原本所指的那部分内存,
//被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
tmp=nullptr;
_size--;
}
// 打印链表
void printLinkedList() {
LinkedNode* cur = _dummyHead;
while (cur->next != nullptr) {
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int _size;
LinkedNode* _dummyHead;
};
LeetCode 206. 反转链表
解题思路
主要使用双指针法,其中cur指向更新前的链表,pre 指向更新后的链表。核心在于将cur当前指向的节点 加到pre中,然后更新pre的值; 同时将cur下一节点付给cur;
代码解析
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* temp; // 保存cur的下一个节点
ListNode* cur = head;
ListNode* pre = NULL;
while(cur) {
temp = cur->next; // 保存一下 cur的下一个节点,因为接下来要改变cur->next
cur->next = pre; // 翻转操作
// 更新pre 和 cur指针
pre = cur;
cur = temp;
}
return pre;
}
};