链表理论基础
203.移除链表元素
题目链接:203.移除链表元素
视频讲解:https://www.bilibili.com/video/BV18B4y1s7R9/手把手带你学会操作链表 | LeetCode:203.移除链表元素_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV18B4y1s7R9/
题目描述:
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
注意点
1、头节点为空
2、头节点满足
Node.val == val
条件
解题思路1
考察删除节点操作。使用虚拟头节点法。
让cur始终指向不满足
Node.val == val
条件的结点
代码1
/**
* 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* removeElements(ListNode* head, int val) {
ListNode* dummyhead = new ListNode(0,head);
ListNode* cur = dummyhead;
while(cur->next)
{
if(cur->next->val == val)
{
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = nullptr;
}
else
cur = cur->next;
}
return dummyhead->next;
}
};
解题思路2
从头节点开始遍历。
增加一个判断头节点是否满足
Node.val == val
条件的循环
代码2
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 删除头结点
while (head != NULL && head->val == val) { // 注意这里不是if
ListNode* tmp = head;
head = head->next;
delete tmp;
}
// 删除非头结点
ListNode* cur = head;
while (cur != NULL && cur->next!= NULL) {
if (cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
} else {
cur = cur->next;
}
}
return head;
}
};
707.设计链表
题目链接:707.设计链表
视频讲解:707.设计链表帮你把链表操作学个通透!LeetCode:707.设计链表_哔哩哔哩_bilibili707.设计链表
题目描述:
你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val
和 next
。val
是当前节点的值,next
是指向下一个节点的指针/引用。
如果是双向链表,则还需要属性 prev
以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList
类:
MyLinkedList()
初始化MyLinkedList
对象。int get(int index)
获取链表中下标为index
的节点的值。如果下标无效,则返回-1
。void addAtHead(int val)
将一个值为val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。void addAtTail(int val)
将一个值为val
的节点追加到链表中作为链表的最后一个元素。void addAtIndex(int index, int val)
将一个值为val
的节点插入到链表中下标为index
的节点之前。如果index
等于链表的长度,那么该节点会被追加到链表的末尾。如果index
比长度更大,该节点将 不会插入 到链表中。void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为index
的节点。
示例:
输入 ["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"] [[], [1], [3], [1, 2], [1], [1], [1]] 输出 [null, null, null, null, 2, null, 3] 解释 MyLinkedList myLinkedList = new MyLinkedList(); myLinkedList.addAtHead(1); myLinkedList.addAtTail(3); myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3 myLinkedList.get(1); // 返回 2 myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3 myLinkedList.get(1); // 返回 3
各方法实现思想步骤
1、代码结构补全
补全链表构造,参数定义
struct LinkedNode{
int val;
LinkedNode* next;
LinkedNode(int val): val(val),next(nullptr){};
};
private:
int _size;
LinkedNode* dummyhead;
2、构造函数补全
使用虚拟头节点,之后的方法实现让cur指向该虚拟头节点后遍历,可以保证cur始终位于要处理的节点的前一个节点处,好处在于不会出现空指针异常
MyLinkedList() {
dummyhead = new LinkedNode(-1);//虚拟头节点
_size = 0;
}
3、void addAtHead(int val)
void addAtHead(int val) {
LinkedNode* node = new LinkedNode(val);
node->next = dummyhead->next;
dummyhead->next = node;
_size++;
}
遵循从后往前操作
4、void addAtTail(int val)
遍历至最后一个节点(cur->next != nullptr)
void addAtTail(int val) {
LinkedNode* cur = dummyhead;
while(cur->next != nullptr) cur = cur->next;
LinkedNode* node = new LinkedNode(val);
cur->next = node;
_size++;
}
5、 void addAtIndex(int index, int val)
void addAtIndex(int index, int val) {
if(index >= 0 && index < _size)
{
LinkedNode* node = new LinkedNode(val);
LinkedNode* cur = dummyhead;
while(index--) cur = cur->next;
node->next = cur->next;
cur->next = node;
_size++;
}
else if(index == _size)
{
addAtTail(val);
}
else return ;
}
6、void deleteAtIndex(int index)
void deleteAtIndex(int index) {
if(index >= 0 && index < _size)
{
LinkedNode* cur = dummyhead;
while(index--)
{
cur = cur->next;
}
LinkedNode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
tmp = nullptr;
_size--;
}
else return ;
}
206.反转链表
题目链接:206.反转链表
题目讲解:帮你拿下反转链表 | LeetCode:206.反转链表 | 双指针法 | 递归法_哔哩哔哩_bilibili
题目描述:
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
解题思路
1、双指针法
初始化指针pre和cur,pre指向nullptr,cur指向链表头节点。每次反转链表时从后往前操作:
先保存cur->next,再更新cur->next。之后向前移动时从前往后操作:先改变pre,再改变cur。直到cur==nullptr,返回pre。
2、递归式法
将双指针法中的pre和cur更新过程改成递归式法
代码
双指针写法:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* cur = head;
while(cur)
{
ListNode* tmp = cur->next;
cur->next = pre;
pre = cur;//步骤1
cur = tmp;//步骤2
}
return pre;
}
};
递归写法:
class Solution {
public:
ListNode* reverse(ListNode* cur, ListNode* pre)
{
if(cur == nullptr) return pre;
ListNode* tmp = cur->next;
cur->next = pre;
return reverse(tmp,cur);
}
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* cur = head;
return reverse(cur,pre);
}
};
由上述代码可看出递归写法将原先pre = cur和cur = tmp改成了return reverse(tmp,cur)。