3 第二章 链表
day1 任务以及具体安排:训练营一期day 1
day 2 任务以及具体安排:day 2 第一章数组
今日任务
● 链表理论基础
● 203.移除链表元素
● 707.设计链表
● 206.反转链表
详细布置
链表理论基础
建议:了解一下链接基础,以及链表和数组的区别
文章链接:代码随想录
203.移除链表元素
【链接】(文章,视频,题目)
建议: 本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。
题目链接/文章讲解/视频讲解::代码随想录
【第一想法与实现(困难)】暴力实现,但是runtime error,估计是哪里判断的空指针不对,
【看后想法】
-
原链表移除。
-
需要考虑头结点的特殊情况。头结点删掉之后,会有新的头结点需要检查,因此需要一直检查头结点的删除,要用while不是if。
-
注意因为删除某结点,需要删去指向这个结点的指针。实际上每次删除的只能是cur->next,也就是修改cur的next指针。
-
删除非头结点之后,cur->next就更新了,因此再次判断cur->next->val,而cur不需要后移
-
使用某个指针的字段之前,都要检查指针自身非空(因为总是可能有某个后移操作,就到了空指针,导致runtime error)
-
c++实现要手动delete删除掉的结点
-
-
虚拟头结点
-
手动生成与释放一个链表节点的语法,ListNode* dummy_head = new ListNode(0, head);
-
返回的head是虚拟头结点的后继。dummy_head->next
-
最后要将虚拟头结点delete掉
-
【实现困难】
【自写代码】
/**
* 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) {
// 原链表移除
while (head != nullptr && head->val == val) {
ListNode* ori_head = head;
head = head->next;
delete ori_head;
}
ListNode* cur = head;
while (cur != nullptr && cur->next != nullptr) {
if (cur->next->val == val) {
ListNode* next_node_del = cur->next;
cur->next = cur->next->next;
delete next_node_del;
} else {
cur = cur->next;
}
}
return head;
}
};
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
// 虚拟头结点
ListNode* dummy_head = new ListNode(0, head);
ListNode* cur = dummy_head;
while (cur != nullptr && cur->next != nullptr){
if (cur->next->val == val) {
ListNode* next_node_del = cur->next;
cur->next = cur->next->next;
delete next_node_del;
} else {
cur = cur->next;
}
}
head = dummy_head->next;
delete dummy_head;
return head;
}
};
【收获与时长】
707.设计链表
【链接】(文章,视频,题目)
建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点
题目链接/文章讲解/视频讲解:代码随想录
【第一想法与实现(困难)】
-
由于有头尾操作,想用双链表,加上一个size_维护内部大小。
-
自己定义了双链表结点MyListNode,及其构造函数
-
问题:
-
runtime error, 问题在于,虚拟头,虚拟尾是指针,在初始化要手动new,否则永远是空指针
-
如何在class中调用自定义的struct,将结构体放里面外面均可
-
【看后想法】
-
随想录的参考代码是使用单链表,我可以自己在实现一次单链表,ok
-
内部自己增加一个打印链表函数void printLinkedList(),方便debug
【实现困难】
-
对于单链表,找index前一个节点的方法,while(index–)很巧妙。
-
addAtIndex,index0,indexsize_的可以融合到一起,不用再单独写。
【自写代码】
class MyLinkedList {
public:
// 单链表
// - 对于单链表,找index前一个节点的方法,while(index--)很巧妙。
// addAtIndex,index==0,index==size_的可以融合到一起,不用再单独写。
struct MyListNode {
int val;
MyListNode* next;
MyListNode() : val(0), next(nullptr) {}
MyListNode(int x) : val(x), next(nullptr) {}
MyListNode(int x, MyListNode* n) : val(x), next(n) {}
};
MyLinkedList() : dummy_head_(new MyListNode()), size_(0) {}
int get(int index) {
if (index < 0 || index > size_ - 1) {
return -1;
}
MyListNode* cur = dummy_head_;
for (int i = 0; i <= index; ++i) {
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
MyListNode* new_node = new MyListNode(val, dummy_head_->next);
dummy_head_->next = new_node;
size_++;
}
void addAtTail(int val) {
MyListNode* new_node = new MyListNode(val, nullptr);
MyListNode* cur = dummy_head_;
while (cur->next != nullptr) {
cur = cur->next;
}
cur->next = new_node;
size_++;
}
void addAtIndex(int index, int val) {
if (index > size_) {
std::cout << "addAtIndex error, index:" << index << std::endl;
return;
}
if (index < 0) {
index = 0;
}
MyListNode* cur = dummy_head_;
while (index--) {
cur = cur->next;
}
// 在cur后增加
MyListNode* new_node = new MyListNode(val, cur->next);
cur->next = new_node;
size_++;
}
void deleteAtIndex(int index) {
if (index < 0 || index > size_ - 1) {
std::cout << "deleteAtIndex error, index:" << index << std::endl;
return;
}
MyListNode* cur = dummy_head_;
while (index--) {
cur = cur->next;
}
// 在cur后删除
MyListNode* del_node = cur->next;
cur->next = cur->next->next;
delete del_node;
size_--;
}
private:
MyListNode* dummy_head_;
int size_;
};
/**
* 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);
*/
class MyLinkedList {
public:
// 双链表
struct MyListNode {
int val;
MyListNode* prev;
MyListNode* next;
MyListNode(): val(0), prev(nullptr), next(nullptr) {}
MyListNode(int x): val(x), prev(nullptr), next(nullptr) {}
MyListNode(int x, MyListNode* p, MyListNode* n): val(x), prev(p), next(n) {}
};
MyLinkedList(): dummy_head_(new MyListNode()), dummy_tail_(new MyListNode()), size_(0) {
dummy_tail_->prev = dummy_head_;
dummy_head_->next = dummy_tail_;
}
int get(int index) {
if (index >= 0 && index < size_) {
MyListNode* cur = dummy_head_;
for (int i = 0; i <= index; ++i) {
cur = cur->next;
}
return cur->val;
} else {
return -1;
}
}
void addAtHead(int val) {
MyListNode* head = dummy_head_->next;
MyListNode* new_head = new MyListNode(val, dummy_head_, head);
dummy_head_->next = new_head;
head->prev = new_head;
size_++;
}
void addAtTail(int val) {
MyListNode* tail = dummy_tail_->prev;
MyListNode* new_tail = new MyListNode(val, tail, dummy_tail_);
tail->next = new_tail;
dummy_tail_->prev = new_tail;
size_++;
}
void addAtIndex(int index, int val) {
if (index < 0) {
addAtHead(val);
return;
}
if (index == size_) {
addAtTail(val);
return;
}
if (index > size_) {
std::cout << "addAtIndex, wrong index:" << index << std::endl;
return;
}
MyListNode* cur = dummy_head_;
for (int i = 0; i <= index; ++i) {
cur = cur->next;
}
MyListNode* prev = cur->prev;
MyListNode* new_node = new MyListNode(val, prev, cur);
prev->next = new_node;
cur->prev = new_node;
size_++;
}
void deleteAtIndex(int index) {
if (index < 0 || index >= size_) {
std::cout << "deleteAtIndex, wrong index:" << index << std::endl;
return;
}
MyListNode* cur = dummy_head_;
for (int i = 0; i <= index; ++i) {
cur = cur->next;
}
cur->prev->next = cur->next;
cur->next->prev = cur->prev;
delete cur;
size_--;
}
private:
MyListNode* dummy_head_;
MyListNode* dummy_tail_;
int size_;
};
/**
* 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);
*/
【收获与时长】2-3小时
-
指针,在初始化要手动new,否则永远是空指针。析构要手动delete
-
单双链表实现
-
类class中调用结构体struct
-
对于单链表,找index前一个节点的方法,while(index–)很巧妙
206.反转链表
【链接】(文章,视频,题目)
建议先看我的视频讲解,视频讲解中对 反转链表需要注意的点讲的很清晰了,看完之后大家的疑惑基本都解决了。
题目链接/文章讲解/视频讲解:代码随想录
【第一想法与实现(困难)】暴力,用vector存起来。好像没啥困难,就是笨办法
class Solution {
public:
ListNode* reverseList(ListNode* head) {
// 暴力,用vector存起来
std::vector<int> vec;
ListNode* cur = head;
while (cur != nullptr) {
vec.emplace_back(cur->val);
cur = cur->next;
}
cur = head;
for (int i = vec.size() - 1; i >= 0 ; --i, cur = cur->next) {
cur->val = vec.at(i);
}
return head;
}
};
【看后想法】
-
递归方法与双指针方法的while循环,有对应关系。
-
递归结束条件对应while循环结束条件
-
递归返回值对应while循环结束返回值
-
递归函数的变量变化,对应while循环一轮的变量变化
-
【实现困难】
-
双指针。注意while循环结束条件
-
递归。与双指针的对应关系。结束条件,返回值,变化关系
-
从后往前的递归。从后往前的递归。0,1,…,k,…,m。从后往前假设k+1~m已经完成反转,每一步只完成k, k+1的反转。也就是k+1指向k。同时k要指向空。
【自写代码】
【收获与时长】
3小时,有点久了。