一、链表理论基础
了解一下链接基础,以及链表和数组的区别 ,文章链接:代码随想录
1.1 链表的定义
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
所以如果不定义构造函数,C++默认生成一个构造函数,但是使用默认构造函数的话,在初始化的时候就不能直接给变量赋值 。
//通过自己定义构造函数初始化节点:
ListNode* head = new ListNode(5);
//使用默认构造函数初始化节点:
ListNode* head = new ListNode();
head->val = 5;
1.2 链表的类型
1)单链表,单链表中的指针域只能指向节点的下一个节点:
2)双链表,双链表 既可以向前查询也可以向后查询:
3)循环链表,顾名思义,就是链表首尾相连。:
1.3 链表的操作
1)删除节点
删除D节点,如图所示:
只要将C节点的next指针 指向E节点就可以了。此时D节点不是依然存留在内存里,只不过是没有在这个链表里而已,所以在C++里最好是再手动释放这个D节点,释放这块内存。
2)添加节点
可以看出链表的增添和删除都是O(1)操作,也不会影响到其他节点。但是要注意,要是删除第五个节点,需要从头节点查找到第四个节点通过next指针进行删除操作,查找的时间复杂度是O(n)。
二、203.移除链表元素,力扣题目链接
2.1 题目的第一想法
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* p=head;
ListNode* res=p; //用于返回新的头结点
while(p!=nullptr)
{
if(p->next->val==val)
{
p->next=p->next->next;
}
p=p->next;
}
return res;
}
};
结果:通过测试用例:2 / 66。
2.2 看完代码随想录之后的想法,代码随想录
1)没考虑删除头结点的情况;
2)没考虑是else才p=p->next;
3)学习了加入虚拟头结点的方式;
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode *vp=new ListNode(0); // 设置一个虚拟头结点
vp->next=head; // 将虚拟头结点指向head,这样方面后面做删除操作
ListNode* tmp=vp; //tmp用于遍历,vp用于放回头节点
while(tmp->next!=nullptr)
{
if(tmp->next->val==val)
{
tmp->next=tmp->next->next;
}
else{
tmp=tmp->next;
}
}
return vp->next;
}
};
三、707.设计链表,力扣题目链接
3.1 题目的第一想法
class MyLinkedList {
private:
int size;
ListNode *head;
public:
MyLinkedList() {
this->head = new ListNode(0); //这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
this->size = 0; //链表的长度
}
int get(int index) {
if (index < 0 || index >= size) {
return -1;
}
ListNode *cur=head;
while(index>=0)
{
index--;
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
addAtIndex(0,val);
}
void addAtTail(int val) {
addAtIndex(size,val);
}
void addAtIndex(int index, int val) {
if (index > size) {
return;
}
size++;
ListNode *pred=head;
for(int i=0;i<index;i++)
{
pred=pred->next;
}
ListNode *toAdd = new ListNode(val);
toAdd->next = pred->next;
pred->next = toAdd;
}
void deleteAtIndex(int index) {
if (index < 0 || index >= size) {
return;
}
size--;
ListNode *pred = head;
for (int i = 0; i < index; i++) {
pred = pred->next;
}
ListNode *p = pred->next;
pred->next = pred->next->next;
delete p;
}
};
3.2 看完代码随想录之后的想法,代码随想录
1)需要定义变量size和指针head;
2)需要定义一个初始化构造,包括虚拟头结点和size=0;
3)重点是插入和删除,需要多复习。
四、206.反转链表,力扣题目链接
4.1 题目的第一想法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur=head; //定义一个当前指针,用于遍历
ListNode* pre=nullptr; //定义一个当前节点的前一个指针pre
while(cur)
{
ListNode* next=cur->next; //存储原来的下一个节点
cur->next=pre; //将当前节点的next指针指向pre
pre=cur;
cur=next;
}
return cur;
}
};
4.2 看完代码随想录之后的想法,代码随想录
1)定义一个当前节点的指针cur,用于遍历;
2)定义一个当前节点的前一个节点的指针,初始为null,用于修改cur->next;
3) 遍历cur,并定义一个next指针,存储原来节点的下一个节点指针;
4)将当前节点的next指针更新为pre;
5)将per更新为cur,cur更新为next,继续遍历。
6) 最后返回的是pre,pre为新的头结点,因为cur指向null了。
学习时长
时长3h。