链表
分享在b站看的评论,有助于我理解临时的链表指针和实际节点间的关系
Hustune
看不懂的听一下我的思路
我觉得 链表的创建本身是用到了同样地址的两个指针在其中一个指针的数据(即next)改变时 另一个指针也会做相同操作
所以创建链表的过程本质就变成了这样
1,创建新节点(第一次是head)
2,将pre指针与head关联起来 这样pre的next指向哪里 head的next也指向哪里3,pre的next指向新节点(p1) head的next也指向了p1 4,pre断开与head的链接 与p1关联起来
这样pre的next指向哪里 p1的next也指向哪里…
这样两步循环往复 链表的每个节点就关联起来了 至于新节点的next设置为nullptr
我只认为这是一种为了更好检测节点的末尾或者用于保险(防止next不经意间指向其他地方) 关于输出和操作 我觉得看了我上面的个人观点
在结合草稿演算 应该是能搞懂的以上纯属个人观点 如有错误 请大佬们指出
203.移除链表元素
https://leetcode.cn/problems/remove-linked-list-elements/
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
/**
* 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* dummy = new ListNode(0); //虚拟头节点
dummy->next = head;
ListNode* cur = dummy;
while (cur->next != nullptr) {
if( cur->next->val == val){
//删除节点
ListNode* temp = cur->next;
cur->next = cur->next->next;
delete temp;
}
else {
cur = cur->next;
}
}
head = dummy->next; // 更新头节点
delete dummy;
return head;
}
};
707.设计链表
https://leetcode.cn/problems/design-linked-list/
class MyLinkedList {
// 定义链表节点结构体
struct ListNode {
int val;
ListNode* next;
ListNode(int val) : val(val), next(nullptr) {}
};
public:
MyLinkedList() {
_dummy = new ListNode(0);//虚拟头节点,不是链表真实的节点
_size = 0;
}
int get(int index) {
if (index >= _size || index < 0) {
//index从0开始计数 //index 的范围是 [0, _size - 1]
return -1;
}
ListNode* cur = _dummy;
while (index) {
cur = cur->next;
--index;
}//看图解 cur会指向index的前一个节点,也可以理解添加和删除元素时cur的轨迹
return cur->next->val;
}
void addAtHead(int val) {
ListNode* cur = _dummy;
ListNode* temp = new ListNode(val);
temp->next = cur->next;
cur->next = temp;
_size++; // 更新长度
}
void addAtTail(int val) {
ListNode* cur = _dummy;
ListNode* temp = new ListNode(val);
while (cur->next != nullptr) {
cur = cur->next;
}
cur->next = temp;
_size++;
}
void addAtIndex(int index, int val) {
if (index > _size || index < 0) {
//index == _size 时,即在尾节点后添加元素
return;
}
ListNode* cur = _dummy;
ListNode* temp = new ListNode(val);
while (index) {
cur = cur->next;
index--;
}
temp->next = cur->next;
cur->next = temp;
_size++;
}
void deleteAtIndex(int index) {
if (index >= _size || index < 0) {
//index 的范围是 [0, _size - 1]
return;
}
ListNode* cur = _dummy;
while (index) {
cur = cur->next;
index--;
}
ListNode* temp = cur->next;
cur->next = cur->next->next;
delete temp;
_size--;
}
private:
int _size;
ListNode* _dummy;
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new MyLinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
对于在链表末尾添加元素,创建新的节点对象时有temp->next = nullptr;
;所以temp->next = cur->next;
可以省略,只需要将尾节点 指向要添加的节点 即可。
206.反转链表
https://leetcode.cn/problems/reverse-linked-list/
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
迭代
class Solution {
public:
ListNode* reverseList(ListNode* head) {
//迭代
ListNode* dummy = new ListNode(0);
ListNode* cur = head;
ListNode* pre = nullptr;
while (cur != nullptr) {
ListNode* temp = cur->next; //存储后续节点
cur->next = pre; //反转当前节点
pre = cur;
cur = temp;
}
return pre;
}
};
反转时,对于头节点,要让其指向nullptr,其他节点指向它的前一个节点;
需要一个临时变量temp来保存cur->next,来保存原链表的遍历信息;
需要一个变量pre来保存cur的前一个节点,初始值即为nullptr;
遍历结束时,pre做为新节点的头节点,返回即可。
递归
class Solution {
public:
ListNode* reverseList(ListNode* head) {
return reverse(head, nullptr);
}
ListNode* reverse(ListNode* cur, ListNode* pre) {
if (cur == nullptr) return pre;
ListNode* temp = cur->next;
cur->next = pre;
return reverse(temp,cur);
}
};
reverse(1, nullptr); --> reverse(2, 1); --> reverse(3, 2); --> reverse(4, 3); --> reverse(5, 4); --> reverse(nullptr, 5);
其实就是将更新pre和cur的过程pre = cur;cur = temp;
使用递归呈现。