今天进入链表章节的学习了,也是之前学过的内容,这次争取快速AC。
203.移除链表元素
leetcode链接:https://leetcode.cn/problems/remove-linked-list-elements/
题意:删除链表中等于给定值 val 的所有节点。
示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2: 输入:head = [], val = 1 输出:[]
示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]
没什么好说的,这里注意引入了一个虚拟头节点dummy,这样就不用处理需要删除第一个节点的特殊情况。删除时C++需要手动detete。我本来使用free的,后来发现两者其实也是有区别的:详见文章new和malloc,delete和free的区别
new的底层也是通过malloc来开辟内存的,new比malloc多一项功能,就是开辟完内存,还可以进行初始化:
int *p=new int(10);
Test *p=new Test();
第一条语句是new的基本操作,10代表开辟整型内存的初始值。
第二条语句会在堆上开辟Test类型的一块内存,同时构造出一个对象。
以上两条malloc均办不到。
new开辟内存失败抛出bad_alloc类型的异常,要捕获异常才能判断内存是否分配成功。而malloc内存开辟失败返回的是NULL指针。
再谈谈delete和free:
delete比free多一项功能,就是不仅可以释放内存,还可以析构指针指向的对象。
还有一点要补充:
new和delete不仅仅是运算符,它们实际上是运算符重载函数的调用,对应的函数名是operator new和operator delete,可以在全局或者类的作用域中提供自定义的new和delete运算符的重载函数,以改变默认的malloc和free内存开辟释放行为。
然后删除的顺序,先让前一个节点指向要删除节点的后一个节点,再把要删的节点delete掉
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode *first = new ListNode(0);
first->next = head;//虚拟头节点
ListNode *p = first;//用于保存前节点的指
while (p->next != NULL) {
if (p->next->val == val) {
ListNode *tmp = p->next;
p->next = p->next->next;
delete tmp; //这里不是free(p);
} else {
p = p->next;
}
}
return first->next;
}
};
707.设计链表
题目链接:力扣题目链接(opens new window)
这题难度不大,主要是链表基本操作的实现。重点如下:
- 手撸链表的数据结构,我之前学的是C语言版,对于struct的比较熟悉,但C++需要补充一个构造函数和初始化函数,这样才可以new出对象。
- 这个class的调用方式是这样的:
这里使用new出一个MyLinkedList而不是我们自己创建的ListNode。然后使用其中的方法,简单来说就是包装吧。
class MyLinkedList {
public:
struct ListNode{
int val;
ListNode* next;
ListNode(int x):val(x),next(NULL){};//构造函数
};
//初始化链表,
MyLinkedList() {
_dummy = new ListNode(0);
_size = 0;
}
int get(int index) {
if(index >= _size || index < 0){
return -1;
}
ListNode* cur = _dummy->next;//这里指向的是_dummy->next
while(index--){
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
ListNode* p = new ListNode(val);
p->next = _dummy->next;
_dummy->next = p;
_size++;
}
void addAtTail(int val) {
ListNode* p = new ListNode(val);
ListNode* cur = _dummy;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = p;
_size++;
}
void addAtIndex(int index, int val) {
ListNode *p = new ListNode(val);
ListNode* cur = _dummy;//这里指向_dummy,因为我们要的效果是循环结束后cur停在对于节点的前面
if(index > _size){
return;//index大于链表长度,不插入
}
while(index--){
cur = cur->next;
}
p->next = cur->next;
cur->next = p;
_size++;
}
void deleteAtIndex(int index) {
if(index >= _size || index < 0){
return;
}
ListNode* cur = _dummy;
while(index--){
cur = cur->next;
}
ListNode* temp = cur->next;
cur->next = cur->next->next;
delete temp;
_size--;
}
private:
ListNode* _dummy;
int _size;
};
206.反转链表
题目链接:力扣题目链接(opens new window)
题解:题解
这里使用双指针,用pre节点保存cur的前一个节点,注意顺序:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
// ListNode* dummy = new ListNode(0);
// dummy->next = head;
ListNode* pre = nullptr;
ListNode* cur = head;
while(cur != nullptr){
ListNode* tmp = cur->next;
cur->next = pre;
pre = cur;
cur = tmp;
}
return pre;
}
};
这里不需要创建虚拟头节点了,因为对head节点的处理是一样的,不需要特殊处理。文章来源:https://www.toymoban.com/news/detail-581152.html
递归法没怎么看,今天有点忙,有时间再看,感觉跟双指针的思路是一样的。文章来源地址https://www.toymoban.com/news/detail-581152.html
到了这里,关于Day03|链表01:203.移除链表元素、707.设计链表、206.反转链表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!