一、移除链表元素-LeetCode 203
Leecode链接: leetcode 203
文章链接: 代码随想录
视频链接: B站
题意:删除链表中等于给定值 val 的所有节点。
示例:
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
思路
基础数据结构题目,删除节点时,遍历指针需要指向前一个指针来保存所删除结点地址,由于头节点可能符合条件,所以需要设置一个虚拟头节点,便于数据删除。
实现代码
//cpp
ListNode* removeElements(ListNode* head, int val) {
if(head == nullptr){//空链表直接返回本身
return head;
}
//不为空链表则构造虚拟头节点
ListNode *vhead = new ListNode(-1);//考虑头节点数据无意义,取-1
ListNode *p = nullptr;//指针
vhead ->next = head;//构建空的头节点
p = vhead;
while(p->next){
if(p->next->val == val){
ListNode *a = p->next;//保存节点
p->next = a->next;
delete a;
a = nullptr;
}
else{
p = p->next;
}
}
head = vhead->next;
delete vhead;
return head;
}
个人问题
熟练度不够,没有立马写出代码。
总结
整体比较简单,需要注意链表循环的退出条件以及删除节点后需要delete以及置空,避免出现未定义行为;删除节点后由于头节点可能被删除,所以返回时需要返回虚拟头节点的下一个节点,即vhead->next。
二、设计链表-LeetCode 707
Leecode链接: LeetCode 707
文章链接: 代码随想录
视频链接: B站
在链表类中实现这些功能:
1.get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
2.addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
3.addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
4.addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
5.deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
思路
相当于写一个不带模板的只支持存储int类型的list。
实现代码
//cpp
class MyLinkedList {
public:
struct LinkedNode{
int val;
LinkedNode *next;
LinkedNode(int val):val(val),next(nullptr){}
};
MyLinkedList() {
dum = new LinkedNode(0);
size = 0;
}
//搜索节点
int get(int index) {
if((index>(size-1))||(index < 0)){
return -1;
}
LinkedNode *p = dum->next;
while(index--){
p = p->next;
}
return p->val;
}
//头插
void addAtHead(int val) {
LinkedNode *p = new LinkedNode(val);
p->next = dum->next;
dum->next = p;
size++;
}
//尾插
void addAtTail(int val) {
LinkedNode *m = new LinkedNode(val);
LinkedNode *p = dum;
while(p->next != nullptr){
p = p->next;
}
p->next = m;
size++;
}
void addAtIndex(int index, int val) {
LinkedNode *p = dum;
if(index>size){
return ;
}
if(index<0){
index = 0;
}
while(index--){
p = p->next;
}
LinkedNode *m = new LinkedNode(val);
m->next = p->next;
p->next = m;
size++;
}
//删除节点
void deleteAtIndex(int index) {
LinkedNode *p = dum;
if(index>=size||size == 0){
return ;
}
while(index--){
p = p->next;
}
LinkedNode *m = p->next;
p->next = m->next;
delete m;
m = nullptr;
size--;
}
void printLinkedList(){
LinkedNode *cur = dum->next;
while(cur){
cout<<cur->val;
cur = cur->next;
}
cout<<endl;
}
private:
int size;
LinkedNode *dum;
};
个人问题
熟练度不够
总结
链表的操作实现没有难点只有熟练度问题。
三.反转链表-LeeCode 206
Leecode链接: LeetCode 206
文章链接: 代码随想录
视频链接: B站
题意:反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
思路
使用双指针,我个人还是理解为三指针,因为双指针无法保存需要插入节点的地址,所以需要有一个中间变量来保存快指针的next,保证当快指针的next指向慢指针时,原有的next值不会丢失。慢指针表示指向已经反转完成的链表的头结点,快指针表示指向待插入的节点,将快指针的next赋值给慢指针,也就实现了由1->2转化为2->1,然后将快指针的地址赋值给慢指针,将中间变量的地址赋值给快指针,为下一个循环做准备,直到快指针为nullprt时退出循环,此时慢指针为新的链表头节点。
实现代码
//cpp
ListNode* reverseList(ListNode* head) {
if(head == nullptr) return head;
ListNode *fast = head;
ListNode *slow = nullptr;
while(fast){
ListNode* cur = fast->next;
fast->next = slow;
slow = fast;
fast = cur;
}
return slow;
}
个人问题
while循环的退出条件写的不够熟练,容易出现访问非法空间导致未定义行为。
总结
总体简单,在单链表中,对链表的操作如果涉及删除添加,都需要有一个指针去指向需要操作节点的前一个节点,这样才能保证链表的连贯性。时间复杂度O(n),空间复杂度O(1)。