算法打卡|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数据结构和算法(七)——链表
前面博客我们在讲解数组中,知道数组作为数据存储结构有一定的缺陷.在无序数组中,搜索性能差,在有序数组中,插入效率又很低,而且这两种数组的删除效率都很低,并且数组在创建后,其大小是固定了,设置的过大会造 ...
随机推荐
- Hive执行计划之什么是hiveSQL向量化模式及优化详解
Hive开启向量化模式也是hiveSQL优化方法中的一种,可以提升hive查询速率,也叫hive矢量化. 问题1:那么什么是hive向量化模式呢? 问题2:hive向量化什么情况下可以被使用,或者说它 ...
- 基于php的外卖订餐网站(php+mysql)
介绍 一个基于php的外卖订餐网站,包括前端和后台. 效果演示 http://101.43.124.118:8001/admin 源码地址 https://github.com/geeeeeeeek/ ...
- 花了一周时间,总算把mysql的加锁搞清楚了,再也不怕间隙锁和next-key了
接触mysql都知道在mysql中有很多锁,共享锁(S).排他锁(X).间隙锁(gap).next-key,当然还有意向锁.表锁等.今天不讲别的,专门来看下innodb引擎下的锁是什么样子的. 现在有 ...
- git reset --hard 撤回后commit的代码消失了的解决办法
楼主在今天的工作中使用了这个命令 git reset --hard 撤回后commit的代码消失了,因为有commit,所以暂时得到了拯救,太不容易了,差点以为自己写的代码没了. 网上到处找帖子,看看 ...
- 我在使用Winform7.0开发海康相机应用的时候系统悄无声息的退出
一.简介 1.说明一下 最近,我在开发一个玻璃幕墙检测的项目,这个项目需要使用到海康的相机系统.业务是这样的,相机按着指定的坐标,扫描玻璃幕墙的每块玻璃,通过算法查看是否有损坏的,如果有就发出报警信息 ...
- 【AI新趋势期刊#1】GPT自动理解视频、AI法律顾问、大模型安全围栏
每天都要浏览大量AI相关新闻,是不是感到信息量爆炸,有效信息少? 这么多新产品和新工具,到底哪些是真正是有价值的,哪些只是浮躁的一时热点? 想参与AI产品和工具的开发,从哪里能够获得大量的灵感和思路? ...
- Unity的Console的控制类LogEntries:深入解析与实用案例
使用Unity Console窗口的LogEntries私有类实现自定义日志系统 在Unity开发过程中,我们经常需要使用Console窗口来查看程序运行时的日志信息.Unity内置的日志系统提供了基 ...
- Vue-treeselect 实现下拉树懒加载,末节点不要箭头
项目需要,可选择的下拉树,由于数据过多,显示要有层级感,所以使用了懒加载模式 vue-treeselect官网:https://www.vue-treeselect.cn/ 1.前端代码 1.下载依赖 ...
- http方式内网搭建CDH6.3.2与部分组件优化
Cloudera_Manager_6.3.2安装配置文档 1. 配置准备 Cloudera Manager (简称CM)用于管理CDH6集群,可进行节点安装.配置.服务配置等,提供Web窗口界面提高了 ...
- ubuntu 终端选中黏贴、自带截图
鼠标选中 -- 复制 鼠标中键 -- 粘贴 注意,在tmux中,这个操作需要加上 Shift 键. PrtSc:截图整个桌面保存到Pictures Ctrl + PrtSc:截图整个桌面到剪贴板 Sh ...