链表:C/C++中单链表节点定义方式
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};
C++默认构造函数和自己定义的节点构造之间的区别:
203.移除链表元素
题目链接:203. 移除链表元素 - 力扣(LeetCode)
讲解链接:代码随想录 (programmercarl.com)
1)不使用虚拟头节点
对头节点和非头节点分开讨论。如果是要移除头节点时,将head指向head的下一个节点即可;如果是非头节点时,待移除节点的上一个节点的指针指向待移除节点的下一个节点即可。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//直接用原来的链表进行操作;对头节点和非头节点分类讨论
while (head != NULL && head->val == val)//当头节点是目标元素时
{
ListNode* cmp = head;//cmp储存待删去元素
head = head->next;
delete cmp;//记得释放空间
}
ListNode* cur = head;/*开始删除非节点元素,cur指向检测元素的上一个节点,因为删一个节点时,需要该节点的上一个节点的指针指向其下一个节点的数据,而单向的节点无法回溯上一个节点*/
while (cur != NULL && cur->next != NULL)//cur节点和当前节点都不为空时
{
if (cur->next->val == val)//进行判断cur的下一个节点的值是否满足要求
{
ListNode* cmp = cur->next;
cur->next = cur->next->next;
delete cmp;
}
else
{
cur = cur->next;//进行下一个元素的检测
}
}
return head;
}
};
PS:C++记得移除元素后释放空间
2)虚拟头节点法
即在头节点前方创立一个新节点,新节点的指针指向头节点,这样头节点和非头节点的处理方式就一致了。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//虚拟头节点法
ListNode* dummyHead = new ListNode(0);//创建一个虚拟节点,指向head
dummyHead->next = head;
ListNode* cur = dummyHead;/*开始删除非节点元素,cur指向检测元素的上一个节点,因为删一个节点时,需要该节点的上一个节点的指针指向其下一个节点的数据,而单向的节点无法回溯上一个节点*/
while (cur != NULL && cur->next != NULL)//cur节点和当前节点都不为空时
{
if (cur->next->val == val)//进行判断cur的下一个节点的值是否满足要求
{
ListNode* cmp = cur->next;
cur->next = cur->next->next;
delete cmp;
}
else
{
cur = cur->next;//进行下一个元素的检测
}
}
return dummyHead->next;
}
};
707.设计链表
题目链接: 707. 设计链表 - 力扣(LeetCode)
讲解链接:代码随想录 (programmercarl.com)
参考代码:
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) {
//首先判断index是否合法,当index<0或者index>_size-1时非法
LinkedNode* cur = dummyHead->next;
if (index > (_size - 1) || index < 0)
{
return -1;
}
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 != NULL)
{
cur = cur->next;//找到最优一个元素的位置
}
cur->next = newNode;
_size++;
}
void addAtIndex(int index, int val) {
//首先判断n的合法性
if (index < 0)index = 0;//在头部插入节点
if (index > _size)return;//大于链表长度,不插入
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = dummyHead;
while (index--)
{
cur = cur->next;
//cur->next指向第index个元素
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}
void deleteAtIndex(int index) {
LinkedNode* cur = dummyHead;
if (index<0 || index>_size - 1)
{
return;
}
else
{
while (index--)
{
cur = cur->next;
}
LinkedNode* cmp = cur->next;
cur->next = cur->next->next;
delete cmp;//释放空间
_size--;
}
}
private:
int _size;//数组大小
LinkedNode* dummyHead;//虚拟头节点
};
206.反转链表
讲解链接:代码随想录 (programmercarl.com)
1)双指针法
首先定义两个指针,指针pre初始化为NULL,因为第一个元素反转后变成最后一个元素所指向的即为NULL,指针cur初始化为head,即从head开始反转。当cur为NULL时,停止反转,返回pre即可。
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* cur = head;
ListNode* pre = NULL;//初始化,第一个值翻转过来后指向NULL
while (cur)
{
ListNode* temp = cur->next;//用一个临时节点储存下一个值
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
};
2)递归法
逻辑同双指针法,只是更加简洁
class Solution {
public:
ListNode* reverse(ListNode* pre, ListNode* cur)
{
if (cur == NULL) return pre;//递归结束
ListNode* temp;
temp = cur->next;
cur->next = pre;//翻转
return reverse(cur, temp);//现在的cur是下一个pre,现在的temp是下一个cur
}
ListNode* reverseList(ListNode* head) {
return reverse(NULL, head);//初始值同双指针法
}
};