LeetCode 203.移除链表元素
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyhead=new ListNode(0,head);
ListNode* curr=dummyhead;
while(curr->next){
if(curr->next->val==val){
ListNode* tmp=curr->next;
curr->next=curr->next->next;
delete tmp;
}
else curr=curr->next;
}
head=dummyhead->next;
delete dummyhead;
return head;
}
};
虚拟头节点的思想:新开辟一段空间作为虚拟头结点,该结点的next指向头结点head。
注意:
- 虚拟头结点是一个新的结点,开辟的空间在最后要删除
- 注意这道题目的条件要求:返回头结点,也就是最后我们需要有头结点的地址
- 注意curr在这里的意义:
- curr在这里写成pre更好理解,这里dummyhead始终指向头结点
- curr的意义在变化,curr始终是可能删除结点的前一位
LeetCode 707.设计链表
class MyLinkedList {
public:
struct ListNode{
int val;
ListNode* next;
ListNode(int val):val(val),next(nullptr){}
ListNode(int val,ListNode* next):val(val),next(next){}
};
MyLinkedList() {
dummyhead=new ListNode(0,nullptr);
size=0;
}
int get(int index) {
if(index>=size || index<0){
return -1;
}
ListNode* curr=dummyhead->next;
while(index--){
curr=curr->next;
}
return curr->val;
}
void addAtHead(int val) {
ListNode* newnode=new ListNode(val);
newnode->next=dummyhead->next;
dummyhead->next=newnode;
size++;
}
void addAtTail(int val) {
ListNode* newnode=new ListNode(val,nullptr);
ListNode* curr=dummyhead;
while(curr->next){
curr=curr->next;
}
curr->next=newnode;
size++;
}
void addAtIndex(int index, int val) {
if(index>size || index<0)return ;
if(index==size){
addAtTail(val);
return ;
}
ListNode* newnode=new ListNode(val);
ListNode* curr=dummyhead;
while(index--){
curr=curr->next;
}
newnode->next=curr->next;
curr->next=newnode;
size++;
return ;
}
void deleteAtIndex(int index) {
if(index>=size || index<0)return ;
ListNode* curr=dummyhead;
while(index--){
curr=curr->next;
}
ListNode* tmp=curr->next;
curr->next=tmp->next;
delete tmp;
//delete命令指示释放了tmp指针原本所指的那部分内存,
//被delete后的指针tmp的值(地址)并非就是NULL,而是随机值。也就是被delete后,
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
tmp=nullptr;
size--;
return ;
}
private:
ListNode* dummyhead;
int size;
};
虚拟头结点的运用,三种插入,一种删除,本质上是一样的思想
curr=dummyhead,在while(index--)条件下遍历后,curr指向的始终是需要操作位置链表的前一位,这样就知道了需要操作链表位置的前中后三个位置,更方便操作。
LeetCode 206.反转链表
双指针法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre=nullptr;
ListNode* curr=head;
while(curr){
ListNode* tmp=curr->next;
curr->next=pre;
pre=curr;
curr=tmp;
}
return pre;
}
};
以上是正确代码,思路比较明确
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre=nullptr;
ListNode* curr=head;
while(curr->next){
ListNode* tmp=curr->next;
curr->next=pre;
pre=curr;
curr=tmp;
}
curr=curr->pre;
return curr;
}
};
下面是我自己写的错误代码,错误原因关于空指针有一点要注意:
空指针可以被访问,但空指针所指向的内存不能被访问。
在我之前写代码时,总会出现报错
我一直认为是空指针无法被访问,今天问了一个学长才知道是空指针可以被访问,但是我错误的地方是访问了空指针所指向的空间。在这一段代码中,传入的链表可能为空,此时curr=null,curr->next指向的空间就是无法被访问的。
递归法
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* curr){
if(curr==nullptr)return pre;
ListNode* tmp=curr->next;
curr->next=pre;
return reverse(curr,tmp);
}
ListNode* reverseList(ListNode* head) {
return reverse(nullptr,head);
}
};
这里唯一需要注意的就是在reverse中对于curr==nullptr时的返回需要时pre而不能是curr或nullptr,因为在递归中最后需要的是头结点,这段代码不止用于判断是否curr==nullptr,还与返回值有关。
总结
还要更专注才行,加油吧