Leetcode 203.移除链表元素
思路分析:
移除链表元素和移除数组元素类似,可采用元素覆盖的方式。链表移除head节点和非head节点有差异,可分类讨论。更好的实现方法是引入dummy节点,令dummy->next为head。这样可以实现移除head节点和非head节点的处理逻辑相同。只要cur_node->next不是空指针,就需要一直向后寻找,如果cur_node->next的元素等于指定的val,则需要更新覆盖用next->next覆盖next元素,否则只需用往后查找。
注意:被覆盖的链表指向的内存需要释放掉,否则会引起内存泄漏。虽然不释放,leetcode上也不报错,但有内存泄漏风险。
代码实现:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummy = new ListNode();
dummy->next = head;
ListNode* cur_node = dummy;
while (cur_node->next) {
if (cur_node->next->val == val) {
ListNode* tmp = cur_node->next;
cur_node->next = cur_node->next->next;
delete tmp; // 注意释放被删除节点的内存
} else {
cur_node = cur_node->next;
}
}
// return dummy->next;
head = dummy->next;
delete dummy; // 注意释放被删除节点的内存
return head;
}
};
Leetcode 707.设计链表
思路解析:使用了带有虚拟头节点的单链表来实现一个简单的链表数据结构。链表中的每个节点都包含一个整数值和一个指向下一个节点的指针。
首先,定义一个 LinkedNode 结构体作为链表节点。结构体包含一个整数值 val 和一个指向下一个节点的指针 next。
在 MyLinkedList 类中定义一个私有的 _size 变量用于记录链表的大小,以及一个 _dummyHead 虚拟头节点指针,初始化时设置为空节点。
实现 MyLinkedList 的构造函数 MyLinkedList(),在构造函数中初始化 _dummyHead 为一个值为 0 的虚拟头节点,并将 _size 设置为 0。
实现 get(int index) 方法,根据给定的索引获取链表中对应位置节点的值。首先检查索引是否有效,如果无效则返回 -1。然后遍历链表直到找到目标节点,返回其值。
实现 addAtHead(int val) 方法,在链表头部插入一个新节点。创建一个新的节点并将其 next 指针指向当前头节点,然后将 _dummyHead 的 next 指针指向新节点,并将 _size 增加 1。
实现 addAtTail(int val) 方法,在链表尾部插入一个新节点。创建一个新的节点并将其 next 指针设置为空。然后通过遍历链表找到最后一个节点,将其 next 指针指向新节点,并将 _size 增加 1。
实现 addAtIndex(int index, int val) 方法,在给定的索引位置插入一个新节点。首先检查索引是否有效,如果无效则直接返回。然后创建一个新的节点,并在链表中找到目标位置的前一个节点。将新节点的 next 指针指向目标位置的节点,并将前一个节点的 next 指针指向新节点。最后将 _size 增加 1。
实现 deleteAtIndex(int index) 方法,删除给定索引位置的节点。首先检查索引是否有效,如果无效则直接返回。然后找到目标位置的前一个节点,并将其 next 指针指向目标位置的下一个节点。删除目标位置的节点,并将 _size 减少 1。
实现 printLinkedList() 方法,用于打印链表中所有节点的值。通过遍历链表,依次输出每个节点的值。
代码实现:
class MyLinkedList {
public:
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val), next(nullptr){}
};
MyLinkedList() {
_dummyHead = new LinkedNode(0);
_size = 0;
}
int get(int index) {
if (index > (_size - 1) || index < 0) {
return -1;
}
LinkedNode* cur = _dummyHead->next;
while(index--){
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val);
newNode->next = _dummyHead->next;
_dummyHead->next = newNode;
_size++;
}
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(cur->next != nullptr){
cur = cur->next;
}
cur->next = newNode;
_size++;
}
void addAtIndex(int index, int val) {
if (index > _size || index < 0) {
return;
}
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
void deleteAtIndex(int index) {
if (index >= _size || index < 0) {
return;
}
LinkedNode* cur = _dummyHead;
while(index--) {
cur = cur ->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
_size--;
}
void printLinkedList() {
LinkedNode* cur = _dummyHead;
while (cur->next != nullptr) {
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}
private:
int _size;
LinkedNode* _dummyHead;
};
Leetcode 206.反转链表
思路分析:
通过不断修改节点之间的连接关系来实现链表的反转。在遍历过程中,每次都将当前节点的next指针指向前一个节点,使得链表方向反转。同时,使用三个指针变量在循环过程中进行节点的遍历和保存,保证了反转操作的正确性。
定义pre_node,让当前节点cur_node的next指针指向pre_node,可定义pre_node的初值为空指针nullptr。
当前链表的最后一个节点即为反转链表的head节点。或cur_node的最后一个值即为反转链表的head节点。
代码实现:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre_node = nullptr;
ListNode* cur_node = head;
ListNode* reverse_head = nullptr;
while (cur_node != nullptr) {
ListNode* p_next = cur_node->next; // next值需要先保存下来,因为后面会被覆盖
if (cur_node->next == nullptr) {
reverse_head = cur_node;
}
cur_node->next = pre_node;
pre_node = cur_node;
cur_node = p_next;
}
return reverse_head;
}
};