云计算百科
云计算领域专业知识百科平台

代码随想录第三天 | 203.移除链表元素 | 707.设计链表 |206.反转链表

一、移除链表元素

题目链接/文章讲解/视频讲解::https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html

链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)

在单链表的核心操作中,增删节点是最常用的场景,而查找节点的效率较低 —— 因为单链表只能从头节点开始逐个遍历,无法直接定位目标节点。无论是增加还是删除节点,需要找到前驱节点,,再修改该前驱节点的next指针指向。为了简化操作,通常会创建一个虚拟头节点(哑节点):一方面能将 处理首节点”和 处理其他节点的逻辑统一(避免首节点无前驱节点的特殊判断),另一方面也能更便捷地返回操作后的新链表头节点。

完整代码如下:

class Solution {
public:
ListNode* removeElements(ListNode* head, int val)
{
ListNode* dummyHead = new ListNode(0);//设置一个虚拟头结点
dummyHead->next = head;//将虚拟头结点指向head,这样方便后面做删除操作
ListNode* cur = dummyHead;
while (cur->next != NULL)
{
if (cur->next->val == val)
{
ListNode* tmp = cur->next;//待删除节点的地址保存到tmp变量里,等完成链表指针的修改后释放内存
cur->next = cur->next->next;
delete tmp;
}
else
{
cur = cur->next;
}
}
head = dummyHead->next;
delete dummyHead;
return head;
}
};

  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

二、设计链表

题目链接/文章讲解/视频讲解:

https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html

题意:

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val  的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

题目中的index指的是从0开始。

采用设置虚拟头结点进行操作,代码如下:

class MyLinkedList {
public:
//定义链表节点结构体
struct LinkedNode {
int val;
LinkedNode* next;
LinkedNode(int val):val(val),next(nullptr){}
};

//初始化链表
MyLinkedList() {
_dummyHead = new LinkedNode(0);//这里定义的头结点,是一个虚拟头节点,而不是真正的链表头结点
_size = 0;
}

//获取到第index个节点数值,如果index是非法数值直接返回-1,注意index是从0开始的,第0个节点就是头结点
int get(int index) {
if (index > (_size – 1) || index< 0) {
return -1;
}
LinkedNode* cur = _dummyHead->next;
while (index–) {//如果–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++;
}

//在第index个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新节点
//如果index等于链表的长度则说明是新插入的节点为链表的尾节点
//如果index大于链表的长度,则返回空
//如果index小于0,则在头部插入节点
void addAtIndex(int index, int val) {
if (index > _size) return;
if (index < 0) index = 0;
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = _dummyHead;
while (index–) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
_size++;
}

//删除第index个节点,如果index大于等于链表的长度,直接return,注意index是从0开始的
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;
//delete命令指示释放了tmp指针原本所指的那部分内存,
//被delete后的指针tmp的值(地址)并非就是NULL而是随机值也就是被delete后
//如果不再加上一句tmp=nullptr,tmp会成为乱指的野指针
//如果之后的程序不小心使用了tmp,会指向难以预想的内存空间
tmp = nullptr;
_size–;
}

//打印链表
void printLinkedList() {
LinkedNode* cur = _dummyHead;
while (cur->next != nullptr) {
cout << cur->next->val << " ";
cur = cur->next;
}
cout << endl;
}

private:
int _size;
LinkedNode* _dummyHead;

};

三、反转链表

题目链接/文章讲解/视频讲解:https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html

通过改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表,如图:

双指针法:

class Solution
{
public:
ListNode* reverseList(ListNode* head) {
ListNode* temp;//保存cur的下一个节点
ListNode* cur = head;
ListNode* pre = NULL;
while (cur) {
temp = cur->next;//保存一下cur的下一个节点,因为接下来要改变cur->next
cur->next = pre;//翻转操作
//更新pre和cur指针
pre = cur;
cur = temp;
}
return pre;
}
};

  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

递归法:

class Solution {
public:
ListNode* reverse(ListNode* pre, ListNode* cur) {
if (cur == NULL) return pre;
ListNode* temp = cur->next;
cur->next = pre;
//可以和双指针的代码进行对比
//双指针:
//pre = cur;
//cur = temp;
//递归:
return reverse(cur, temp);
}
ListNode* reverseList(ListNode* head) {
//和双指针初始化是一样的逻辑
//ListNode* cur=head;
//ListNode* pre=NULL;
return reverse(NULL, head);
}
};

  • 时间复杂度: O(n), 要递归处理链表的每个节点
  • 空间复杂度: O(n), 递归调用了 n 层栈空间
赞(0)
未经允许不得转载:网硕互联帮助中心 » 代码随想录第三天 | 203.移除链表元素 | 707.设计链表 |206.反转链表
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!