代码随想录算法训练营第三天 c++实现 | LeetCode203. 移除链表元素、LeetCode707. 设计链表、LeetCode206. 反转链表
203. 移除链表元素
题目链接:203. 移除链表元素
思路:
- 删除链表中的元素,其实只需要将其前一个节点的指针指向被删除元素的下一个节点即可
- 但是在代码实现中有一些需要考虑
- 如果需要删除的元素为链表第一个节点,直接删除会丢失头指针,这里使用一个前缀节点指向链表的第一个节点,删除的时候改变前缀节点的指针即可。返回的时候也返回前缀节点指针。
- 为了便于理解和代码逻辑清楚,使用前后两个指针指向。后者指向当前比较值的节点,前者指向后者的前一个节点。
- 为避免内存泄漏,对删除的节点释放内存,释放前先用临时指针标志位置
- 前缀节点也会形成内存泄漏,所以用直接构造对象的方式创建
时间复杂度:O(n)
,空间复杂度O(1)
代码:
class Solution
{
public:
ListNode *removeElements(ListNode *head, int val)
{
// 创建前缀节点对象,离开作用域自动释放内存
ListNode pre;
// 前缀节点指针指向头节点
pre.next = head;
ListNode *front = ⪯
ListNode *back = head;
while(back != nullptr)
{
if (back->val==val)
{
ListNode * temp = back;
back = back->next;
front->next = back;
delete temp;
}
else
{
front = back;
back = back->next;
}
}
// 返回前缀节点的指针
return pre.next;
}
};
总结:
- 前缀节点的思想在链表这些题目中挺常见,(嘿嘿)这个思想在看别人代码之前自己有悟了出来,后来看carl哥的文章补充上了内存释放,这次做的还是很快的
- 做题体验:
哈哈
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
10min
三刷
707. 设计链表
题目链接:707. 设计链表
思路:
- 这里也用到了前缀节点的思想,
MyLinkedList *obj = new MyLinkedList();
的时候返回的是指向前缀节点的指针 - 成员变量:
val
和next
自己写一下 - 成员函数:由易到难来讲吧
MyLinkedList()
: 构造函数的时候指针赋nullptr
,值随意void addAtHead(int val)
:开辟内存构造新节点,新节点指针指向原先第一个节点,前缀节点指针指向新节点void addAtTail(int val)
:从前缀节点开始遍历,遇到next
指向nullptr
时指向新节点即可int get(int index)
:从0开始计数遍历,计数等于索引的时候返回值,指针为空时退出。无效索引都会退出,返回-1即可void addAtIndex(int index, int val)
:index<=0的情况下直接头插;接下来的操作和索引查询类似void deleteAtIndex(int index)
:把遍历查询的思路和移除节点那一题结合一下就行
时间复杂度:O(n)
,遍历链表
空间复杂度O(n)
,开辟链表空间?
代码:
class MyLinkedList
{
friend void printList(MyLinkedList *mll);
public:
MyLinkedList()
{
next = nullptr;
val = 0;
}
int get(int index)
{
int count = 0;
MyLinkedList *p = this;
while (p->next != nullptr)
{
if (count == index)
{
return p->next->val;
}
else
{
p = p->next;
++count;
}
}
return -1;
}
void addAtHead(int val)
{
MyLinkedList *newOne = new MyLinkedList();
newOne->val = val;
newOne->next = this->next;
this->next = newOne;
}
void addAtTail(int val)
{
MyLinkedList *p = this;
while (p->next != nullptr)
{
p = p->next;
}
MyLinkedList *newOne = new MyLinkedList();
newOne->val = val;
p->next = newOne;
}
void addAtIndex(int index, int val)
{
if (index <= 0)
{
MyLinkedList *newOne = new MyLinkedList();
newOne->val = val;
newOne->next = this->next;
this->next = newOne;
}
else
{
int count = 0;
MyLinkedList *p = this;
while (p != nullptr)
{
if (count == index)
{
MyLinkedList *newOne = new MyLinkedList();
newOne->val = val;
newOne->next = p->next;
p->next = newOne;
break;
}
else
{
p = p->next;
++count;
}
}
}
}
void deleteAtIndex(int index)
{
int count = 0;
MyLinkedList *p = this;
while (p->next != nullptr)
{
if (count == index)
{
MyLinkedList *tmp = p->next;
p->next = tmp->next;
delete tmp;
break;
}
else
{
p = p->next;
++count;
}
}
}
private:
int val;
MyLinkedList *next;
};
总结:
- 自己一刷的时候做了很久,不过也是一遍过了,二刷熟悉多了。carl哥的解法好像和我不太一样,他还定义了一个结构体?以后看看吧[TODO],我觉得我这种把结构和方法定义在一起的思路也挺好嘿嘿
- 做题体验:
哈哈
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
40min
二刷
206. 反转链表
题目链接:206. 反转链表
思路:
- 双指针思路;注意前侧指针的初始值应该为空
时间复杂度:O(n)
,空间复杂度O(1)
代码:
class Solution
{
public:
ListNode *reverseList(ListNode *head)
{
ListNode *front = nullptr;
ListNode *back = head;
while (back != nullptr)
{
// 临时指针指向下一个节点
ListNode *tmp = back->next;
// 当前节点指针指向前任节点
back->next = front;
// 前任节点指针指向当前节点
front = back;
// 当前节点指针指向下一节点
back = tmp;
}
// 推出循环的时候当前节点指向空
// 前任节点指向原先链表的最后一个节点
return front;
}
};
总结:
- 注意指针的交换逻辑即可,该题难度不大
- 做题体验:
哈哈
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
10min
三刷