给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
之前很少接触链表的题目,一看道题感觉非常懵,以为是用list做,没想到是listnode,完全用不了list的方法,后面看了视频讲解才慢慢了解listnode应该怎么写,本题主要有两种方法:
1、在原链表上直接删除,这种方法需要注意单独判断头节点是不是val,因为我们正常删除节点的方法是找到其上一个节点,让上一个节点指向其下一个节点,然后再删除(1->2->3,要删除2则必须将1指向3),而头节点前面没有节点,所以当头节点是val时则需要做head = head->next的操作,另外还需要注意,该方法是需要用两个while来做循环遍历
2、虚拟头节点,这种方法比较容易理解,就是类似数学添辅助线的方法在头节点前再加一个节点,这样头节点就不是头了,然后正常判断,最后把刚开始添加的头节点删掉就行
// 原链表
class Solution {
public:
ListNode *removeElements(ListNode *head, int val)
{
// 先判断头结点是不是val
while (head != nullptr && head->val == val) {
ListNode *tmp = head; // 用临时节点来存储头节点
head = head->next;
delete tmp;
}
// 如果头结点不是val
ListNode *cur = head;
while(cur != nullptr && cur->next != nullptr)//这里要判断cur和cur->next都不为空 {
if(cur->next->val == val) {
ListNode *tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
}
else cur = cur->next;
}
return head;
}
};
// 虚拟节点
class Solution {
public:
ListNode *removeElements(ListNode *head, int val)
{
ListNode *dummyHead = new ListNode(0); // 虚拟头节点
dummyHead->next = head;
ListNode *cur = dummyHead;
// 和删除原链表中头节点不是val的方法一样
while (cur->next != nullptr) {
if (cur->next->val == val) {
ListNode *tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else
cur = cur->next;
}
head = dummyHead->next; // 要把dummyHead删除,所以先把head确定好
delete dummyHead;
return head;
}
};
在链表类中实现这些功能:
这题是就是把链表的各种方法给自己写出来,没有什么算法技巧,对于我来说,需要注意的有以下几点:
1、本题用虚拟头节点非常有效,但是需要注意在private对象里先把dummyHead和size给先申明好
2、每个方法开头要判断index是否会越界导致异常
3、在写插入元素的方法时要先将要插入的node->next = cur->next, 然后再将cur->next = node,不然链会断掉;另:在指定位置插入元素是判断越界条件是index>size而不是index>size-1,因为可以在listnode的末尾前插入元素,相当于push_back
public:
MyLinkedList() {
dummyHead = new ListNode(0);
size = 0;
}
int get(int index) {
if(index < 0 || index >= size) return -1;
ListNode* cur = dummyHead->next;
while(index--) cur = cur->next;
return cur->val;
}
void addAtHead(int val) {
ListNode* node = new ListNode(val);
node->next = dummyHead->next;
dummyHead->next = node;
size++;
}
void addAtTail(int val) {
ListNode* node = new ListNode(val);
ListNode* cur = dummyHead;
while(cur->next != nullptr) cur = cur->next;
cur->next = node;
size++;
}
void addAtIndex(int index, int val) {
if(index > size) return ;//关键,可以在listnode的末尾前插入元素,相当于push_back
if(index < 0) index = 0;//如果index小于0,则在头部插入节点
ListNode* node = new ListNode(val);
ListNode* cur = dummyHead;
while(index--) cur = cur->next;
node->next = cur->next;
cur->next = node;
size++;
}
void deleteAtIndex(int index) {
if(index < 0 || index >= size) return ;
ListNode* cur = dummyHead;
while(index--) cur = cur->next;
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
//delete命令指示释放了tmp指针原本所指的那部分内存,
//被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
tmp = nullptr;
size--;
}
private:
int size;
ListNode* dummyHead;
};
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
不看讲解视频确实想不到用双指针的解法,还在傻傻地想着像二叉树翻转一样用stack,本题精髓在于三点:
1、用双指针pre和cur,初始pre = nullptr, cur = head,因为要反过来,像示例里那样最后需要指向nullptr
2、在各自向前移位时先保存tmp = cur->next,这样当cur->next = pre后cur = tmp,不至于断链
3、从示例中来看,循环到最后时*pre = 5, cur = nullptr,pre就变成了头节点,所以return pre
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = head;
ListNode* pre = nullptr;//双指针
while(cur != nullptr) {
ListNode* tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};