代码随想录 | Day05 | 链表:移除链表元素&&设计链表&&反转链表
主要学习内容:
1.链表删除操作
2.设计自己的链表
3.双指针操作链表
203.移除链表元素
解法1:设置虚拟头结点
设置一个虚拟头结点用虚拟头结点的next进行判断
好处是不需要写关于头结点的处理逻辑,比较容易处理
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode * t=new ListNode;
ListNode * q=new ListNode;
t->next=head;
q=t;
while(t->next!=NULL)
{
if(t->next->val==val)
{
ListNode *tmp=t->next;
t->next=t->next->next;
delete tmp;
}
else
t=t->next;
}
return q->next;
}
};
关键点:
1.设置一个额外的指针q指向虚拟头结点,head节点可能会被删除,最后无法返回结果
2.手动释放内存 delete
解法2:直接删除
思路:先处理头节点再处理其余节点
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
while(head && head->val==val)
{
ListNode * tmp= head;
head=head->next;
delete tmp;
}
ListNode * t=new ListNode;
t=head;
while(t && t->next!=NULL)
{
if(t->next->val==val)
{
ListNode *tmp=t->next;
t->next=t->next->next;
delete tmp;
}
else
t=t->next;
}
return head;
}
};
关键点:
1.头结点是val头结点连着的下面几个可能也是val,所以要用while而不是if来处理头结点
2.没有了虚拟头结点,我们就不知道第一个到底是不是为空,则需要在两个while()处加入head不为空的判断条件
class MyLinkedList {
public:
struct ListNode
{
int val;
ListNode *next;
ListNode *pre;
ListNode() : val(0), next(nullptr),pre(nullptr) {}
};
MyLinkedList() {
prenode=new ListNode;
_size=0;
}
int get(int index) {
if(index>(_size-1)||index<0)
return -1;
int n=index+1;
ListNode *p=new ListNode;
p=prenode;
while(n--&&p->next!=NULL)
p=p->next;
return p->val;
}
void addAtHead(int val) {
ListNode* p=new ListNode;
p->next=prenode->next;
prenode->next=p;
if(p->next!=nullptr)
p->next->pre=p;
p->val=val;
p->pre=prenode;
_size++;
}
void addAtTail(int val) {
ListNode* p=new ListNode;
p->next=NULL;
p->val=val;
ListNode* q=new ListNode;
q=prenode;
while(q->next!=NULL)
q=q->next;
q->next=p;
p->pre=q;
_size++;
}
void addAtIndex(int index, int val) {
if(index<0)
{
addAtHead(val);
}
if(index>_size)
return ;
else if(index==_size)
{
addAtTail(val);
}
else
{
int n=index;
ListNode *p=new ListNode;
p=prenode;
ListNode *q=new ListNode;
while(n--&&p->next!=NULL)
p=p->next;
q->next=p->next;
p->next=q;
q->val=val;
_size++;
}
}
void deleteAtIndex(int index) {
if(_size==0)
return;
if(index>=_size||index<0)
return ;
int n=index;
ListNode *p=new ListNode;
p=prenode;
while(n--&&p->next!=NULL)
p=p->next;
if(p->next==nullptr)
return;
ListNode *q=p->next;
p->next=q->next;
if (q->next != NULL)
{
q->next->pre = p;//更新前后节点的连接
}
_size--;
}
private:
ListNode *prenode;
int _size;
};
707.设计链表
解法一:单链表
注意的点:
addAtIndex函数的if条件里面是要index>size而不是index>=size,因为=的时候是要插入到末尾
(改了2小时才发现,无语住了╭(╯^╰)╮)
class MyLinkedList {
public:
struct ListNode
{
struct ListNode* next;
int val;
ListNode() {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode* next) : val(x), next(next) {}
};
MyLinkedList()
{
size = 0;
prenode = new ListNode;
}
int get(int index)
{
if (index < 0 || index >= size)
return -1;
ListNode* t = new ListNode;
t = prenode;
int n = index + 1;
while (n--)
{
t = t->next;
}
return t->val;
}
void addAtHead(int val)
{
ListNode* t = new ListNode;
t->val = val;
t->next = prenode->next;
prenode->next = t;
size++;
}
void addAtTail(int val)
{
ListNode* t = prenode;
int n = size;
while (n--&& t->next != NULL)
t = t->next;
ListNode* tmp = new ListNode;
tmp->val = val;
tmp->next = NULL;
t->next = tmp;
size++;
}
void addAtIndex(int index, int val)
{
if (index < 0 || index > size)
return;
ListNode* t = prenode;
if (index == 0)
addAtHead(val);
else
{
while (index--)
t = t->next;
ListNode* q = new ListNode;
q->val = val;
q->next = t->next;
t->next = q;
size++;
}
}
void deleteAtIndex(int index)
{
if (size == 0)
return;
if (index < 0 || index >= size)
return;
if (index == 0)
{
ListNode* t = new ListNode;
t = prenode->next;
prenode->next = prenode->next->next;
size--;
delete t;
}
else
{
ListNode* t = prenode;
while (index-- && t->next)
t = t->next;
ListNode* p = new ListNode;
p = t->next;
t->next = t->next->next;
delete p;
size--;
}
}
private:
ListNode* prenode;
int size;
};
解法二:双链表
class MyLinkedList {
public:
struct ListNode
{
int val;
ListNode *next;
ListNode *pre;
ListNode() : val(0), next(nullptr),pre(nullptr) {}
};
MyLinkedList() {
prenode=new ListNode;
_size=0;
}
int get(int index) {
if(index>(_size-1)||index<0)
return -1;
int n=index+1;
ListNode *p=new ListNode;
p=prenode;
while(n--&&p->next!=NULL)
p=p->next;
return p->val;
}
void addAtHead(int val) {
ListNode* p=new ListNode;
p->next=prenode->next;
prenode->next=p;
if(p->next!=nullptr)
p->next->pre=p;
p->val=val;
p->pre=prenode;
_size++;
}
void addAtTail(int val) {
ListNode* p=new ListNode;
p->next=NULL;
p->val=val;
ListNode* q=new ListNode;
q=prenode;
while(q->next!=NULL)
q=q->next;
q->next=p;
p->pre=q;
_size++;
}
void addAtIndex(int index, int val) {
if(index<0)
{
addAtHead(val);
}
if(index>_size)
return ;
else if(index==_size)
{
addAtTail(val);
}
else
{
int n=index;
ListNode *p=new ListNode;
p=prenode;
ListNode *q=new ListNode;
while(n--&&p->next!=NULL)
p=p->next;
q->next=p->next;
p->next=q;
q->val=val;
_size++;
}
}
void deleteAtIndex(int index) {
if(_size==0)
return;
if(index>=_size||index<0)
return ;
int n=index;
ListNode *p=new ListNode;
p=prenode;
while(n--&&p->next!=NULL)
p=p->next;
if(p->next==nullptr)
return;
ListNode *q=p->next;
p->next=q->next;
if (q->next != NULL)
{
q->next->pre = p;//更新前后节点的连接
}
_size--;
}
private:
ListNode *prenode;
int _size;
};
关键点:
1.记得释放空间
2.很需要虚拟头指针和size,可以对很多操作进行简化
206.反转链表
解法1:双指针
快慢指针分别指向需要改变指向的两个结点,然后进行翻转即可
//自己写的
class Solution {
public:
ListNode* reverseList(ListNode* head)
{
if(head==NULL)
return head;
ListNode * pre=NULL;//慢指针
ListNode * cur=head;//快指针
ListNode * tmp=head->next;//保存节点所用
while(cur)
{
cur->next=pre;//翻转操作
//快慢指针更新
pre=cur;
cur=tmp;
if(tmp)
tmp=tmp->next;//更新保存点
}
return pre;
}
};
//代码随想录的
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前移
pre = cur;
cur = temp;
}
return pre;
}
};
改进:
用tmp=cur->next来更新tmp,使得cur和tmp产生关联,和循环条件统一起来,当tmp=NULL时,不会因为使用tmp->next而报错。
解法2:递归法
递归由方法一改写而来
递归结束条件对应while循环结束条件
快慢指针前移过程由向下一个递归函数进行赋值来代替,下一次的pre和cur(即需要翻转的两个结点)作为形参传入
最后返回的就是pre
(如果不太明白的小伙伴可以暂时放一放,可以等做完回溯章节再回来,会好很多很多很多很多)
class Solution {
public:
ListNode* re(ListNode *cur,ListNode * pre)
{
if(cur==NULL)//递归结束条件对应while循环条件
return pre;//返回头结点
ListNode * tmp=cur->next;//保存cur的下一结点
cur->next=pre;//翻转操作
//递归的过程就是快慢指针前移的过程
//pre=cur;
//cur=tmp;
return re(tmp,cur);
}
ListNode* reverseList(ListNode* head)
{
return re(head,NULL);
}
};