1.Leetcode 203
题目描述:给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
思路解析:双指针法,使用虚拟头节点,避免删除头节点需要特殊判断。因为删除单链表节点需要找的目标节点的前一个节点才能进行删除,指针1从头节点的前一个即虚拟节点开始,指针2从头节点开始,指针2指向的是判断是否需要删除的节点,指针1是指针2的前一个结点。
代码演示附详细注释:
ListNode* removeElements(ListNode* head, int val) {
//创建一个虚拟节点(head的前驱节点)
ListNode* p = new ListNode();
ListNode* nullHead = p;
//并将它的next指向head
p->next = head;
//目标节点指针
ListNode* target = head;
ListNode* temp;
//遍历链表
while (target) {
//存储目标节点的下一个节点
temp = target->next;
//处理目标节点是否需要删除
//需要则将前驱节点p的next指向目标节点的next
if (target->val == val) {
p->next = temp;
}
//否则前驱节点向后移
else {
p = target;
}
//目标节点向后移
target = temp;
}
//返回虚拟节点的下一个节点即头节点
return nullHead->next;
}
2.Leetcode 707
题目描述:自己设计单链表并能实现以下操作
思路解析:多画图,多思考特殊情况
代码演示附详细注释:
typedef struct Node {
int val;
Node* next;
Node() { val = -1; next = nullptr; }
Node(int v) { val = v; next = nullptr; }
};
class MyLinkedList {
public:
Node* head;
Node* tail;
int size;
MyLinkedList() {
//初始化元素个数为0
size = 0;
//创建一个虚拟头节点方便头插和尾插
tail = head = new Node();
}
int get(int index) {
//不存在该索引直接返回-1
if (index < 0 || index >= size)return -1;
Node* p = head;
for (int i = 0; i <= index; i++) {
p = p->next;
}
return p->val;
}
//头插法
void addAtHead(int val) {
Node* p = new Node(val);
p->next = head->next;
head->next = p;
//特殊情况,尾指针需要指向链表的最后一个元素
if (size == 0)tail = p;
size++;
}
void addAtTail(int val) {
Node* p = new Node(val);
tail->next = p;
tail = p;
size++;
}
void addAtIndex(int index, int val) {
if (index<0 || index>size)return;
Node* p = head;
//找要插入元素节点的前一个节点
for (int i = 0; i < index; i++) {
p = p->next;
}
Node* q = new Node(val);
q->next = p->next;
p->next = q;
//当向尾端插入元素时,需要更新尾指针
if (size == index)tail = q;
size++;
}
void deleteAtIndex(int index) {
//不存在
if (index < 0 || index >= size)return;
//找要删除索引节点的前一个节点
Node* p = head;
for (int i = 0; i < index; i++) {
p = p->next;
}
Node* temp = p->next;
p->next = temp->next;
//当删除尾节点时,需要更新尾指针
if (index == size - 1)tail = p;
size--;
}
};
3.Leetcode 206
题目描述:给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
思路解析:递归版往往能体现程序设计的精髓,这里我们使用递归版,大家多思考画图。
代码演示附详细注释:
//递归函数
//第一个参数,未反转的部分链表头节点
//第二个参数,已经反转的链表头节点
ListNode* reverse(ListNode* head, ListNode* pre) {
//没有未反转的节点了,递归出口,已经全部反转,直接返回已经反转的链表头节点
if (!head)return pre;
//没有到达递归出口,还有未反转的节点
//记录下一次递归所需要反转的头节点
ListNode* temp = head->next;
//新的头节点指向已反转链表的节点点
head->next = pre;
//下一层递归
return reverse(temp, head);
/*举个例子
未反转 已反转
第0次:1-2-3-4-5-null null
第1次:2-3-4-5-null 1-null
第2次:3-4-5-null 2-1-null
第3次:4-5-null 3-2-1-null
第4次:5-null 4-3-2-1-null
第5次:null 5-4-3-2-1-null
第5次发现head为null直接return结束递归
*/
}