文档讲解:代码随想录
题目链接:https://leetcode.cn/problems/remove-linked-list-elements/
思路:
题目的本质就是让我们学会如何删除链表中的节点,是否需要删除看节点的值是否与目标值val相同。
删除操作其实也很简单,假设链表中我们要删除q节点,经过遍历我们可以得到q前后的两个节点p和r。满足p->next=q,q->next=r。那我们如何将q删除呢,直接让p指向r即可,即直接:p->next=r,一般来说,我们直接使用p->next=q->next即可。
核心代码:
/**
* 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* dhead=new ListNode(0);
dhead->next=head;
ListNode* tmp=dhead;
while(tmp->next!=NULL){
if(tmp->next->val==val){
ListNode* p=tmp->next;
tmp->next=p->next;
delete p;
}
else tmp=tmp->next;
}
head=dhead->next;
delete dhead;
return head;
}
};
题目链接:https://leetcode.cn/problems/design-linked-list/
思路:
本题目要求我们实现一个链表类型。
首先我们需要定义链表节点类型,才能在此基础上实现整个链表。同时我定义了链表的头结点head。除了上述必须的变量,我额外定义了链表节点数目cnt,方便判断下标是否在范围内。
本题目要求实现的函数有六个,但实际上经过归类分析,是让我们做三件事:链表节点的查看、插入和删除。
首先,链表节点值的查看依次序遍历整个链表即可,到达指定下标后输出节点中的值。
其次,在链表中指定节点前后插入节点,实际上是一类操作。对于在下标为index的节点前插入,其实就是在下标为(index-1)的节点后插入。因此插入操作我们可以分为两步走,即寻找到目标节点,然后插入。寻找目标节点遍历链表即可,不再赘述。下面叙述插入操作:
假设我们已经寻找到了目标节点p,下面在p之后插入节点r。我们易知,p原本指向的节点为q=p->next,接下来我们只需要让p指向r,r再指向q,就完成了插入操作。即:p->next=r,r->next=q。经调整顺序我们可以省略掉q的定义,即:r->next=q->next,q->next=r。
最后,链表节点的删除,这一点与本文章第一道题目LeetCode203相同,也不再赘述。
核心代码:
class MyLinkedList {
private:
struct ListNode {
int val;
ListNode *next;
ListNode(){
val=0;
next=NULL;
}
};
ListNode* head;
int cnt;
public:
MyLinkedList() {
head=new ListNode();
cnt=0;
}
int get(int index) {
if(index>=cnt) return -1;
else{
ListNode* cur=head;
index++;
while(index--) cur=cur->next;
return cur->val;
}
}
void addAtHead(int val) {
ListNode* p=new ListNode();
p->val=val;
p->next=head->next;
head->next=p;
cnt++;
}
void addAtTail(int val) {
ListNode* cur=head;
while(cur->next!=NULL) cur=cur->next;
ListNode* p=new ListNode();
p->val=val;
p->next=NULL;
cur->next=p;
cnt++;
}
void addAtIndex(int index, int val) {
if(index==cnt) addAtTail(val);
else if(indexnext;
ListNode* p=new ListNode();
p->val=val;
p->next=cur->next;
cur->next=p;
cnt++;
}
}
void deleteAtIndex(int index) {
if(cnt>index){
ListNode* cur=head;
while(index--) cur=cur->next;
ListNode* p=cur->next;
cur->next=p->next;
cnt--;
}
}
};
/**
* 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);
*/
题目链接:https://leetcode.cn/problems/reverse-linked-list/
思路:
本题目要求将一个单链表进行反转,给出了链表节点的定义和头节点。
一种简单的做法是开个新链表,对旧链表利用递归或迭代进行遍历,这个做法简单易懂,但是会占用比较多的空间。
本文用的方法是在原来链表的基础上直接进行反转操作,这样不会占用新空间,时间复杂度也低,因为只需要将原链表遍历一遍即可,下面分析如何实现。
首先,我们易知单链表为空或者只有一个结点时,我们不需要反转,直接返回即可。
接下来我们考虑现在有两个节点p和q,p和q从head开始移动,满足在原链表上q为p的后继,即p->next=q。首先我们考虑将q->next=p,这样即完成了p和q之间的反转,由于我们在链表上按照顺序移动,因此q之前的节点都完成了反转。但是如果直接写q->next=p,q原本的后继会丢失,我们无法继续移动,因此考虑开第三个节点r存放原本的p->next,然后移动的时候将p=q,q=r即可。
将上面一段用代码表示就是r=q->next,q->next=p,p=q,q=r。p和q从head开始循环执行即可。
核心代码:
/**
* 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* reverseList(ListNode* head) {
if(head==NULL||head->next==NULL) return head;
ListNode* p=head;
ListNode* q=head->next;
head->next=NULL;
while(q!=NULL){
ListNode* cur=q->next;
q->next=p;
p=q;
q=cur;
}
return p;
}
};
今日学习时长4h,做题手感变好,思路更加清晰,感觉状态慢慢回暖。