代码随想录算法训练营第三天
学习新知识链表,刷题3道,先学习链表理论基础,然后刷题。 203.移除链表元素 707.设计链表 206.反转链表
● 203.移除链表元素
碎碎念:思路都会,代码语法不知道怎么写,伪代码写的太多也不好,还是得上机啊,两个版本,第一个是不加虚拟头结点,第二个是加的,实践证明加了的好用。
ps:得补c++语法了,要学的可真多啊,我还在输液,心情很不美丽。
1.直接使用原来的链表来进行移除节点操作:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
//删除头节点
while(head != NULL && head -> val == val){
ListNode* tmp = head; //链表创建新指针
head = head -> next;
delete tmp; //释放空间,c语言是free()
}
//删除非头结点。
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;
}
};
2.设置一个虚拟头结点在进行移除节点操作:
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummyHead = new ListNode(0); //设置新结点的写法
dummyHead -> next = head;
ListNode* cur = dummyHead;
while(cur -> next != NULL){
if(cur -> next -> val == val){
ListNode* tmp = cur -> next;
cur -> next =cur -> next -> next;
delete tmp;
}
else{
cur = cur -> next;
}
}
head = dummyHead -> next;
delete dummyHead;
return head;
}
};
● 707.设计链表
题目链接/文章讲解/视频讲解:https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html
碎碎念:链表的常用操作,需要掌握。
class MyLinkedList {
public:
struct LinkedNode{ //构建链表结点的结构体
int val;
LinkedNode* next;
LinkedNode(int val):val(val),next(nullptr){}
};
//初始化链表
MyLinkedList() {
dummyhead = new LinkedNode(0); //定义虚拟头结点,不是真的结点
size = 0;
}
//获取链表中第 index 个节点的值。如果索引无效,则返回-1。
int get(int index) {
if(index > (size - 1) || index < 0){
return -1;
}
LinkedNode* cur = dummyhead -> next;
while(index--){
cur = cur -> next;
}
return cur -> val;
}
//在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
void addAtHead(int val) {
LinkedNode* newNode = new LinkedNode(val); //创建新结点,并赋值为val
newNode -> next = dummyhead -> next;
dummyhead -> next = newNode;
size++;
}
//将值为 val 的节点追加到链表的最后一个元素。
void addAtTail(int val) {
LinkedNode* newNode = new LinkedNode(val);
LinkedNode* cur = dummyhead;
while(cur -> next != nullptr){ //nullptr是C++空指针类型的关键字
cur = cur -> next;
}
cur -> next = newNode;
size++;
}
//在链表中的第 index 个节点之前添加值为 val 的节点。
//如果 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 个节点。
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;
size--;
}
//打印链表
void printLinkedList(){
LinkedNode* cur = dummyhead;
while(cur -> next != nullptr){
cout << cur -> next -> val << " ";
cur =cur -> next;
}
cout << endl;
}
private: //因为初始化里没有定义全局?
int size;
LinkedNode* dummyhead;
};
● 206.反转链表
题目链接/文章讲解/视频讲解:https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html
碎碎念:先是双链表指针法,然后根据它的思想可以改写成面试官偏爱的递归写法,emmmm,感觉遍历一遍,挨个头插也是可以的,回头试一下。
PS:休息了,不对,去打游戏了,好像有一点点心动的感觉,喜欢一个人就是要和她一起打很多很多把游戏,散会!
双链表指针法:
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;
}
};
递归法:
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);
}
};
我们可以发现,上面的递归写法和双指针法实质上都是从前往后翻转指针指向,其实还有另外一种与双指针法不同思路的递归写法:从后往前翻转指针指向:
class Solution {
public:
ListNode* reverseList(ListNode* head) {
// 边缘条件判断
if(head == NULL) return NULL;
if (head->next == NULL) return head;
// 递归调用,翻转第二个节点开始往后的链表
ListNode *last = reverseList(head->next);
// 翻转头节点与第二个节点的指向
head->next->next = head;
// 此时的 head 节点为尾节点,next 需要指向 NULL
head->next = NULL;
return last;
}
};
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端