list模拟实现

news2024/11/29 22:42:59

image-20221213014908853

文章目录

  • list的介绍
  • list和vector的对比
    • **list和vector对于排序算法速度的比较**
    • **list和vector对于迭代器的比较**
  • **list的模拟实现**
    • **框架**
      • **节点**
      • **迭代器**
        • **普通迭代器-普通写法**
        • **const 迭代器-普通写法**
        • **迭代器-高级写法**
        • **链表结构**
        • **关于节点的析构**
        • **关于迭代器没有实现拷贝构造和赋值为什么可以用?**

list的介绍

1、list是可以在常数范围内在任意位置进行插入和删除(相比其他容器如vector、string等等效率更高)

2、list底层是带头双向链表结构,每个元素存储在互不相干的独立节点,节点之间通过指针相互联系

3、与其他容器相比,list存在的最大缺陷就是不能随机访问即不能通过【】访问(list的内存空间的不连续的)

list和vector的对比

vector

优点缺点
下标随机访问
尾插尾删效率高前面部分插入删除数据效率低O(N)
cpu高速缓存命中高扩容有损耗,还有一定的空间浪费

list

优点缺点
按需申请释放空间,无需扩容不支持下标随机访问
任意位置插入删除O(1)cpu高速缓存命中低

可以看到vector和list是互补配合的!

list和vector对于排序算法速度的比较

这里我创建了一个list和vector,对两者同时插入100w个和1000w个随机数,插入后记一次时间,然后进行排序,排完序再记一次时间,两次时间相差即排序的时间, 然后对两者时间比较;理论存在实践开始:

//要包含的头文件
#include<iostream>
#include<list>
#include<vector>
#include<time.h>
#include<algorithm>
using namespace std;

void test_sort_time()
{
	srand(time(0));
	const int N = 1000000;//100w个随机数
	vector<int> v;
	v.reserve(N);//扩容
	list<int> lt1;
	
for (int i = 0; i < N; ++i)
	 {
		auto e = rand();
		lt1.push_back(e);
	 }
	int begin1 = clock();
	lt1.sort();
	int end1 = clock();//记的list排序时间
	
for (int i = 0; i < N; ++i)
	 {
		auto e = rand();
		v.push_back(e);
	 }
	
	int begin2 = clock();
sort(v.begin(), v.end());
	int end2 = clock();//记的vector排序时间
	
printf("list sort:%d\n", end1 - begin1);//打印list排序时间
printf("vector sort:%d\n", end2 - begin2);//打印vector排序时间
	

debug版本下,得出结论vector排序比list块,但两者差距不是很明显【可能是我这破电脑的缘故(doge】

image-20221210195518840

release版本下,可以看到两者差距还是非常大的,甚至把list里面的数据导入vector里排序完再导回list的时间都比本身list排序的时间短(各位彦祖们可以去尝试噢)

image-20221210195633988

list和vector对于迭代器的比较

在STL中,迭代器一般有两个特点:1、内嵌类型;2、行为像指针一样

内嵌类型:1、内部类-在类里面定义的类;2、typedef出来的(typedef内置类型)

1、迭代器类型: vector的迭代器本质上是内置类型 ,但实现方式不一定是(可以用内置类型实现)—在g++底层是用内置类型实现;vs下不是内置类型实现-迭代器通过封装已经不是原生指针了。 list的迭代器底层实现是内部类

2、迭代器解引用: vector的迭代器解引用是对指针的解引用 (可以取到数据);而 list的迭代器解引用是对节点的指针解引用但这种方式取不到数据所以要用函数调用 (用的operator()函数重载)*

3、对于迭代器的判断: vector迭代器可以用>或<进行判断 —vector内存空间是连续的,连续的地址可以大小比较; list迭代器不能用>或<进行判断,只能用!=判断 —list内存空间不是连续的且是随机的

这里的实验直接或间接证明了第1点和第3点:我创建了一个vector和list,同样插入数据1234,通过对迭代器的++一个遍历打印;通过内存观察看出这里的两者迭代器的不同点

这里是vector迭代器++,++后前后地址相差为一个数据类型大小,说明vector迭代器本质是原生指针

image-20221210192829661

这里是list迭代器++,加加后前后地址相差并不是一个数据类型大小,说明vector迭代器本质不是原生指针,而且通过调试可知这是函数调用

image-20221210193007183

迭代器的优势: 1、封装底层实现,不暴露底层实现细节 2、提供了统一的访问方式,降低了使用成本

list的模拟实现

框架

我们要用命名空间围起来

namespace listrealize()
{ //节点
  template<typename T>
    struct list_node()
    {
        list_node<T>* _next;
        list_node<T>* _prev;
        T _data;
        list_node(const T& val)//节点初始化
        :_next(nullptr)
        ,_prev(nullptr)
        ,_data(val)
        {}
    };
        
        //迭代器
   template<typedef T>
     struct list_iterator
     {
         ......
         ......    
      };
        
        //链表结构
        template<typename T>
        class list
 {
    typedef list_node<T> node;
    public:
    typedef list_iterator<T> iterator;
            ......
            ......
};
   
}

节点

	template<typename T>
	struct list_node//节点初始化
	{
	
		list_node <T>* _next;
		list_node <T>* _prev;
		T _data;

		list_node(const T& val)
			:_next(nullptr)
			,_prev(nullptr)
			,_data(val)
		{}
	}

迭代器

由于list迭代器不是像vector那样的原生指针,所以得用类中类来写迭代器,而不是直接typedef出来,而且普通迭代器和const迭代器都要写

普通迭代器-普通写法


	template<typename T>
	struct list_iterator
	{

		typedef list_node<T> node;
		 node* _pnode;//成员变量---类指针

		list_iterator(node* p)//构造函数-迭代器
			:_pnode(p)
		{}

	T& operator*()//引用返回
		{
			return _pnode->_data;//返回数据
		}

		list_iterator& operator++()//返回下一个节点
		{
			_pnode = _pnode->_next;
			return *this;
		}

		list_iterator& operator--()//返回上一个节点
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		bool operator!=(const list_iterator& it )
		{
			return _pnode != it._pnode;
		}

	};

const 迭代器-普通写法

template<typename T>
	struct list_const_iterator
    {
		typedef list_node<T> node;
		node* _pnode;//成员变量---类指针

		list_const_iterator(node* p)//构造函数-迭代器
			:_pnode(p)
		{}

	const T& operator*()
		{
			return _pnode->_data;
		}

		list_const_iterator<T>& operator++()
		{
			_pnode = _pnode->_next;
			return *this;
		}

		list_const_iterator<T>& operator--()
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		bool operator!=(const list_const_iterator<T>& it)
		{
			return _pnode != it._pnode;
		}

	};

迭代器-高级写法

如果我们按照上面普通写法实现迭代器的话,相当于写了两份迭代器(普通迭代器和const迭代器),而大佬是不允许发生这种情况。大佬选择给类模板和函数模板增加参数关键字来减少代码膨胀。为什么不能直接在迭代器前加const以构造const迭代器的原因是迭代器指向的内容可读可写,而const迭代器指向的内容只能读不能写。在迭代器前面加const是指迭代器这个像指针的的东西不能改变!(指针指定的方向不能改变)

image-20221212205708443

而list这里的迭代器不是原生指针,迭代器和const迭代器其本质区别问题所在就是对数据的输出即解引用,所以我们通过给类模板和函数模板加参数就能实现。

image-20221212211244830

template<typename T,typename ref>
	struct list_iterator
	{
		typedef list_node<T> node;
		typedef list_iterator<T, ref> self;
		
		 node* _pnode;//成员变量---类指针

		list_iterator(node* p)//构造函数-迭代器
			:_pnode(p)
		{}

	ref& operator*()
		{
			return _pnode->_data;
		}

		self& operator++()//前置++
		{
			_pnode = _pnode->_next;
			return *this;
		}

		self operator++(int)//后置++
		{
			self tmp(*this);
			_pnode = _pnode->_next;
			return tmp;//返回迭代器-不需要用引用
		}

		self& operator--()//前置--
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		self operator--(int)//后置--
		{
			self tmp(*this);//用地址构造迭代器
			_pnode = _pnode->_prev;
			return tmp;
		}

		bool operator!=(const self& it )
		{
			return _pnode != it._pnode;
		}

		bool operator==(const self& it)
		{
			return _pnode == it._pnode;
		}

	};

链表结构

	template <typename T>
	class list//链表结构
	{
	
		typedef list_node<T>  node;
	public:
			typedef list_iterator<T,T&>  iterator;
		typedef list_iterator<T,const T&> const_iterator;
        
		void empty_initialize()//节点初始化
		{
			_head = new node(T());
			_head->_next = _head;
			_head->_prev = _head;
            _size=0;
		}

		list()//构造函数
		{
			empty_initialize();
		}

	template <class InputIterator>
		list(InputIterator first, InputIterator last)//迭代器区间构造
		{
			empty_initialize();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//lt1(lt)
		list( const list<T>& lt)//旧时代写法
		{//自己初始化一份
			empty_initialize();
		
			for (const auto& e : lt)//这里要用引用
			{
				push_back(e);
			}
		}
        
       list(const list<T>& lt)//拷贝构造-现代写法
			{//自己初始化一份
				empty_initialize();

				list<T> tmp(lt.begin(), lt.end());
				swap(tmp);
			}

		iterator begin()
		{
			return iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}

		const_iterator begin()const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end()const
		{
			return const_iterator(_head);
		}

        size_t size() const
		{
			return _size;
		}

		bool empty()const
		{
			return _size == 0;
		}
        
		~list()//析构
		{
			clear();
			delete[] _head;
			_head = nullptr;
		}

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

        void swap( list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}
        
		void push_back(const T& val)//传的是数据-T&
		{
			
			insert(end(), val);


		/*	node* cur = new node(val);
			node* tail = _head->_prev;
			tail->_next = cur;
			cur->_prev = tail;
			cur->_next = _head;
			_head->_prev = cur;*/
		}
		 
		void push_front(const T& val)
		{
			insert(begin(), val);
		}

		iterator insert(iterator pos, const T& val)//在pos节点前面插入
		{
			node* newnode = new node(val);
			node* cur = pos._pnode;
			node* prev = cur->_prev;
			//链接
			newnode->_next = cur;
			cur->_prev = newnode;
			newnode->_prev = prev;
			prev->_next = newnode;
			++_size;
			return iterator(newnode);
		}

		iterator erase(iterator pos)//删除节点
		{
			assert(end());

			node* cur = pos._pnode;
			node* next = cur->_next;
			node* prev = cur->_prev;
			//链接
			next->_prev = prev;
			prev->_next = next;
			delete[] cur;
            --_size;
			return iterator(next);
		}

		void pop_back()
		{
			assert(++end());
			erase(_head->_next);
		}

		void pop_front()
		{
			assert(--end());
			erase(_head->_prev);
		}

		list<T>& operator=( const list<T>&lt)//这里加const就报错
		{
			if (this != &lt)
			{
				clear();
				for (const auto& e : lt)
				{
					push_back(e);
				}
			}
			return *this;
		}

	private:
		node* _head;
	};

写到这,我们创建一个POS,类似坐标的类,成员变量有_row和 _col,那我们用POS来初始化list,该怎么访问到POS 里的 _row 和 _col呢?

	struct POS
	{
		int _row;
		int _col;

		POS(int row = 0, int col = 0)
			:_row(row)
			,_col(col)
		{}
	};

我们知道,在类外面访问类的公有成员可以用三种访问方式:访问限定符:: 、.、->

所以我们可以用->指针的方式访问,那么还需要在写一个->的迭代器运算符重载

		ptr operator->()
		{
			return &_pnode->_data;//返回节点数据的地址,用指针接收
		}

image-20221213012709356

加上之后就能访问了

下面是完善后的代码

	//节点的实现
	template<typename T>
	struct list_node//节点初始化
	{
	
		list_node <T>* _next;
		list_node <T>* _prev;
		T _data;

		list_node(const T& val)
			:_next(nullptr)
			,_prev(nullptr)
			,_data(val)
		{}
	}//迭代器的实现
	template<typename T,typename ref,typename ptr>
	struct list_iterator
	{
		typedef list_node<T> node;
		typedef list_iterator<T, ref,ptr> self;
		
		 node* _pnode;//成员变量---类指针

		list_iterator(node* p)//构造函数-迭代器
			:_pnode(p)
		{}

        		ptr operator->()
		{
			return &_pnode->_data;//返回节点数据的地址,用指针接收
		}
        
	ref& operator*()
		{
			return _pnode->_data;
		}

		self& operator++()//前置++
		{
			_pnode = _pnode->_next;
			return *this;
		}

		self operator++(int)//后置++
		{
			self tmp(*this);
			_pnode = _pnode->_next;
			return tmp;//返回迭代器-不需要用引用
		}

		self& operator--()//前置--
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		self operator--(int)//后置--
		{
			self tmp(*this);//用地址构造迭代器
			_pnode = _pnode->_prev;
			return tmp;
		}

		bool operator!=(const self& it )
		{
			return _pnode != it._pnode;
		}

		bool operator==(const self& it)
		{
			return _pnode == it._pnode;
		}

	};
//链表的实现
template <typename 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;
	
		void empty_initialize()
		{
			_head = new node(T());
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

		list()//构造函数
		{
			empty_initialize();
		}

		template <class InputIterator>
		list(InputIterator first, InputIterator last)//迭代器区间构造
		{
			empty_initialize();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
			list(const list<T>& lt)//拷贝构造-现代写法
			{//自己初始化一份
				empty_initialize();

				list<T> tmp(lt.begin(), lt.end());
				swap(tmp);
			}
		iterator begin()
		{
			return iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}

		const_iterator begin()const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end()const
		{
			return const_iterator(_head);
		}

		size_t size() const
		{
			return _size;
		}

		bool empty()const
		{
			return _size == 0;
		}

		~list()//析构
		{
			clear();
			delete[] _head;
			_head = nullptr;
			_size = 0;
		}

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

		void swap( list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

		void push_back(const T& val)//传的是数据-T&
		{
			insert(end(), val);
		}
		 
		void push_front(const T& val)
		{
			insert(begin(), val);
		}

		iterator insert(iterator pos, const T& val)//在pos节点前面插入
		{
			node* newnode = new node(val);
			node* cur = pos._pnode;
			node* prev = cur->_prev;
			//链接
			newnode->_next = cur;
			cur->_prev = newnode;
			newnode->_prev = prev;
			prev->_next = newnode;
			++_size;
			return iterator(newnode);
		}

		iterator erase(iterator pos)
		{
			assert(end());

			node* cur = pos._pnode;
			node* next = cur->_next;
			node* prev = cur->_prev;
			//链接
			next->_prev = prev;
			prev->_next = next;
			delete[] cur;
			--_size;
			return iterator(next);
		}

		void pop_back()
		{
			assert(++end());
			erase(_head->_next);
		}

		void pop_front()
		{
			assert(--end());
			erase(_head->_prev);
		}
        
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}
	private:
		node* _head;
		size_t _size;//复用-提高代码的可维护性
	};

关于节点的析构

我们知道,c++类的自定义类型会调用自定义类型的析构函数,没有析构函数则调用默认析构,而写了析构函数的类一般都要写拷贝构造和赋值运算符重载,那么编译器敢析构节点吗?又或者说编译器会析构头节点吗?

首先答案很明确是不会的!

1、在程序结束时会调用自定义类型的析构函数,而内置类型则不会-指针也不会,在这里若把这个头节点析构掉了则找不到后面的节点了

2、迭代器也不会把指针析构掉,这个指针是链表的,迭代器只是借用它指向数据;

关于迭代器没有实现拷贝构造和赋值为什么可以用?

首先迭代器没有实现析构函数,那么就不是必须实现拷贝构造和赋值运算符重载;其次,迭代器拷贝构造和赋值运算符重载都属于浅拷贝地址—指针指向同一块空间,因为迭代器不会把指向的数据析构,所以浅拷贝就够用!

好啦,关于list的实现和相关问题就分析到这里,制作不易,给个点赞支持支持吧~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85051.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

模型交易平台|金融大数据项目案例模型分享

股市评论数据情感分析 涉及关键技术&#xff1a; TF-IDF; 词嵌入; LSTM 主要工具&#xff1a;Python 技术大类&#xff1a;自然语言处理 主要业务问题&#xff1a; 随着互联网的日益发展&#xff0c;越来越多的人依赖网络搜索信息和分享交流。同时&#xff0c;股市投资者…

Java整合RabbitMQ实现生产消费(7种通讯方式)

文章目录环境说明工程搭建连接RabbitMQ通讯模式1.简单通讯2.工作队列通讯3.发布/订阅通讯4.路由通讯5.主题通讯6.RPC通讯7.Publisher确认通讯代码仓库环境说明 RabbitMQ环境&#xff0c;参考RabbitMQ环境搭建Java版本&#xff1a;JDK1.8Maven版本&#xff1a;apache-maven-3.6…

软件测试基础理论体系学习7-【一文看懂】什么是等价类划分法?边界值分析法?因果图法?错误推测法?功能图分析法?

7-【一文看懂】什么是等价类划分法&#xff1f;边界值分析法&#xff1f;因果图法&#xff1f;错误推测法&#xff1f;功能图分析法&#xff1f;1 等价类划分法1.1 理论知识1.1.1 划分等价类1.1.2 划分等价类的方法1.1.3 设计测试用例1.1.4 设计测试用例原则&#xff1a;1.2 等…

产品解读丨鸿翼ISO质量体系文件管理系统 合规 安全 高效

接轨国际&#xff0c;顺应全球标准化浪潮是当下国内制造企业发展过程的必经之路。通过建立从上而下的、符合国际各类标准的质量体系&#xff0c;鸿翼ISO质量体系文件管理系统能够严格监管企业质量体系的正常运转&#xff0c;为制造企业降本提效、重塑核心竞争力提供科学高效的解…

【JVM深层系列】「监控调优体系」针对于Alibaba-Arthas的安装入门及基础使用开发实战指南

Arthas特性介绍 Arthas是Alibaba开源的Java诊断工具&#xff0c;深受开发者喜爱。在线排查问题&#xff0c;无需重启&#xff1b;动态跟踪Java代码&#xff1b;实时监控JVM状态。 Arthas支持JDK 6&#xff0c;支持Linux/Mac/Windows&#xff0c;采用命令行交互模式&#xff0c…

网络电话会议摸鱼利器:会议自动退出器 - 网络会议结束后自动退出工具 方便会议挂机

随着远程办公和网络化的发展&#xff0c;我们可能经常需要开更多的在线电话会议。有些网络会议可能并不重要&#xff0c;但是你却不能一走了之。如果你打开了会议程序&#xff0c;然后埋头扎进Excel或笔记工具中&#xff0c;但没多一会一看&#xff0c;你竟然成了网络会议室中的…

叠氮聚乙二醇丙烯酰胺,N3-PEG-ACA,ACA-PEG-Azide

在铜催化剂催化下&#xff0c;叠氮可以与炔基在水溶液中发生click环化反应&#xff0c;生成含氮五元杂环。修饰性PEG广泛应用于修饰蛋白类药物、肽类化合物、有机小分子药物、生物材料等。 产品名称 叠氮聚乙二醇丙烯酰胺 N3-PEG-ACA 中文名称 叠氮PEG丙烯酰胺 丙烯酰胺P…

[附源码]Python计算机毕业设计SSM基于web动物园网站(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

(附源码)php 网上投票系统 毕业设计 121500

基于PHP网上投票系统 摘 要 随着全球Internet的迅猛发展和计算机应用的普及&#xff0c;特别是近几年无线网络的广阔覆盖以及无线终端设备的爆炸式增长&#xff0c;使得人们能够随时随地的访问网络&#xff0c;以获取最新信息、参与网络活动、和他人在线互动。为了能及时地了解…

SpringMVC学习:一、概念、入门

SpringMVC 1.SpringMVC简介 ​ Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web 框架&#xff0c;即使用了MVC架构模式的思想&#xff0c;将web 层进行职责解耦&#xff0c;基于请求驱动指的就是使用请求-响应模型&#xff0c;框架的目的就是…

【C语言刷题】牛客网编程入门130题--精选题目(编程初学者赶紧进来!!!)

牛客编程入门130题–精选&#xff08;一&#xff09; 前言 以下题目来自牛客网的编程入门训练题库(<—)&#xff0c;题库适合大一&#xff0c;大二学生&#xff0c;题目有难有易&#xff0c;主要偏向入门。 不过里面还是有很多不错的题目&#xff0c;节约时间&#xff0c;没…

VM系列振弦采集模块 温度传感器使用及UART 通讯参数

VM系列振弦采集模块 温度传感器使用及UART 通讯参数 VMXXX 模块支持外接温度传感器&#xff0c;通过设置寄存器 TEMP_EX 的值来选择外接温度传感器的类型&#xff0c; 通过读取寄存器 TEMP 来获取实时的温度传感器测量值&#xff0c; 温度计算参数寄存器 TEMP_PAR1和 TEMP_PAR…

十一月券商金工精选

✦ 研报目录 ✦ ✦ 简述 ✦ 按发布时间排序 国信证券 财报中的竞争对手分析能否用来预测股票收益&#xff1f; 发布日期&#xff1a;2022-11-01 关键词&#xff1a;股票、文本分析、竞争对手 主要内容&#xff1a;竞争对手提及次数被定义为一家公司在全市场所有公司的最新…

VCS学习1

1、Verilog simulation event queue&#xff08;Verilog 仿真时间队列&#xff09; 龟腚&#xff1a; VCS大概的处理Verilog代码的流程&#xff1a; 上述流程在t0之前&#xff0c;先对一些不存在延时的一些语句进行处理&#xff0c;然后到达t0时刻&#xff0c;也称为current t…

web网页制作与实现 html+css+javascript+jquery+bootstarp响应式美食网站设计与实现

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

算法竞赛入门【码蹄集进阶塔335题】(MT2001-2025)

算法竞赛入门【码蹄集进阶塔335题】(MT2001-2025&#xff09; 文章目录算法竞赛入门【码蹄集进阶塔335题】(MT2001-2025&#xff09;前言为什么突然想学算法了&#xff1f;为什么选择码蹄集作为刷题软件&#xff1f;目录1. MT2001 幸运的32. MT2002 买马3. MT2003 三角数4. MT2…

大小端、高低字节

1. 大小端 大端&#xff1a;高位字节存放在低位地址&#xff0c;低位字节存放在高位地址 小端&#xff1a;高位字节存放在高位地址&#xff0c;低位字节存放在低位地址 2. 高低位字节在理解有效位这个概念时&#xff0c;可以想象一下你的支票数额的第一位增加 1 和最后一位增…

前端 未来的路怎么走?

大家好&#xff0c;我是一名前端程序员&#xff0c;纯前端干了6年&#xff0c;在这个疫情解封的大背景下谈谈前端这条路该怎么走&#xff08;纯个人看法&#xff09; 低代码对前端的冲击 首先说一下2022年很火的低代码平台&#xff0c;网上两种观点&#xff0c;第一种人是很不屑…

【学习打卡】可解释机器学习之导论

可解释机器学习之导论 文章目录可解释机器学习之导论可解释学习为什么我们需要可解释机器学习前沿的AI方向可解释性好的机器学习算法深度学习的可解释性分析可视化卷积核遮挡Mask、缩放、平移、旋转找到能使某个神经元激活的原图像素&#xff0c;或者小图基于类激活热力图&…

Windows虚拟机安装docker

1.安装docker https://docs.docker.com/docker-for-windows/install/2.打开运行docker desktop出现错误: 解决&#xff1a;旧版 WSL 的手动安装步骤 | Microsoft Learn 根据步骤来&#xff0c;按顺序执行PowerShell命令: a)启用适用于 Linux 的 Windows 子系统 dism.exe /o…