链表理论基础
struct LisiNode {
int val;
ListNode* next;
ListNode() {
val = 0;
next = nullptr;
}
ListNode(int v) {
val = val;
next = nullptr;
}
ListNode(int v, ListNode* n) {
val = v;
next = n;
}
};
链表节点是一个结构体,内部有一个指针,保存它逻辑结构上的下一个节点的位置。内部构造函数有如上三种写法,根据需要取其中两种即可。定义时采用对应的构造函数用new来实现或者直接定义对应的指针即可。
今天的链表专题主要是关于头结点的。实际上我之前在面试时候遇到链表题目的时候,就下意识3定义了之前一刷时候用过的虚拟头节点,但是我直接把头节点的值赋给它并且还没怎么实际用到。那是一道移除链表中重复元素的题目。面试官还说了我定义了一个虚拟头节点啊,但是后面就没说了因为我没咋用。最后花的时间也挺多还没调出来。今天重点用虚拟头节点实现一下几道题目。
LeetCode 203 移除链表元素
这种类型题目我之前一般都要花蛮多时间才能解出来,这次只花了几分钟。都是虚拟头节点的功劳了。它让我避免了很多复杂的情况,只需要花费O(1)的时间代价,就能够达到将prev节点用作i,cur节点用作j的情况来像对数组进行操作那样对链表进行同样的操作。就如同数据结构中链表结构中使用的for循环+下标对链表元素进行遍历一般方便了。
代码如下:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* virtualHead = new ListNode(0, head);
ListNode* cur = head;
ListNode* prev = virtualHead;
while (prev && prev->next) {
if (prev->next->val == val) {
cur = prev->next;
while (cur && cur->val == val) {
if (cur->next) cur = cur->next;
else cur = nullptr;
}
prev->next = cur;
prev = prev->next;
} else {
prev = prev->next;
}
}
head = virtualHead->next;
delete virtualHead;
return head;
}
};
LeetCode 707 设计链表
这题一刷时候就遇到了两个bug,修改之后过了。二刷还是遇到了。通过修改两个bug,得出了最后结果。主要是在deleteAtIndex和addAtIndex时cnt和index比较条件的不同。前者是cnt和index + 1比较,后者是cnt和index比较。这次在理解了new和delete(通过内存池去理解的)的前提之上,自己还加了一个deleteAll作为防止内存泄漏的析构函数
代码如下:
/*struct ListNode {
int val;
ListNode* next;
ListNode() {
val = 0; next = nullptr;
}
ListNode(int v) {
val = v; next = nullptr;
}
ListNode(int v, ListNode* n) {
val = v; next = n;
}
};*/
class MyLinkedList {
private:
ListNode* virtualHead;
ListNode* prev;
ListNode* cur;
ListNode* newNode;
int cnt;
public:
MyLinkedList() {
virtualHead = new ListNode(0, nullptr);
prev = cur = virtualHead;
cnt = 0;
}
int get(int index) {
if (index + 1 > cnt) return -1;
prev = virtualHead;
for (int i = 0; i <= index; i++) {
prev = prev->next;
}
return prev->val;
}
void addAtHead(int val) {
ListNode* newNode = new ListNode(val, virtualHead->next);
virtualHead->next = newNode;
cnt++;
}
void addAtTail(int val) {
cur = virtualHead;
while (cur && cur->next) {
cur = cur->next;
}
cur->next = new ListNode(val);
cnt++;
}
void addAtIndex(int index, int val) {
if (index > cnt) return;
prev = virtualHead;
for (int i = 0; i < index; i++) {
prev = prev->next;
}
newNode = new ListNode(val, prev->next);
prev->next = newNode;
cnt++;
}
void deleteAtIndex(int index) {
if (index + 1 > cnt) return;
prev = virtualHead;
for (int i = 0; i < index; i++) {
prev = prev->next;
}
cur = prev->next;
if (cur->next) {
prev->next = cur->next;
} else {
prev->next = nullptr;
}
cnt--;
}
void deleteAll() {
ListNode* temp = virtualHead;
cur = virtualHead;
while (cur && cur->next) {
temp = cur;
cur = cur->next;
delete temp;
}
temp = cur;
delete temp;
}
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
LeetCode 206 反转链表
我人都啥掉了,一直超时,也看不出来原因是什么,先放代码,之后再看看吧。
/**
* 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* reverseList(ListNode* head) {
if (!head) return head;
ListNode* virtualHead = new ListNode(0, head);
ListNode* prev = virtualHead;
ListNode* cur = head;
while (cur && cur->next) {
ListNode* temp = cur->next;
cur->next = prev;
prev = cur;
cur = temp;
}
cur->next = prev;
head = cur;
while (cur && cur->next) {
prev = cur;
cur = cur->next;
}
prev->next = nullptr;
delete virtualHead;
return head;
}
};
晚上结合打印输出发现是第二个循环进入死循环了。原因是我把virtualHead初始化时候next指针指向head节点了,这样后面想去尾的时候就会从尾再到头再循环下去,改成如下的形式就能解决了。记得腾讯客户端一次面试也是这种类型题目挂的,哎。说多了都是泪。