代码随想录训练营Day3 | Leetcode203、707、206
一、203 移除链表元素
题目链接:203 移除链表元素
核心:使用虚拟节点dummyHead,即head前增加一个val=0的节点,目的是从第一个节点开始查找元素;要求移除某元素,意味着需要遍历到该移除元素的前一个节点(cur),而不是当下要移除的节点(cur->next),移除时令前一个节点指向该移除节点的下一个节点,移除之后需delete该节点。
方法:初始化时令cur指向dummyHead,遍历链表节点时从cur->next开始,就能从第一个节点开始遍历。
若不使用虚拟节点dummyHead,此时遍历时实际是从第二个节点开始,即跳过了第一个节点,因此需要在循环结束后单独对第一个节点判断;而且也需要在程序最开始处判断当前链表是否为空。
ListNode* removeElements(ListNode* head, int val) {
//使用虚拟头节点dummyHead,位于当前head前一个,目的是能遍历所有元素
ListNode* dummyHead=new ListNode(0); //注意如何初始化
dummyHead->next=head;
ListNode* cur=dummyHead; //由cur->next遍历该链表
while(cur->next)
{//实际从第一个节点开始遍历,无需对第一个节点特殊处理
if(cur->next->val==val)
{
ListNode* temp=cur->next;
cur->next=temp->next;
delete temp;
}
else
cur=cur->next;
}
return dummyHead->next; //返回的是虚拟节点的下一个,而不再是head!
/*
//未使用虚拟头节点
if(head==NULL)
return head;
ListNode* cur=head;//记录原链表的头结点
while(cur->next)
{//实际从第二个节点开始遍历
if(cur->next->val==val)
{
ListNode* temp=cur->next; //记录待删除的节点
cur->next=temp->next; //当前节点的下一个节点跳过待删除节点
delete temp; //必须删除该节点
}
else
cur=cur->next;
}
if(head->val==val)
{//判断第一个head节点是否为val
ListNode* temp=head;
head=head->next;
delete temp;
}
return head;
*/
}
二、707 设计链表
题目链接:707 设计链表
核心:
第一,定义链表节点的结构体;
第二,引入虚拟节点,方便插入函数、查找函数、删除函数的实现;
第三,熟悉类的编写,成员变量应在private里,public里的成员函数无需定义成员变量。
第四,删除函数在删除某节点后,先使用delete释放该节点的内存,再令该节点赋值为nullptr,因为释放内存后该节点是一个野指针,可能为任意值。
class MyLinkedList {
public:
//定义链表节点结构体
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val),next(nullptr) {} //构造函数
};
//引入虚拟节点
//初始化链表
MyLinkedList() {
dummyHead=new LinkedNode(0); //记住:dummyHead的类型不在此定义!
size=0; //size的类型也不在此定义,都在private里单独定义
}
//获取下标为index的节点元素
int get(int index) {
if(index<0 || index>=size)
return -1;
LinkedNode* cur=dummyHead->next;
while(index--)
{//直到index==0时cur指向元素即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;//注意此时cur初始化为虚拟节点,而不再是第一个节点
while(cur->next)
{//由于退出循环时要求cur必须指向最后一个节点,而不是nullptr
cur=cur->next;
}
cur->next=newNode;
size++;
}
void addAtIndex(int index, int val) {
if(index>size)
return;//index大于长度则return,表示不会插入节点
if(index<0)
index=0; //特殊情况特殊处理
LinkedNode* newNode=new LinkedNode(val);
LinkedNode* cur=dummyHead; //必须初始化为虚拟节点
while(index--)
{//退出循环时要求cur指向待插入节点的前一个,否则无法插入新节点
cur=cur->next;
}
newNode->next=cur->next;
cur->next=newNode;
size++;
}
void deleteAtIndex(int index) {
if(index<0 || index>=size)
return;
LinkedNode* cur=dummyHead;
while(index--)
{//退出循环时要求cur指向index前一个节点
cur=cur->next;
}
LinkedNode* temp=cur->next;
cur->next=temp->next;
delete temp;
temp=nullptr; //注意:需令释放内存后的temp赋值为空
size--;
}
private:
LinkedNode* dummyHead;
int size;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
三、206 反转链表
题目链接:206 反转链表
核心:反转链表实质是改变各节点的next指向,具体而言,原链表第一个节点反转后应指向nullptr,第二个节点指向第一个节点,以此类推,最后一个节点指向倒数第二个节点,反转后的头结点head即原链表的最后一个节点。
第一,cur指向原链表的head,目的是遍历各节点,直到最后一个节点;
第二,pre初始化指向nullptr,实质是与cur实现反转,即cur->next=pre;
第三,完成一次反转需立即更新pre和cur,先pre后cur,因为pre是更新到此时cur所在节点,而cur是更新到反转前的下一个节点,最后一次更新后,cur指向nullptr,pre指向原链表的最后一个节点,即反转后链表的头结点。
1、方法1:双指针法
令pre和cur分别为两个双指针,cur遍历当前链表元素,直至最后一个元素结束遍历;pre为反转前cur的前一个元素,即在pre和cur之间实现两个节点的反转;一次反转之后需更新pre和cur指针,均向前移一个节点,即pre=cur,cur=cur->next。
ListNode* reverseList(ListNode* head) {
ListNode* cur=head;
ListNode* pre=nullptr; //记录反转后cur的下一个节点
ListNode* temp; //记录反转前cur的下一个节点
while(cur)
{//cur从head遍历链表各节点
temp=cur->next;
cur->next=pre; //reverse
//更新cur和pre,必须先更新pre后cur
pre=cur; //最后一次更新后pre指向反转前链表的最后一个节点
cur=temp; //最后一次更新后cur指向nullptr,
}
return pre;
}
2、递归法
实质与双指针相同,首先需构造一个实现两节点反转的函数,即传入参数是双指针pre和cur;
(1)基底条件是cur遍历完链表所有元素,即cur为空时返回此时cur的前一个节点pre;
(2)递归条件是cur非空时(cur指向节点有效),需与前一个节点pre完成反转,反转之后进入下一次递归,传入参数需更新,即pre=cur,cur=cur->next。
ListNode* reverse(ListNode* pre, ListNode* cur)
{//构造一个节点反转的函数,传入的参数即双指针法的pre和cur
if(!cur)
return pre; //base case
ListNode* temp=cur->next; //recursion case
cur->next=pre; //reverse
return reverse(cur,temp);
}
ListNode* reverseList(ListNode* head) {
//递归法,实质与双指针相同
ListNode* cur=head;
ListNode* pre=nullptr;
return reverse(pre,cur);
}

628




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



