算法打卡|Day3 链表part01
Day3 链表part01
今日任务
● 链表理论基础
● 203.移除链表元素
● 707.设计链表
● 206.反转链表
链表理论基础
文章链接:https://programmercarl.com/链表理论基础.html
重点:
- 单链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。

//单链表实现代码
#include <iostream>
using namespace std;
struct Node
{
int val;
Node* next;
} *head;
int main()
{
for (int i = 1; i <= 5; i ++ )
{
Node* p = new Node();
p->val = i;
p->next = head;
head = p;
}
for (Node* p = head; p; p = p->next)
cout << p->val << ' ';
cout << endl;
return 0;
}
Problem: 203. 移除链表元素
思路
首先最原始的思路是我们可以将头结点和后面的结点分开处理。但是为了逻辑统一我们可以用虚拟头结点的方式删除链表指定元素。
解题方法
虚拟头结点
Code
/**
* 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) {}
* };
*/
/**
时间复杂度: O(n)
空间复杂度: O(1)
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummy = new ListNode(-1);//涉及到单向链表头结点可以用虚拟头结点的技巧
dummy->next = head;
ListNode* i = dummy;
while(i->next!=nullptr){
if(i->next->val == val){
//记住用临时变量保存删除
ListNode* tmp = i->next;
i->next = i->next->next;
delete tmp;
} else{
i = i->next;
}
}
//利用虚拟头结点时候要注意最后真实的头结点是由虚拟头结点确定的,所以要记得更新再释放
head = dummy ->next;
delete dummy;
return head;
}
};
Problem: 707. 设计链表
思路
注意index下标的遍历,然后插入和删除都要在index前一位开始停下操作。
链接:
https://leetcode.cn/problems/design-linked-list/solutions/1738065/by-linken_54-7moa/
Code
class MyLinkedList {
public:
int len;
struct Listnode{
int val;
Listnode* next;
Listnode(): val(0),next(nullptr){}
Listnode(int _val): val(_val),next(nullptr){}
Listnode(int _val, Listnode* _next): val(_val),next(_next){}
};
Listnode* dummynode;
MyLinkedList() {
len = 0;
dummynode = new Listnode();
}
int get(int index) {
if(index<0 || index >len-1) return -1;
Listnode* cur = dummynode->next;//如果从真实头结点开始遍历,那么index--循环就是index所指示的地方
while(index--){
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
if(val<0||val>1000) return;//val值无效,直接返回
Listnode* head=new Listnode(val,dummynode->next);
dummynode->next = head;
len++;
}
void addAtTail(int val) {
if(val<0||val>1000) return;//val值无效,直接返回
Listnode* tail = dummynode;
//遍历尾结点可以直接用tail->next去判断尾结点
while(tail->next){
tail = tail->next;
}
tail->next = new Listnode(val);
len++;
}
void addAtIndex(int index, int val) {
if(val<0||val>1000||index>len) return;//val值或index值无效,直接返回
if(index<=0) addAtHead(val);//index<=0时,在头部插入值为val的新结点
else if(index==len) addAtTail(val);//index=len时,在尾部插入值为val的新结点
else{
Listnode* cur = dummynode;
while(index--) cur=cur->next;
cur->next = new Listnode(val,cur->next);
len++;
}
}
void deleteAtIndex(int index) {
if(index<0||index>len-1) return;//index值无效,直接返回
Listnode* cur = dummynode;//如果从虚拟头结点开始遍历,那么index--循环就是index前一位所指示的地方
while(index--){
cur = cur->next;
}
Listnode* tmp = cur->next;
cur->next = cur->next->next;
delete tmp;
len--;
}
};
/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList* obj = new Myb LinkedList();
* int param_1 = obj->get(index);
* obj->addAtHead(val);
* obj->addAtTail(val);
* obj->addAtIndex(index,val);
* obj->deleteAtIndex(index);
*/
Code2
class MyLinkedList {
private:
struct Listnode{
int val;
Listnode* next;
Listnode(): val(0),next(nullptr){}
Listnode(int _val): val(_val),next(nullptr){}
Listnode(int _val,Listnode* _node): val(_val), next(_node){}
};
int len;
Listnode* dummyhead;
public:
MyLinkedList(){
len = 0;
dummyhead = new Listnode();
}
int get(int index) {
if(index<0 || index > len-1) return -1;
Listnode* cur = dummyhead->next;
for(int i = 0;i!=index;i++){
cur = cur->next;
}
return cur->val;
}
void addAtHead(int val) {
if(val<0||val>1000) return;
Listnode* node = new Listnode(val, dummyhead->next);
dummyhead->next = node;
len++;
}
void addAtTail(int val) {
if(val<0||val>1000) return;
Listnode* i;
for(i =dummyhead; i->next; i = i->next){}
i->next = new Listnode(val);
len++;
}
void addAtIndex(int index, int val) {
if(val<0||val>1000||index>len) return;
if(index<=0) addAtHead(val);
else if(index==len) addAtTail(val);
else{
Listnode* cur =dummyhead;
for(int i = -1; i!=index-1; i++){
cur = cur->next;
}
cur->next = new Listnode(val,cur->next);
len++;
}
}
void deleteAtIndex(int index) {
if(index<0||index>len-1) return;
Listnode* cur = dummyhead;
for(int i = -1;i!=index-1; i++){
cur = cur->next;
}
Listnode* tmp = cur->next;
cur->next =cur->next->next;
delete tmp;
len--;
}
};
/**
* 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);
*/
Problem: 206. 反转链表
思路
链表题,多画图!
好理解的双指针
1.定义两个指针: pre 和 cur ;pre 在前 cur 在后。
2.每次让 pre 的 next 指向 cur ,实现一次局部反转
3.局部反转完成之后,pre 和 cur 同时往前移动一个位置
4.循环上述过程,直至 pre 到达链表尾部
简洁的递归
1.使用递归函数,一直递归到链表的最后一个结点,该结点就是反转后的头结点,记作 ret .
2.此后,每次函数在返回的过程中,让当前结点的下一个结点的 next 指针指向当前节点。
3.同时让当前结点的 next 指针指向 NULL ,从而实现从链表尾部开始的局部反转
4.当递归函数全部出栈后,链表反转完成。
解题方法
双指针和递归
Code1: 双指针
//时间复杂度: O(n)
//空间复杂度: O(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* reverseList(ListNode* head) {
ListNode* a = nullptr;
ListNode* b = head;
while(b){
ListNode* tmp = b->next;
b->next = a;
a = b;
b = tmp;
}
return a;
}
};
Code2: 递归法
//时间复杂度: O(n), 要递归处理链表的每个节点
//空间复杂度: O(n), 递归调用了 n 层栈空间
/**
* 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* reverseList(ListNode* head) {
if(head == nullptr||head->next == nullptr) return head;
else{
ListNode* tail=reverseList(head->next);
head->next->next = head;
head->next =nullptr;
return tail;
}
}
};
算法打卡|Day3 链表part01的更多相关文章
- cc150:实现一个算法来删除单链表中间的一个结点,仅仅给出指向那个结点的指针
实现一个算法来删除单链表中间的一个结点,仅仅给出指向那个结点的指针. 样例: 输入:指向链表a->b->c->d->e中结点c的指针 结果:不须要返回什么,得到一个新链表:a- ...
- 算法:输入一个链表,输出该链表中倒数第k个结点。
算法:输入一个链表,输出该链表中倒数第k个结点.<剑指offer> 思路加到注释里面了: 1:两个if判断是否返回值为空,首个为空,没有第k个值: 2:for循环找到倒数第k个值,返回为a ...
- LeetCode初级算法的Python实现--链表
LeetCode初级算法的Python实现--链表 之前没有接触过Python编写的链表,所以这里记录一下思路.这里前面的代码是和leetcode中的一样,因为做题需要调用,所以下面会给出. 首先定义 ...
- 数据结构与算法之美 06 | 链表(上)-如何实现LRU缓存淘汰算法
常见的缓存淘汰策略: 先进先出 FIFO 最少使用LFU(Least Frequently Used) 最近最少使用 LRU(Least Recently Used) 链表定义: 链表也是线性表的一种 ...
- 算法之python创建链表实现cache
算法之python创建链表实现cache 本节内容 问题由来 解决思路 实现代码 总结 1. 问题由来 问题起因于朋友的一次面试题,面试公司直接给出两道题,要求四十八小时之内做出来,语言不限,做出来之 ...
- JavaScript 版数据结构与算法(三)链表
今天,我们要讲的是数据结构与算法中的链表. 链表简介 链表是什么?链表是一种动态的数据结构,这意味着我们可以任意增删元素,它会按需扩容.为何要使用链表?下面列举一些链表的用途: 因为数组的存储有缺陷: ...
- 实用算法系列之RT-Thread链表堆管理器
[导读] 前文描述了栈的基本概念,本文来聊聊堆是怎么会事儿.RT-Thread 在社区广受欢迎,阅读了其内核代码,实现了堆的管理,代码设计很清晰,可读性很好.故一方面了解RT-Thread内核实现,一 ...
- 数据结构和算法 c#– 1.单项链表
1.顺序存储结构 Array 1.引用类型(托管堆) 2.初始化时会设置默认值 2.链式存储结构 2.1.单向链表 2.2.循环链表 2.3.双向链表
- 008实现一个算法从一个单链表中返回倒数第n个元素(keep it up)
我们维护两个指针, 它们之间的距离为n. 然后.我将这两个指针同步地在这个单链表上移动,保持它们的距离 为n不变. 那么, 当第二个指针指到空时.第一个指针即为所求. #include <ios ...
- Java数据结构和算法(七)——链表
前面博客我们在讲解数组中,知道数组作为数据存储结构有一定的缺陷.在无序数组中,搜索性能差,在有序数组中,插入效率又很低,而且这两种数组的删除效率都很低,并且数组在创建后,其大小是固定了,设置的过大会造 ...
随机推荐
- MAUI Blazor 项目实战 - 从0到1轻松构建多平台应用UI
前言 最近在项目中尝鲜了MAUI,总体感受下来还是挺不错的,优缺点并存,但是瑕不掩瑜,目前随着.Net版本的迭代升级对它的支持也越来越友好,相信未来可期!感兴趣的朋友欢迎关注.文章中如有不妥的地方,也 ...
- gitlab docker升级报错
背景 使用docker部署gitlab(9.5.4)后,发现合并代码有问题 日志: 看gitlab官网此问题已修复,由于上传了一批代码,又懒得重建,决定对gitlab升级 docker启动命令: do ...
- Linux系统运维之FastDFS集群部署
一.简介 FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储.文件同步.文件访问(文件上传.文件下载)等,解决了大容量存储和负载均衡的问题.FastDFS服务端有两个 ...
- JS逆向实战19——通杀webpack逆向
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 网站 aHR0cHM6Ly ...
- ##Can not deserialize instance of java.lang.String out of START_OBJECT token
请求中定义了一个String字段,该字段主要是一个JSON Object字符串,对应的Java PO的相关字段类型是String. 但是测试的时候传的参数是JSON对象,例如{"aa&quo ...
- 【干货向】我想试试教会你如何修改Git提交信息
Git是目前IT行业使用率最高的版本控制系统,相信大家在日常工作中也经常使用,每次Git提交都会包含提交信息,常用的包括说明.提交人和提交时间等,此篇文章主要向大家介绍下如何修改这些信息,这些命令在正 ...
- 推荐一款C#开源的操作简单、免费的屏幕录制和GIF动画制作神器
前言 今天要给大家推荐一款由C#语言开发且开源的操作简单.免费的屏幕录制和GIF动画制作神器:ScreenToGif . 工具介绍 ScreenToGif 是一款免费的开源屏幕录制和GIF 制作工具. ...
- 推荐一款.NET开源跨平台的开箱即用的DNS服务器软件
前言 今天要给大家推荐一款.NET开源跨平台的开箱即用的DNS服务器软件(用于提供 DNS 解析服务):Technitium DNS Server. 项目介绍 Technitium DNS Serve ...
- 前端关于table的设置
表格超长度后加... table{ table-layout:fixed; } td{ overflow:hidden; text-overflow:ellipsis; white-space:now ...
- 利用python的PyPDF2和PyMuPDF库玩转PDF的提取、合并、旋转、缩放、加密
一.安装PyPDF2和PyMuPDF库 pip install PyPDF2 pip install pymupdf # fitz是pymupdf的子模块 二.工具类代码 from PyPDF2 im ...