题目连接:203.移除链表元素
在链表起始端删除元素与链表中间删除元素不同,在链表操作中常常选用虚拟头结点来使得操作一致性。
不选用虚拟头结点需要在一开始将链表起始端元素处理达到要求,再处理接下来的元素。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* temp;
while(head != nullptr && head->val == val) {
temp = head;
head = head->next;
delete temp;
}
ListNode* curr = head;
while(curr != nullptr && curr->next != nullptr) {
if(curr->next->val == val) {
temp = curr->next;
curr->next = curr->next->next;
delete temp;
}
else{
curr = curr->next;
}
}
return head;
}
};
选用虚拟头结点则不用考虑开始端的元素,自己最开始写的代码如下,虽然在LeetCode上可以成功运行,但是删除结点的内存空间没有释放:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummy_node = new ListNode(0, head);
ListNode* first = dummy_node, *second = head;
while(second != nullptr) {
if (second->val == val) {
second = second->next;
first->next = second;
}
else{
first = first->next;
second = second->next;
}
}
return dummy_node->next;
}
};
看了视频之后改写的代码,释放内存,减少变量数量:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummy_head = new ListNode(0, head), *temp, *curr;
curr = dummy_head;
while(curr->next != nullptr) {
if(curr->next->val == val) {
temp = curr->next;
curr->next = curr->next->next;
delete temp;
}
else {
curr = curr->next;
}
}
temp = dummy_head;
dummy_head = dummy_head->next;
delete temp;
return dummy_head;
}
};
题目连接:707.设计链表
这道题主要巩固链表相关知识,在写代码时碰到了一个bug:定义结构体时在 } 之后没有加 ; 这个bug找了好久才找到,平时在idea里写代码直接就补全,自己写就不太注意。
class MyLinkedList {
public:
struct ListNode {
int val;
ListNode* next;
ListNode():val(0), next(nullptr) {}
ListNode(int num): val(num), next(nullptr) {}
ListNode(int num, ListNode* ptr): val(num), next(ptr) {}
};
MyLinkedList() {
_size = 0;
_dummy_head = new ListNode();
}
int get(int index) {
if (index > _size - 1) return -1;
ListNode* temp = _dummy_head;
while(index >= 0){
temp = temp->next;
index--;
}
return temp->val;
}
void addAtHead(int val) {
ListNode* new_node = new ListNode(val, _dummy_head->next);
_dummy_head->next = new_node;
_size++;
}
void addAtTail(int val) {
ListNode* curr = _dummy_head;
while(curr->next != nullptr){
curr = curr->next;
}
ListNode* new_node = new ListNode(val, curr->next);
curr->next = new_node;
_size++;
}
void addAtIndex(int index, int val) {
if(index <= 0) addAtHead(val);
else if(index > _size) return;
else if(index == _size) addAtTail(val);
else {
ListNode* curr = _dummy_head;
while(index > 0){
curr = curr->next;
index--;
}
ListNode* new_node = new ListNode(val, curr->next);
curr->next = new_node;
_size++;
}
}
void deleteAtIndex(int index) {
if(index < 0 || index > _size - 1) return;
else {
ListNode* curr = _dummy_head;
while(index >= 1){
curr = curr->next;
index--;
}
ListNode* temp = curr->next;
curr->next = curr->next->next;
delete temp;
_size--;
}
}
private:
ListNode* _dummy_head;
int _size;
};
题目连接:206.反转链表
双指针法:在我看来是三指针法,看到卡哥标题里写了双指针法之后我怎么像也没能想出来怎么单纯用两个指针来完成链表翻转操作。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr, *temp = head;
while(temp != nullptr){
temp = head->next;
head->next = pre;
pre = head;
head = temp;
}
delete temp;
return pre;
}
};
迭代法:主要可以用在整个链表按照一定规则重组的时候这个方法比较简单,这里第四行需要注意一下两个判断条件的顺序,如果反过来就会报错,迭代方法自己写的时间内存占用都很多。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == nullptr || head->next ==nullptr) return head; *****
else {
ListNode* sub_head = reverseList(head->next);
ListNode* curr = sub_head;
while(curr->next != nullptr) {
curr = curr->next;
}
curr->next = head;
head->next = nullptr;
return sub_head;
}
}
};