C++:List的使用和模拟实现

✨✨✨学习的道路很枯燥,希望我们能并肩走下来!

文章目录

目录

文章目录

前言

一 list的介绍及使用

1.1 list的介绍

1.2 list的使用 

1.2.1 list的构造

1.2.2 list iterator的使用

1.2.3 list capacity 

1.2.4 list element access 

 1.2.5 list modifiers

1.2.6 list的迭代器失效 

 1.2.7 List中sort的效率测试

二、list的模拟实现

 2.1 正向迭代器的实现

2.1.1 正向迭代器的封装 

 2.1.2 迭代器的使用

2.2 list相关的成员函数

 2.2.1 构造函数

2.2.1.1 默认构造函数

 2.2.1.2 有参构造函数

 2.2.1.3 迭代器区间构造函数 

2.2.1.4 拷贝构造 

1.传统 

2.现代 

2.2.2 析构函数和clear

2.2.2.1 析构函数

2.2.2.2 clear()

 2.2.3 赋值重载

2.2.4.1 empty、size

 2.2.4.2 insert

2.2.4.3 erase 

2.2.4.4 尾插尾删头插头删 

2.3 反向迭代器的实现 

 

三 list模拟实现的全部代码


前言

本篇详细介绍了list的使用和模拟实现,让使用者了解list,而不是仅仅停留在表面,更好的模拟,为了更好的使用. 文章可能出现错误,如有请在评论区指正,让我们一起交流,共同进步!


一 list的介绍及使用

1.1 list的介绍

list文本介绍

1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代

2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。

3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。

4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。

5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list 的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

1.2 list的使用 

list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展 的能力。以下为list中一些常见的重要接口。

1.2.1 list的构造

构造函数( (constructor))接口说明
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list

1.2.2 list iterator的使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点

函数声明接口说明
begin + end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置

【注意】

1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动

2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动 

1.2.3 list capacity 

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

1.2.4 list element access 

函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

 1.2.5 list modifiers

函数声明接口说明
push_front在list首元素前插入值为val的元素
pop_front删除list中第一个元素
push_back在list尾部插入值为val的元素
pop_back删除list中最后一个元素
insert在list position 位置中插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素

list中还有一些操作,需要用到时大家可参阅list的文档说明。

1.2.6 list的迭代器失效 

前面说过,此处大家可将迭代器暂时理解成类似于指针

迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。

因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

void TestListIterator1(){int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while (it != l.end()){// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给
其赋值l.erase(it);  ++it;}}// 改正
void TestListIterator(){int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while (it != l.end()){l.erase(it++);    }}

 1.2.7 List中sort的效率测试

我们用一段代码来测试一下list中sort的性能

#include<iostream>
#include<algorithm>
#include<vector>
#include<list>
using namespace std;
void test_op()
{srand((unsigned int)time(NULL));const int N = 1000000;vector<int> v;v.reserve(N);list<int> lt1;list<int> lt2;for (int i = 0; i < N; ++i){int e = rand();lt1.push_back(e);lt2.push_back(e);}// 拷贝到vector排序,排完以后再拷贝回来int begin1 = clock();for (auto e : lt1){v.push_back(e);}sort(v.begin(), v.end());size_t i = 0;for (auto& e : lt1){e = v[i++];}int end1 = clock();//list调用自己的sortint begin2 = clock();lt2.sort();int end2 = clock();printf("vector sort:%d\n", end1 - begin1);printf("list sort:%d\n", end2 - begin2);
}

 

会发现哪怕我先拷贝到vector排完再拷贝回去效率都比list的sort效率高,所以list的sort实际中意义不是很大!! 

二、list的模拟实现

 2.1 正向迭代器的实现

2.1.1 正向迭代器的封装 

我们在学习vector的时候,发现vector的迭代器就是一个原生指针T*,这得益于vector的空间的连续性

那我们还能像vector一样用原生指针去修饰迭代器吗?

不能,链表空间上是不连续的,那我们对一个节点指针进行加减,就很难说能不能找到下一个节点,更多的是找不到的情况

我们在数据结构中访问下一个节点,往往是利用当前节点存的下一节点的地址来进行访问的

所以我们可以将迭代器单独封装成一个类去管理节点

template<class T, class Ref, class Ptr> //Ref == T& Ptr == T*
struct ListIterator //这里使用struct是因为我们要多次访问成员,也可使用class+public
{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;ListIterator(PNode pNode = nullptr) :_pNode(pNode){}ListIterator(const Self& l) :_pNode(l._pNode){}T& operator*() //解引用{return _pNode->_val;}T* operator->() {return &_pNode->_val;}Self& operator++() //前置++{_pNode = _pNode->_pNext;return *this;}Self operator++(int) //后置++{Self temp(*this);_pNode = _pNode->_pNext;return temp;}Self& operator--() //前置--{_pNode = _pNode->_pPre;return *this;}Self& operator--(int) //后置--{Self temp(*this);_pNode = _pNode->_pPre;return temp;}bool operator!=(const Self& l) //不相等判断{return _pNode != l._pNode;}bool operator==(const Self& l) //相等判断{return !(*this != l);}PNode _pNode;
};

 T* operator->()  大家可能有疑惑

 当我们的节点里存的是内置类型或者STL容器是,库里面的<<重载了这些来读取数据

但如果是我们自己写的类型(如class CH),<<并不能读取该类型的数据

因此我们要取来节点自定义类的指针,来访问该类的内部的数据(因为最底层都是内置类型

 2.1.2 迭代器的使用

 template<class T>class list{typedef ListNode<T> Node;typedef Node* PNode;public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T&> const_iterator;public:iterator begin(){return iterator(_pHead->_pNext);}iterator end(){return iterator(_pHead);}const_iterator begin()const{return const_iterator(_pHead->_pNext);}const_iterator end()const{return const_iterator(_pHead);}
private:PNode _pHead;    
}

这边我们用到了匿名对象。 

这里的const迭代器为什么不能直接用const修饰普通迭代器??

 因为typedef碰到const的话,就不是简单的字符串替换  实际上你以为的const T* ,在这里变成了T*const ,因为迭代器我们是希望他可以进行++和--的,而我们只是不希望他指向的内容给改变,所以我们的const要修饰的是指针的内容,而不是修饰指针。

2.2 list相关的成员函数

 2.2.1 构造函数

2.2.1.1 默认构造函数
list()
{_pHead = new Node;_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;
}    

因为无论如何都要有哨兵节点(方便我们不用判空,所以我们直接封装一个 

void CreateHead()
{_pHead = new Node;_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;
}
 2.2.1.2 有参构造函数
 list(int n, const T& value = T()){CreateHead();for (int i = 0; i < n; ++i)push_back(value);}
 2.2.1.3 迭代器区间构造函数 
template <class Iterator>
list(Iterator first, Iterator last)
{CreateHead();while (first != last){push_back(*first);++first;}
}
2.2.1.4 拷贝构造 
1.传统 
//拷贝构造函数传统写法
list(const list<T>& l)
{CreateHead();for (auto e : l)push_back(e);
}
2.现代 
void swap(list<T>& l)
{std::swap(_pHead, l._pHead);
}list(const list<T>& l)
{CreateHead();// 用l中的元素构造临时的temp,然后与当前对象交换list<T> temp(l.begin(), l.end());swap(temp);
}

2.2.2 析构函数和clear

2.2.2.1 析构函数
~list()
{clear();delete _pHead;_pHead = nullptr;
}
2.2.2.2 clear()
void clear()
{iterator p = begin();while (p != end()){p = erase(p);}_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;
}

 2.2.3 赋值重载

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

2.2.4 修改相关函数(Modifiers)

2.2.4.1 empty、size
size_t size()const
{size_t size = 0;ListNode* p = _pHead->_pNext;while (p != _pHead){size++;p = p->_pNext;}return size;
}
bool empty()const
{return size() == 0;
}
 2.2.4.2 insert
// 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T& val)
{PNode newnode = new Node(val);PNode cur = pos._pNode;PNode prev = cur->_pPre;newnode->_pPre = prev;newnode->_pNext = cur;prev->_pNext = newnode;cur->_pPre = newnode;return iterator(newnode);
}
2.2.4.3 erase 
// 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
{assert(pos != end());PNode prev = pos._pNode->_pPre;PNode next = pos._pNode->_pNext;prev->_pNext = next;next->_pPre = prev;delete pos._pNode;return iterator(next);//利用匿名对象返回
}
2.2.4.4 尾插尾删头插头删 
 // List Modifyvoid push_back(const T& val){insert(end(), val);}void pop_back(){erase(--end());}void push_front(const T& val){insert(begin(), val);}void pop_front(){erase(begin());}

2.3 反向迭代器的实现 

 sgi版本下的反向迭代器,其实就是将构建一个反向迭代器的类将正向迭代器封装起来,这个时候正向迭代器的++就是反向迭代器的--

namespace bit
{// 适配器 -- 复用template<class Iterator, class Ref, class Ptr>   //Ref == T&  Ptr == T*struct Reverse_iterator{typedef Reverse_iterator<Iterator, Ref, Ptr> Self;Reverse_iterator(Iterator it):_it(it){}Ref operator*(){Iterator temp = _it;--temp;return *temp;}Self& operator++(){--_it;return *this;}Self operator++(int){Iterator tmp = _it;--_it;return tmp;}Self& operator--(){++_it;return *this;}Self operator--(int){iterator temp = _it;++_it;return temp;}bool operator!=(const Self& s){return _it != s._it;}bool operator==(const Self& s){return _it == s._it;}Iterator _it;};
}

 为什么解引用的是前一个位置的元素???

 

 from C++:List的使用和模拟实现-CSDN博客 图来源

 

三 list模拟实现的全部代码

#pragma once
namespace ch
{// List的节点类template<class T>struct ListNode{ListNode(const T& val = T()) :_pPre(nullptr),_pNext(nullptr),_val(val){}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val;};//List的迭代器类template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;ListIterator(PNode pNode = nullptr) :_pNode(pNode){}ListIterator(const Self& l) :_pNode(l._pNode){}T& operator*(){return _pNode->_val;}T* operator->(){return &_pNode->_val;}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self temp(*this);_pNode = _pNode->_pNext;return temp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self& operator--(int){Self temp(*this);_pNode = _pNode->_pPre;return temp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return !(*this != l);}PNode _pNode;};template<class Iterator, class Ref, class Ptr>   //Ref == T&  Ptr == T*struct Reverse_iterator{typedef Reverse_iterator<Iterator, Ref, Ptr> Self;Reverse_iterator(Iterator it):_it(it){}Ref operator*(){Iterator temp = _it;--temp;return *temp;}Self& operator++(){--_it;return *this;}Self operator++(int){Iterator tmp = _it;--_it;return tmp;}Self& operator--(){++_it;return *this;}Self operator--(int){iterator temp = _it;++_it;return temp;}bool operator!=(const Self& s){return _it != s._it;}bool operator==(const Self& s){return _it == s._it;}Iterator _it;};//list类template<class T>class list{typedef ListNode<T> Node;typedef Node* PNode;public://正向迭代器typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T&> const_iterator;//反向迭代器typedef Reverse_iterator<iterator, T&, T*>  reverse_iterator;typedef Reverse_iterator<iterator, const T&, const T*>  const_reverse_iterator;public:///// List的构造list(){CreateHead();}list(int n, const T& value = T()){CreateHead();for (int i = 0; i < n; ++i)push_back(value);}template <class Iterator>list(Iterator first, Iterator last){CreateHead();while (first != last){push_back(*first);++first;}}list(const list<T>& l){CreateHead();// 用l中的元素构造临时的temp,然后与当前对象交换list<T> temp(l.begin(), l.end());swap(temp);}list<T>& operator=(const list<T> l){swap(l);return *this;}~list(){clear();delete _pHead;_pHead = nullptr;}///// List Iteratoriterator begin(){return iterator(_pHead->_pNext);}iterator end(){return iterator(_pHead);}const_iterator begin()const{return const_iterator(_pHead->_pNext);}const_iterator end()const{return const_iterator(_pHead);}//反向迭代器(可读可写)reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}//反向迭代器(可读不可写)const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}///// List Capacitysize_t size()const{size_t size = 0;ListNode* p = _pHead->_pNext;while (p != _pHead){size++;p = p->_pNext;}return size;}bool empty()const{return size() == 0;}// List AccessT& front(){assert(!empty());return _pHead->_pNext->_val;}const T& front()const{assert(!empty());return _pHead->_pNext->_val;}T& back(){assert(!empty());return _pHead->_pPre->_val;}const T& back()const{assert(!empty());return _pHead->_pPre->_val;}// List Modifyvoid push_back(const T& val){insert(end(), val);}void pop_back(){erase(--end());}void push_front(const T& val){insert(begin(), val);}void pop_front(){erase(begin());}// 在pos位置前插入值为val的节点iterator insert(iterator pos, const T& val){PNode newnode = new Node(val);PNode cur = pos._pNode;PNode prev = cur->_pPre;newnode->_pPre = prev;newnode->_pNext = cur;prev->_pNext = newnode;cur->_pPre = newnode;return iterator(newnode);}// 删除pos位置的节点,返回该节点的下一个位置iterator erase(iterator pos){assert(pos != end());PNode prev = pos._pNode->_pPre;PNode next = pos._pNode->_pNext;prev->_pNext = next;next->_pPre = prev;delete pos._pNode;return iterator(next);//利用匿名对象返回}void clear(){iterator p = begin();while (p != end()){p = erase(p);}_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;}void swap(list<T>& l){std::swap(_pHead, l._pHead);}private:void CreateHead(){_pHead = new Node;_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;}PNode _pHead;};
}

总结

✨✨✨各位读友,本篇分享到内容是否更好的让你理解了C++的list,如果对你有帮助给个👍赞鼓励一下吧!!
🎉🎉🎉世上没有绝望的处境,只有对处境绝望的人。
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!。

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

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

相关文章

golang+redis的延时队列

网址 https://github.com/cfanbo/delay-queue-redis 代码结构很简单&#xff0c;简单代表着自由度很高&#xff0c;使用过程中出现问题也很好修改。 我很喜欢这样的代码&#xff0c;至少我看的懂&#xff0c;该有的都有。 //package main // //import ( // "context&q…

leetcode209_长度最小的子数组

要求某个连续的区间内的元素值总和>S . 思路&#xff1a;滑动窗口&#xff1a;本质上是一种双指针法。 &#xff08;1&#xff09;初始化left right 0&#xff1b; &#xff08;2&#xff09;left不动&#xff0c;right移动&#xff0c;扩大窗口&#xff0c;直至符合要…

selinux的安全策略可以影响ntp的方式

SELinux 是一个灵活而强大的模块化安全策略框架&#xff0c;它允许管理员定义和执行非常具体的访问控制策略。这些策略可以限制程序和进程对系统资源的访问&#xff0c;包括文件、网络端口、进程间通信等。 对于NTP&#xff0c;SELinux 策略可以影响以下几个方面&#xff1a; …

网络空间安全数学基础·整除与同余

主要内容&#xff1a; 整除的基本概念&#xff08;掌握&#xff09; 素数&#xff08;掌握&#xff09; 同余的概念&#xff08;掌握&#xff09; 1.1整除 定义&#xff1a;设a&#xff0c;b是任意两个整数&#xff0c;其中b≠0&#xff0c;如果存在一个整数q&#xff0c;使 …

12306技术内幕

公司内部做的一次技术分享 文章目录 12306的成就12306系统特点12306系统难点解决思路产品角度技术角度余票库存的表如何设计&#xff1f; 抢票软件推荐巨人的肩膀 对于未公开的技术部分&#xff0c;只能结合已公开的信息&#xff0c;去做大胆的猜想。 本文提到的一些解决方案&…

SpringBoot + Mybatis-Plus中乐观锁实现

悲观锁 悲观锁是一种悲观思想&#xff0c;它认为数据很可能会被别人所修改 所以总会对数据进行上锁&#xff0c;读操作和写操作都会上锁&#xff0c;性能较低&#xff0c;使用较少&#xff01; 乐观锁 乐观锁是一种乐观思想&#xff0c;它认为数据并不一定会被别人所修改 所以…

成为程序员后我都明白了什么?从入行到弃坑?

作为一个入行近10年的php程序员&#xff0c;真心感觉一切都才刚开始&#xff0c;对计算机&#xff0c;编程语言的理解也好&#xff0c;程序员中年危机也罢&#xff0c;之前都是听别人说的&#xff0c;真的自己到了这个水平&#xff0c;这个年龄才深刻体会到这其中的种种。 我一…

测试基础05:软件测试的分类

课程大纲 1、两种架构&#xff08;Architecture&#xff09; 1.1、B/S&#xff08;Browser/Server&#xff09; 浏览器服务器架构&#xff08;大体3步&#xff09;&#xff1a;用户通过浏览器向服务器发出请求&#xff0c;服务器处理请求&#xff0c;将结果通过网络返回到用户…

使用Webcam实现摄像头的开启和关闭,并保存和复制图片

实现思路 0&#xff0c;将webcam的jar文件传入项目中 1&#xff0c;显示摄像头的地方&#xff1a;创建一个画板&#xff0c;在画板上添加开启和关闭按钮 2&#xff0c;设置开启和关闭功能&#xff1a;创建一个类实现动作监听器&#xff0c;进而实现监听动作按钮 3&#xff…

【数据结构与算法篇】二叉树链式结构及实现

【数据结构与算法篇】二叉树链式结构及实现 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 4. 二叉树链式结构的实现 4.1 前置说明 4.2 二叉树的遍历 4.2.1 前序、中序以及…

通过ssh在本地打开远程服务器的网页

用途 在远程服务器使用jupyter notebook或者tensorboard等时&#xff0c;在本地打开服务器端的网页的方式有很多比如可以使用MobaXterm工具等&#xff0c;此方法可参考https://blog.csdn.net/cc__cc__/article/details/108060618?spm1001.2014.3001.5502。 若直接使用ssh则可…

C++感受11-Hello Object 成员版

当一个C程序员在设计类型时&#xff0c;他在想什么&#xff1f; 这一类型的对象&#xff0c;需要拥有哪些属性数据&#xff1f;这一类型的对象&#xff0c;它将拥有哪些功能&#xff1f;这一类型的对象&#xff0c;它的各个属性和功能之间&#xff0c;有哪些关联关系&#xff1…

OceanBase的存储架构与传统LSM-Tree架构的异同|OceanBase数据转储合并技术解读(二)

前篇博文将OceanBase的存储架构巧妙地与自然界中的“水生态”进行了类比&#xff0c;今日我们转变视角&#xff0c;聚焦在与拥有相同LSM-Tree架构的其他产品的比较&#xff0c;深入探讨OceanBase相较于它们所展现出的独特性能。 众所周知&#xff0c;OceanBase数据库的存储引擎…

element-ui 前端ui框架用法开发指南(2024-05-22)

Element&#xff0c;一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库 1、npm安装 // npm安装&#xff1a;npm install element-ui --save 能更好地和 webpack 打包工具配合使用 2、cdn在线引入 访问最新版本的资源地址 - element-uiThe CDN for element-u…

RedHat9 | DNS剖析-配置主DNS服务器实例

一、实验环境 1、BIND软件包介绍 BIND软件是一款开放源码的DNS服务器软件&#xff0c;由美国加州大学Berkeley分校开发和维护&#xff0c;全称为Berkeley Internet Name Domain。该软件在DNS&#xff08;域名系统&#xff09;领域具有重要地位&#xff0c;是目前世界上使用最…

使用OpenCV dnn c++加载YOLOv8生成的onnx文件进行目标检测

在网上下载了60多幅包含西瓜和冬瓜的图像组成melon数据集&#xff0c;使用 LabelMe 工具进行标注&#xff0c;然后使用 labelme2yolov8 脚本将json文件转换成YOLOv8支持的.txt文件&#xff0c;并自动生成YOLOv8支持的目录结构&#xff0c;包括melon.yaml文件&#xff0c;其内容…

信息系统管理工程师问答题

信息系统管理工程师问答题 系统管理安全两方面 安全测试 入侵检测系统的功能 用户标识与验证常用的3种方法 (1) 要求用户输入一些保密信息&#xff0c;例如用户名称和密码&#xff1b; (2) 采用物理识别设备&#xff0c;例如访问卡、钥匙或令牌&#xff1b; (3) 采用生物统计学…

Python怎样定位并删除Sql语句中不确定的查询条件

1.问题场景描述: 在sql语句中经常会有查询条件是:查找多个订单签订日期范围的数据,但具体的日期范围是不确定,我们如何来查找定位 例如:查询条件语句的部分如下图: 目标是: 1)定位字符串:t_contract_order.sign_date 2)最终得到结果: 解决问题思路: 1)定位要找的字符串起始位置…

【学习心得】PyTorch的知识要点复习(持续更新)

PyTorch知识要点复习&#xff0c;目的是为了巩固PyTorch基础、快速回顾、深化理解PyTorch框架。这篇文章会持续更新。 一、本文的一些说明 知识点梳理&#xff1a;我将PyTorch的核心概念和高级技巧进行了系统化的整理&#xff0c;从基础的张量操作到复杂的模型构建与训练。这样…

【Linux】进程终止与进程等待

目录 进程终止 errno exit和_exit 进程等待 wait和waitpid 宏&#xff1a;WIFEXITED 非阻塞等待 进程终止 下面要谈的一个话题就是进程终止&#xff0c;就是说一个进程退出了&#xff0c;可能有三种情况 1.进程代码执行完&#xff0c;结果是正确的 2.进程代码执行完&…