当前位置: 首页 > news >正文

【C++初阶】模拟实现list

在这里插入图片描述

👦个人主页:@Weraphael
✍🏻作者简介:目前学习C++和算法
✈️专栏:C++航路
🐋 希望大家多多支持,咱一起进步!😁
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注✨


目录

  • 一、简单剖析list源码
  • 二、准备工作
  • 三、模拟实现list常见操作
      • 3.1 默认构造函数
      • 3.2 push_back - 尾插
      • 3.3 迭代器(重点)
      • 3.4 const的迭代器(重点)
      • 3.5 insert - 插入
      • 3.6 erase - 删除
      • 3.7 头插 - push_front
      • 3.8 尾删 - pop_back
      • 3.9 头删 - pop_front
      • 3.10 个数 - size
      • 3.11 析构
      • 3.12 清空 - clear
      • 3.13 拷贝构造
      • 3.14 交换
      • 3.15 赋值运算符重载
  • 四、源码

一、简单剖析list源码

在模拟vector容量讲过,要想快速了解STL源码,首先要看成员变量

在这里插入图片描述

node从名字上猜测是一个节点,其类型是list_node。然后我发现list_node也是重命名出来的:

在这里插入图片描述

__list_node<T>又是什么东西呢?如下所示:

在这里插入图片描述

显然这是一个双向链表,并且__list_node是用来定义结点的

在这里插入图片描述

接下来就应该分析构造函数

在这里插入图片描述

get_node从名字上是得到结点,那么应该是开辟空间的。我们可以简单看看:

在这里插入图片描述

空间配置器讲起来有点麻烦,直接使用newdelete也是够用的

然后nodenextprev都指向自己。因此list的底层是一个带头(哨兵位)双向循环链表,因此list的成员变量应该是哨兵位结点。

大致结构我们已经知道了,不妨再来看看插入操作:

在这里插入图片描述

这和以往学习过的双向循环链表很相似,无非就是创造新的结点,然后再把它们链接起来。

大致内容已经了解了,直接开始实现吧~

二、准备工作

为了方便管理代码,分两个文件来写:

  • Test.cpp - 测试代码逻辑
  • list.h - 模拟实现list
    在这里插入图片描述

三、模拟实现list常见操作

3.1 默认构造函数

namespace wj
{template<class T>struct list_node // 定义结点{list_node<T>* _next; list_node<T>* _prev;T _val;};template<class T>class list{public:list(){// 为哨兵位头结点开空间_head = new list_node<T>;// 自己指向自己_head->_prev = _head;_head->_next = _head;}private:list_node<T> _head; // 哨兵位(不存储有效数据)};
}

定义结点的成员变量最好是公有的,方便类外可以随时访问。注意:此处的struct可不是C语言的结构体,在C++中已经升级成了类,并且默认成员都是公有的。当然使用class也是没问题的,只是要加上public

以上代码还能简化,我们知道类模板和普通类是不同的,普通类的类名即是类型,而类模板的类名是类名<T>。而有许多人会很容易忘记加上<T>,因此我们可以对list_node<T>进行重命名typedef

namespace wj
{template<class T>struct list_node // 定义结点{list_node<T>* _next; list_node<T>* _prev;T _val;};template<class T>class list{typedef list_node<T> Node;public:list(){// 为哨兵位头结点开空间_head = new Node;// 自己指向自己_head->_prev = _head;_head->_next = _head;}private:list_node<T> _head; // 哨兵位(不存储有效数据)};
}
  • 为了防止与库的list冲突,要重新写一个命名空间域wj
  • typedef在类中是有讲究的。如果typedef放在public段中,则可以在类外部使用;而如果放在private段中,则只能在类内使用。注意:上述代码是只能在类中使用!

3.2 push_back - 尾插

void push_back(const T& val)
{//1. 找尾(哨兵位的prev)Node* tail = _head->_prev;// 2. 开辟一个新节点Node* newnode = new Node(val); // 3. 链接 _head tail newnodetail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;
}

尾插就容易多了,下面有图帮助大家理解:

在这里插入图片描述

注意:new对于自定义类型除了开空间,还会调用构造函数。初始化_val

struct list_node // 结点的定义
{list_node<T>* _next;list_node<T>* _prev;T _val; list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}
};

缺省值给T()相信看过模拟实现vector都不陌生。不能直接给0,这样就写死能,只能int类型适用,对于string就不行了。因此可以给个匿名对象,它会调用T类型的默认构造。内置类型也是有默认构造的:

在这里插入图片描述

3.3 迭代器(重点)

能否定义类似像vector的迭代器?如下所示:

typedef Node* iterator;

答案当然不行!list不能像vector一样以原生指针(普通指针)作为迭代器。

vector类似于数组,数据在内存中是连续存储的。对迭代器(指针)++,就可以跳过一个对象的大小,并且解引用也能得到对应的数据;然而,list的节点不能保证一定在内存空间中连续存在,导致++/--不一定能找到下一个节点,并且对其解引用得到的是结点而不是有效数据。

那问题来了,如何定义list的迭代器呢?

我们可以封装一个类,然后用重载运算符去改变指针的行为。为什么可以这样呢?原因是:内置类型的++是行为规定的,但是自定义类型的++是自己说的算。可以联想以往实现的日期类->点击跳转

auto it = l.begin();
while (it != l.end())
{cout << *it << ' ';++it;
}

我们可以对照以上代码一步一步实现迭代器

  • begin() + end()

在这个类中,只需要一个结点类的指针成员变量,用于指向list某一个结点, 在一开始定义迭代器时,需要一个构造函数,用于迭代器的初始化。注意:beginend需要定义在list类中,因为它们本身就是list内置的接口函数

// 封装一个类实现迭代器
template<class T>
struct __list_iterator 
{typedef list_node<T> Node;Node* _node; //指向某个节点的指针// 迭代器的初始化__list_iterator(Node* node) :_node(node){}
};template<class T>
class list
{typedef list_node<T> Node;
public:typedef __list_iterator<T> iterator; iterator begin(){return _head->_next;// return iterator(_head->_next);}iterator end(){return _head;//return iterator(_head);}
private:Node* _head;
};

这里还有一个知识点,beginend返回类型为迭代器,怎么能返回结点的指针呢?— 这是因为单参数的构造函数支持隐式类型转换。

  • !=== *++--

封装一个类,然后用重载运算符去改变指针的行为

// 封装一个类实现迭代器
template<class T>
struct __list_iterator 
{typedef list_node<T> Node;typedef __list_iterator<T> self;Node* _node; //指向某个节点的指针__list_iterator(Node* node) // 迭代器的初始化:_node(node){}
/// 用结点的指针比bool operator!=(const self& it) const{return _node != it._node;}bool operator==(const self& it) const{return _node == it._node;}
/T& operator*(){// 出了作用域,结点还在,引用返回return _node->_val;}
/// 迭代器++返回的还是迭代器self& operator++() //前置{_node = _node->_next;return *this;}self& operator--() // 前置{_node = _node->_prev;return *this;}self operator--(int) // 后置{self tmp(*this);_node = _node->_prev;return tmp;}self operator++(int) // 后置{self tmp(*this);_node = _node->_next;return tmp;}
};

前置++后置++会发生一个问题:函数名会相同。因此,C++规定:后置(++/--)重载时多增加一个int类型的参数,但调用函数时该参数不用传递。

3.4 const的迭代器(重点)

现在又有一个问题,const的迭代器也能否像类似于vector一样设计?如下所示:

在这里插入图片描述

答案当然是不可以的!这是因为 const迭代器要求的是迭代器指向的内容不可以被修改,而对一个类加上一个const,这是让这个类对象无法被修改啊。也就是类的成员变量都不可以被修改,这样一来,这个迭代器里面的指针无法移动了。(const的迭代器指针是可以移动的,但是指向的内容不可被修改)

那么const的迭代器该如何设计呢?我们知道,list迭代器输出数据是依靠解引用的,因此可以在返回值加上const

template<class T>
struct __list_iterator
{typedef list_node<T> Node;typedef __list_iterator<T> selfNode* _node; //指向某个节点的指针__list_iterator(Node* node) // 迭代器的初始化:_node(node){}// 用结点的指针比bool operator!=(const self& it) const{return _node != it._node;}bool operator==(const self& it) const{return _node == it._node;}T& operator*(){// 出了作用域,结点还在,引用返回return _node->_val;}// 迭代器++返回的还是迭代器self& operator++() //前置{_node = _node->_next;return *this;}self& operator--() // 前置{_node = _node->_prev;return *this;}self operator--(int) // 后置{self tmp(*this);_node = _node->_prev;return tmp;}self operator++(int) // 后置{self tmp(*this);_node = _node->_next;return tmp;}
};template<class T>
struct __list_iterator
{typedef list_node<T> Node;typedef __list_iterator<T> self;Node* _node; //指向某个节点的指针__list_iterator(Node* node) // 迭代器的初始化:_node(node){}// 用结点的指针比bool operator!=(const self& it) const{return _node != it._node;}bool operator==(const self& it) const{return _node == it._node;}const T& operator*(){// 出了作用域,结点还在,引用返回return _node->_val;}// 迭代器++返回的还是迭代器self& operator++() //前置{_node = _node->_next;return *this;}self& operator--() // 前置{_node = _node->_prev;return *this;}self operator--(int) // 后置{self tmp(*this);_node = _node->_prev;return tmp;}self operator++(int) // 后置{self tmp(*this);_node = _node->_next;return tmp;}
};

但以上代码显得有点冗余,只有两个函数的返回值不一样,其它都是一样的。那还有什么别的设计方法呢?

注意:上面两个函数只要返回值的类型不一样,因此可以通过一个类型来控制返回值 -> 即增加一个模板参数(库里也是这么实现的~)

// 封装一个类实现迭代器
template<class T, class Ref> // 增加一个模板参数
struct __list_iterator
{typedef list_node<T> Node;typedef __list_iterator<T, Ref> self;Node* _node; //指向某个节点的指针__list_iterator(Node* node) // 迭代器的初始化:_node(node){}Ref operator*(){return _node->_val;}
}template<class T>
class list
{typedef list_node<T> Node;
public:typedef __list_iterator<T, T&> iterator;typedef __list_iterator<T, const T&> const_iterator;iterator begin(){return _head->_next;}const_iterator end() const{return _head;}const_iterator begin() const{return _head->_next;}iterator end(){return _head;}
private:list_node<T> _head; // 哨兵位(不存储有效数据)
};

补充:除了重载*运算符,当然也要重载->操作符

T* operator->() 
{return &_node->_val;
}

那什么时候会用到->操作符呢?下面有个例子:

#include <iostream>
#include "list.h"
using namespace std;struct A
{A(int a = 0):_a(a){}int _a;
};
int main()
{wj::list<A> lt;lt.push_back(A(1));lt.push_back(A(2));lt.push_back(A(3));lt.push_back(A(4));lt.push_back(A(5));wj::list<A>::iterator it = lt.begin();while (it != lt.end()){cout << it->_a << " ";it++;}cout << endl;
}

【输出结果】

在这里插入图片描述

有没有发现operator->非常怪,首先我们这个运算符重载返回的是什么呢?是T*,也就是A*,也就是说它还需要一次->才能打印_a。严格来说,it->->_a,才是符合语法的。那么这里为什么还能编译通过呢?因为运算符重载要求可读性,那么编译器特殊处理,省略了一个->

但是以上代码还是不够完善,由于->只针对普通对象,如果是const对象,其返回值应该是const T*,这个问题就和运算符重载*类似了,再增加一个模板参数,因此完整代码如下:

template<class T, class Ref, class ptr>
struct __list_iterator // 迭代器
{typedef list_node<T> Node;typedef __list_iterator<T, Ref, ptr> self;Node* _node; //指向某个节点的指针__list_iterator(Node* node) // 迭代器的初始化:_node(node){}Ref operator*(){return _node->_val;// 出了作用域,结点还在,要加&}ptr operator->() {return &_node->_val;}
}template<class T> // 为list提供
class list
{typedef list_node<T> Node; 
public:typedef __list_iterator<T, T&, T*> iterator; typedef __list_iterator<T, const T&, const T*> const_iterator; iterator begin(){// return iterator(_head->_next);return _head->_next;}iterator end(){// return iterator(_head);return _head;}
private:Node* _head; // 哨兵位(不存储有效数据)
};

3.5 insert - 插入

iterator insert(iterator pos, const T& x)
{// pos 不需要检查  // 假设在node前插入// head newnode node tail// 步骤如下// 1. 开辟新的结点Node* newnode = new Node(x);// 2. 找到要删除的结点nodeNode* cur = pos._node;// 3. 以及node的前一个节点Node* prev = cur->_prev;// 4. 链接prev->_next = newnode;newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;return newnode;// 返回新插入元素的位置
}

在这里插入图片描述

3.6 erase - 删除

iterator erase(iterator pos)
{// 检查pos的有效性assert(pos != end());// 1.分别找到pos的前一个节点和后一个节点Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;// 2, 链接prev->_next = next;next->_prev = prev;// 3. 删除delete cur;// 注意:list的erase会有迭代器失效问题// 返回删除元素的下一个位置return next;
}

在这里插入图片描述

3.7 头插 - push_front

复用insert

void push_front(const T& val)
{insert(begin(), val);
}

3.8 尾删 - pop_back

复用erase

void pop_back()
{erase(--end());
}

3.9 头删 - pop_front

void pop_front()
{erase(begin());
}

3.10 个数 - size

遍历即可

size_t size()
{size_t count = 0;iterator it = begin();while (it != end()){++count;++it;}return count;
}

或者还可以在成员变量中定义size_t _size,每次插入数据++,以及删除数据--即可

3.11 析构

~list()
{clear();delete _head;_head = nullptr;
}

3.12 清空 - clear

void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}

3.13 拷贝构造

list(const list<T>& it)
{_head = new Node;_head->_prev = _head;_head->_next = _head;for (auto& e : it){push_back(e);}
}

3.14 交换

void swap(list<T> it)
{std::swap(_head, it._head);std::swap(this->size(), it._size());
}

3.15 赋值运算符重载

list<T>& operator=(const list<T> it)
{swap(it);return *this;
}

四、源码

#pragma once
#include <assert.h>namespace wj
{template<class T> struct list_node {list_node<T>* _next;list_node<T>* _prev;T _val; list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}};template<class T, class Ref, class ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, ptr> self;Node* _node; __list_iterator(Node* node) :_node(node){}Ref operator*(){return _node->_val;}ptr operator->() {return &_node->_val;}self& operator++(){_node = _node->_next;return *this;}self& operator--(){_node = _node->_prev;return *this;}self& operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}bool operator!=(const self& it) const{return _node != it._node;}bool operator==(const self& it) const{return _node == it._node;}};template<class T> class list{typedef list_node<T> Node; public:typedef __list_iterator<T, T&, T*> iterator; typedef __list_iterator<T, const T&, const T*> const_iterator; iterator begin(){// return iterator(_head->_next);return _head->_next;}iterator end(){// return iterator(_head);return _head;}const_iterator begin() const{//return _head->_next;return const_iterator(_head->_next);}const_iterator end() const{return _head;//return const_iterator(_head);}list(){_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;}list(const list<T>& it){_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;for (auto& x : it){push_back(x);}}void push_back(const T& val){Node* tail = _head->_prev;Node* newnode = new Node(val);tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;_size++;}iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;_size++;return newnode;}iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;_size--;return next;}void push_front(const T& val){insert(begin(), val);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}size_t size(){/*size_t count = 0;iterator it = begin();while (it != end()){++count;++it;}return count;*/return _size;}~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}_size = 0;}void swap(list<T> it){std::swap(_head, it._head);std::swap(_size, it._size);}list<T>& operator=(const list<T> it){swap(it);return *this;}private:Node* _head; size_t _size;};
}

测试代码

#include <iostream>
using namespace std;
#include "list.h"int main()
{// 默认构造wj::list<int> ll;// 尾插测试ll.push_back(1);ll.push_back(2);ll.push_back(3);ll.push_back(4);// 迭代器测试wj::list<int>::iterator it = ll.begin();while (it != ll.end()){cout << *it << ' ';it++;}cout << endl;// 范围for(底层迭代器)for (auto& x : ll){cout << x << ' ';}cout << endl;// insert测试// 在3的前面插入30it = ll.begin();for (int i = 0; i < 2; i++){it++;}ll.insert(it, 30);for (auto& x : ll){cout << x << ' ';}cout << endl;//  erase测试it = ll.begin();// 删除30for (int i = 0; i < 2; i++){it++;}ll.erase(it);for (auto x : ll){cout << x << ' ';}cout << endl;// 头插测试// 头插100ll.push_front(100);for (auto x : ll){cout << x << ' ';}cout << endl;// 尾删测试ll.pop_back(); // 100 1 2 3for (auto x : ll){cout << x << ' ';}cout << endl;// 头删测试ll.pop_front(); // 1 2 3for (auto x : ll){cout << x << ' ';}cout << endl;// size测试cout << "个数为:" << ll.size() << endl; // 3// 清空ll.clear();for (auto x : ll){cout << x << ' '; // 无输出}cout << endl;// 拷贝构造ll.push_back(1);ll.push_back(2);ll.push_back(3);ll.push_back(4);ll.push_back(5);wj::list<int> lll(ll);for (auto x : lll){cout << x << ' '; // 1 2 3 4 5}cout << endl;// 赋值运算符重载wj::list<char> a;a.push_back('a');wj::list<char> b;b.push_back('b');b.push_back('b');b.push_back('b');a = b;for (auto x : a){cout << x << ' ';}cout << endl;// 交换wj::list<char> c;a.push_back('c');wj::list<char> d;b.push_back('d');b.push_back('d');b.push_back('d');d.swap(c);for (auto x : c){cout << x << ' ';}cout << endl;for (auto x : d){cout << x << ' ';}cout << endl;return 0;
}

相关文章:

【C++初阶】模拟实现list

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前学习C和算法 ✈️专栏&#xff1a;C航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&#x1…...

三维模拟推演电子沙盘虚拟数字沙盘开发教程第13课

三维模拟推演电子沙盘虚拟数字沙盘开发教程第13课 该数据库中只提供 成都市火车南站附近的数据请注意&#xff0c;104.0648,30.61658 在SDK中为了方便三方数据的接入&#xff0c;引入了一个用户层接口。主要是完成三方数据的接入&#xff0c;含动态数据&#xff08;如GPS&…...

flask中GET和POST的区别

GET和POST是HTTP协议中两种常用的请求方法&#xff0c;它们在如何向服务器发送数据以及数据传输方式上有所不同。下面是GET和POST的主要区别&#xff1a; 一、数据传输位置&#xff1a; GET&#xff1a;将数据通过URL的查询字符串部分&#xff08;即URL的参数&#xff09;传递…...

基于Spring Boot的游泳馆管理系统的设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的游泳馆管理系统的设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java spring…...

git冲突处理(已commit但忘pull的情况)

一般来说&#xff0c;你只要记得先拉再传就不会有问题&#xff0c;但如果pull后没有立刻push&#xff0c;这段时间刚好有人push了&#xff0c;就会导致冲突&#xff0c;那么你可以使用以下方法进行版本回退之后合并代码 步骤&#xff1a; git log查看所有的commit&#xff0c…...

嵌入式设备应用开发(发现需求和提升价值)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 很多做技术的同学,都会陷入到技术的窠臼之中。对于如何做具体的产品、实现具体的技术,他们可能很感兴趣。但是做出来的东西做什么用,或者说是有没有竞争力,事实上他们不是很关心…...

Redis Replication

Redis Replication 1、前言 在单节点搞事情, 存在的问题包括存量问题和增量问题两类, 解决方案就是1个不行上N个, 做到单机维度宕机但服务维度是可用的。 1.1 存量问题 如果目前的单节点QPS满足(也就是综合瓶颈还没达到), 那么只有宕机能影响到。如果业务量不大, 又是出于成…...

软件研发CI/CD流水线图解

当谈到现代软件开发流程时&#xff0c;持续集成&#xff08;Continuous Integration&#xff0c;简称CI&#xff09;和持续交付&#xff08;Continuous Delivery&#xff0c;简称CD&#xff09;是两个关键的实践。它们旨在加速开发流程、提高软件质量&#xff0c;并使软件发布更…...

代码随想录第五十九天

代码随想录第五十九天 Leetcode 503. 下一个更大元素 IILeetcode 42. 接雨水 Leetcode 503. 下一个更大元素 II 题目链接: 下一个更大元素 II 自己的思路:没想到哈哈哈哈&#xff01;&#xff01; 正确思路:这个题在单调栈的情况下转了一个弯&#xff0c;就是需要取一个模操作…...

“yarn“、“npm“、“cnpm“和“pnpm“的区别

"yarn"、"npm"、"cnpm"和"pnpm"的区别 npm优点&#xff1a;缺点&#xff1a; yarn优点&#xff1a;缺点&#xff1a; cnpm优点&#xff1a;缺点&#xff1a; pnpm优点&#xff1a;缺点&#xff1a; 总结&#xff1a; npm npm&#xf…...

批量将txt文件转化为excel文件

可以使用Python的内置库csv和openpyxl来完成这个任务。以下是一个基本的代码示例&#xff1a; import csv from openpyxl import Workbook # 遍历目录中的所有.txt文件 for filename in glob.glob(*.txt): with open(filename, r) as infile: reader csv.reader(…...

StringIndexOutOfBoundsException: String index out of range: 458

报错信息&#xff1a; org.springframework.dao.TransientDataAccessResourceException: ### Error updating database. Cause: java.sql.SQLException: java.lang.StringIndexOutOfBoundsException: String index out of range: 458 ... ... ... 问题原因&#xff1a; <i…...

R语言主成分分析

R语言主成分分析 之前介绍过怎么用SPSS进行主成分分析(PCA)&#xff0c;已经忘了的朋友们可以到主页看看 今天主要介绍下R语言主成分分析的几种方法。都是入门级别&#xff0c;跟着我一步步走&#xff0c;一点都不难哈~ 首先调用R语言自带的数据集&#xff0c;USArrests。这…...

单片机学习-蜂鸣器如何发出声音

硬件电路 软件编写 ①发出声音 #include "reg52.h" typedef unsigned int u16; // 重新定义 类型 typedef unsigned char u8; // 重新定义 类型sbit BEEP P2^5; //定义 P2第五个管教 为BEEP // 延时函数 void delay_time(u16 times) {while(times--); } vo…...

利用敏捷开发工具实现敏捷项目管理的实践经验分享

Scrum中非常强调公开、透明、直接有效的沟通&#xff0c;这也是“可视化的管理工具”在敏捷开发中如此重要的原因之一。通过“可视化的管理工具”让所有人直观的看到需求&#xff0c;故事&#xff0c;任务之间的流转状态&#xff0c;可以使团队成员更加快速适应敏捷开发流程。 …...

代码随想录训练营 贪心02

代码随想录训练营 贪心01 &#x1f338;55. 跳跃游戏&#x1f338;代码 122. 买卖股票的最佳时机 II45. 跳跃游戏 II &#x1f338;55. 跳跃游戏&#x1f338; 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的…...

Linux安装NVM(简洁版)

安装目录 mkdir /opt/nvm && cd /opt/nvm 安装包下载 wget https://github.com/nvm-sh/nvm/archive/refs/tags/v0.39.5.tar.gz 注意&#xff1a;https://github.com/nvm-sh/nvm/tags获取下载链接并替换 安装包解压 for file in *.tar.gz; do tar -zxvf "$file&quo…...

vue 弹出框 引入另一个vue页面

为什么要这么做,适用于在一个页面逻辑比较多的时候,可以搞多个页面,防止出错 index页面点击解约按钮,弹出框 进入jieyue.vue 核心代码 <el-buttonsize"mini"type"text"icon"el-icon-edit"v-if"scope.row.delFlag 0"click"j…...

为Android做一个ShowModal窗口

大家知道&#xff0c;用Delphi实现一个Form&#xff0c;并用ShowModal显示出来&#xff0c;在Android平台是非阻塞的&#xff0c;即执行了Form.ShowModal&#xff0c;代码会继续往下执行而不是等待&#xff0c;这跟在Windows平台是完全不一样的。如果我们需要类似阻塞的效果&am…...

神经网络的工作原理

目录 神经网络的介绍 神经网络的组成 神经网络的工作原理 Numpy 实现神经元 Numpy 实现前向传播 Numpy 实现一个可学习的神经网络 神经网络的介绍 神经网络受人类大脑启发的算法。简单来说&#xff0c;当你睁开眼睛时&#xff0c;你看到的物体叫做数据&#xff0c;再由你…...

Pandas数据分析教程-数据清洗-字符串处理

pandas-02-数据清洗&预处理 D. 字符串处理1. Python自带的字符串处理函数2. 正则表达式3. Series的str属性-pandas的字符串函数文中用S代指Series,用Df代指DataFrame 数据清洗是处理大型复杂情况数据必不可少的步骤,这里总结一些数据清洗的常用方法:包括缺失值、重复值、…...

Nginx 核心配置

一.全局配置 1.1 Nginx 全局配置说明 user nginx nginx; # 启动Nginx⼯作进程的⽤⼾和组 worker_processes [number | auto]; # 启动Nginx⼯作进程的数量 worker_cpu_affinity 00000001 00000010 00000100 00001000; # 将Nginx⼯作进程绑定到指定的CPU核⼼…...

yum命令安装程序

Ubuntu图系统的安装命令&#xff1a; top命令&#xff1a; df -h查看磁盘占用信息 网络状态监控 环境变量和&#xffe5;符号的用法&#xff1a; 上传下载命令&#xff1a; 压缩解压常见&#xff1a;的压缩格式 tar tar解压&#xff1a;...

什么是CSS中的BFC?

①什么是BFC BFC 全称&#xff1a;Block Formatting Context&#xff0c; 名为 “块级格式化上下文”。 W3C官方解释为&#xff1a;BFC它决定了元素如何对其内容进行定位&#xff0c;以及与其它元素的关系和相互作用&#xff0c;当涉及到可视化布局时&#xff0c;Block Forma…...

探讨uniapp的页面问题

1 新建页面 uni-app中的页面&#xff0c;默认保存在工程根目录下的pages目录下。 每次新建页面&#xff0c;均需在pages.json中配置pages列表&#xff1b; 未在pages.json -> pages 中注册的页面&#xff0c;uni-app会在编译阶段进行忽略。pages.json的完整配置参考&am…...

【跨域异常】

想在前端使用vue获取后端接口的数据&#xff0c;但是报了跨域异常&#xff0c;如下图所示。 一种解决的方式是&#xff0c;在后端Controller接口上加上CrossOrigin&#xff0c;从后端解决跨域问题。 还要注意前端请求的url要加上协议&#xff0c;比如http://...

上市公司环境信息披露(含环境负债、管理、监管、业绩与治理披露,2008-2022)

数据简介&#xff1a;环境信息披露是督促企业履行社会责任&#xff0c;推动国民经济社会全面绿色转型和加快实现“双碳”目标的重要抓手。环境信息披露理论缘起于社会责任理论&#xff0c;发展于合法性理论、利益相关者理论、制度理论和信号传递理论等。 结合相关理论&#xf…...

网络基础入门

认识协议 协议其实是一种约定 网络协议初识&#xff1a; 1.内核上以结构体形式呈现 2.操作系统要进行协议管理--先描述&#xff0c;在管理 3.协议的本质是软件&#xff0c;软件是可以分层的&#xff0c;&#xff08;联系C继承多态的知识 &#xff09; 可以参考 &#xff1…...

Oracle工具之SQLLDR

1. 什么是SQLLDR SQLLDR是Oracle服务端自带的工具&#xff0c;用于快速导入大批量数据。 2. 常规路径加载/直接路径加载 sqlldr导入有两种模式&#xff0c;常规路径加载和直接路径加载&#xff0c;默认使用常规路径加载&#xff0c;当directy或者directtrue时&#xff0c;使用…...

探索pytest:Python自动化测试的新境界

在当今的软件开发领域&#xff0c;测试已经不仅仅是一个简单的步骤&#xff0c;而是确保软件质量的核心环节。Python&#xff0c;作为全球最受欢迎的编程语言之一&#xff0c;拥有丰富的测试框架和工具。而在这其中&#xff0c;pytest无疑是最受欢迎和最具影响力的一个。本文将…...