代码随想录算法训练营第三天| 203.移除链表元素,707.设计链表,206.反转链表。
203.移除链表元素
题目链接:203. 移除链表元素,难度:简单
【实现代码】
/**
* 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) {
while (head != nullptr && head->val == val) {
ListNode* tmp = head;
head = head->next;
delete tmp;
}
ListNode *curNode = head;
while (curNode != nullptr && curNode->next != nullptr) {
if (curNode->next->val == val) {
ListNode *tmpNode = curNode->next;
curNode->next = curNode->next->next;
delete tmpNode;
} else {
curNode = curNode->next;
}
}
return head;
}
};
【解题思路】
- 首先判断头节点是否是val,若是单独对头节点处理
- 头节点不是val时,判断当前节点的下一个节点是否不为空且其->val是否等于val
- 若是,则将当前节点指向下下一个节点,并将当前节点的下一个节点删除>
【总结】
【第一想法】在脑中已经有了链表删除的图片,觉得问题不大吧
【存在的问题】只记得原理,对结构体的指针理解不到位,不能直接实现
【收获】理解了结构体指针的用法
707. 设计链表
题目链接:707. 设计链表,难度:中等
【实现代码】
class MyLinkedList {
public:
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val) : val(val), next(nullptr){}
};
MyLinkedList() {
this->_size = 0;
this->_dummyHead = new LinkedNode(0);
}
int get(int index) {
if (index >= this->_size || index < 0) {
return -1;
}
LinkedNode* cur = this->_dummyHead->next;
for (int i = 0; i < index; i++) {
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
if (this->_dummyHead != nullptr) {
newNode->next = this->_dummyHead->next;
this->_dummyHead->next = newNode;
this->_size++;
}
}
void addAtTail(int val) {
if (this->_dummyHead != nullptr) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = this->_dummyHead;
while (cur->next != nullptr) {
cur = cur->next;
}
cur->next = newNode;
this->_size++;
}
}
void addAtIndex(int index, int val) {
if (index < 0) {
index = 0;
} else if (index > this->_size) {
return ;
}
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = this->_dummyHead;
for (int i = 0; i < index; i++) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
this->_size++;
}
void deleteAtIndex(int index) {
if (index >=0 && index < this->_size) {
LinkedNode* cur = this->_dummyHead;
for (int i = 0; i < index; i++) {
cur = cur->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
this->_size--;
}
}
private:
int _size;
LinkedNode* _dummyHead;
};
【总结】
【第一想法】有了第一题的链表练习之后,基本掌握了链表的操作,觉得难度不大。
【存在的问题】
- 对于类的成员不太清楚需要创建什么,也不知道还要创建一个结构体;
- 写的程序也基本通过了验证,比较示例代码后,发现自己写的很多有冗余,判断条件太细,造成了冗余。
- 力扣给的示例有点小问题,声明类对象使用了new,需要用指向对象的指针接收,或者不用new。
【收获】掌握了链表的各种操作。
206. 反转链表
题目链接:206. 反转链表,难度:简单
【实现代码】
//双指针实现
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* preNode = nullptr;
ListNode* curNode = head;
ListNode* nextNode;
while (curNode != nullptr) {
nextNode = curNode->next;
curNode->next = preNode;
preNode = curNode;
curNode = nextNode;
}
head = preNode;
return head;
}
};
//递归实现1
class Solution {
public:
ListNode* reverse(ListNode* pre,ListNode* cur){
if(cur == NULL) return pre;
ListNode* temp = cur->next;
cur->next = pre;
// 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
// pre = cur;
// cur = temp;
return reverse(cur,temp);
}
ListNode* reverseList(ListNode* head) {
// 和双指针法初始化是一样的逻辑
// ListNode* cur = head;
// ListNode* pre = NULL;
return reverse(NULL, head);
}
};
//递归实现2
class Solution {
public:
ListNode* reverseList(ListNode* head) {
// 边缘条件判断
if(head == NULL) return NULL;
if (head->next == NULL) return head;
// 递归调用,翻转第二个节点开始往后的链表
ListNode *last = reverseList(head->next);
// 翻转头节点与第二个节点的指向
head->next->next = head;
// 此时的 head 节点为尾节点,next 需要指向 NULL
head->next = NULL;
return last;
}
};
【解题思路】
和数组的反转相似,不同的是,链表的反转需要额外的空间(链表的指针空间)存储前一个节点和下一个节点。
【总结】
【存在的问题】在未看讲解时,分析的太复杂且不清楚。
【收获】加深了对链表指针的掌握