SLT—List详解

news2024/9/20 7:01:01

1.list概述

相较于 vector 的连续线性空间,list 就显得复杂很多,它的好处是每次插入或删除一个数据,就配置或释放一个元素空间。因此,list 对于空间的运用有绝对的精准,一点也不浪费。对于任何位置的元素进行插入或者元素移除,list 永远是常数时间(O(N))。

list 和 vector 是两个最常被使用的容器,什么时机下最适合使用哪一种容器,必须视元素的多寡、元素的构造复杂度、 元素存取行为的特征而定。

下图是 list 示意图 : 环状链表的尾端加上一个空白节点,符合“前闭后开”区间。

2.list的使用

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

(1) list的构造

void TestList1()
{
    list<int> l1;                         // 构造空的l1
    list<int> l2(4, 100);                 // l2中放4个值为100的元素
    list<int> l3(l2.begin(), l2.end());  // 用l2的[begin(), end())左闭右开的区间构造l3
    list<int> l4(l3);                    // 用l3拷贝构造l4

    // 以数组为迭代器区间构造l5
    int array[] = { 16,2,77,29 };
    list<int> l5(array, array + sizeof(array) / sizeof(int));

    // 列表格式初始化C++11
    list<int> l6{ 1,2,3,4,5 };

    // 用迭代器方式打印l5中的元素
    list<int>::iterator it = l5.begin();
    while (it != l5.end())
    {
        cout << *it << " ";
        ++it;
    }       
    cout << endl;

    // C++11范围for的方式遍历
    for (auto& e : l5)
        cout << e << " ";

    cout << endl;
}

(2) list iterator

暂时将迭代器理解成一个指针,该指针指向 list 中的某个节点。

 

【注意】
1. begin 与 end 为正向迭代器,对迭代器执行++操作,迭代器向后移动
2. rbegin(end) 与 rend(begin) 为反向迭代器,对迭代器执行++操作,迭代器向前移动
// list迭代器的使用
// 注意:遍历链表只能用迭代器和范围for
void PrintList(const list<int>& l)
{
    // 注意这里调用的是list的 begin() const,返回list的const_iterator对象
    for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it)
    {
        cout << *it << " ";
        // *it = 10; 编译不通过
    }

    cout << endl;
}

void TestList2()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> l(array, array + sizeof(array) / sizeof(array[0]));
    // 使用正向迭代器正向list中的元素
    // list<int>::iterator it = l.begin();   // C++98中语法
    auto it = l.begin();                     // C++11之后推荐写法
    while (it != l.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;

    // 使用反向迭代器逆向打印list中的元素
    // list<int>::reverse_iterator rit = l.rbegin();
    auto rit = l.rbegin();
    while (rit != l.rend())
    {
        cout << *rit << " ";
        ++rit;
    }
    cout << endl;
}

(3) list capacity

(4) list element access(元素访问)

(5) list modifiers(修改器)

#include <list>
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
	list<int> ilist; 
	cout << "size=" << ilist.size() << endl;// size=0
	ilist.push_back(0); 
	ilist.push_back(1); 
	ilist.push_back(2); 
	ilist.push_back(3); 
	ilist.push_back(4); 
	cout << "size=" << ilist.size() << endl;//size=5

	list<int>::iterator ite; 
	for (ite = ilist.begin(); ite != ilist.end(); ++ite)
	{
		cout << *ite << ' ';//0 1 2 3 4
	}
	cout << endl;


	ite = find(ilist.begin(), ilist.end(), 3);
	if (ite != ilist.end())
	{
		ilist.insert(ite, 99);
	}
	cout << "size=" << ilist.size() << endl;// size=6
	cout << *ite << endl;// 3
	
	for (ite = ilist.begin(); ite != ilist.end(); ++ite)//0 1 2 99 3 4
		cout << *ite << ' ';
	cout << endl;
	ite = find(ilist.begin(), ilist.end(), 1); 
	if (ite != ilist.end())
		cout << *(ilist.erase(ite)) << endl;//2

	for (ite = ilist.begin(); ite != ilist.end(); ++ite)//0 2 99 3 4
		cout << *ite << ' ';
	cout << endl;
}

(6) list迭代器失效

前面说过,此处可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在 list 中进行插入时是不会导致 list 的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响这在 vector 是不成立的,因为 vector 的常茹操作可能造成记忆体重新配置,导致原有的迭代器全部失效。
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++); // it = l.erase(it);
	}
}

3.list的模拟实现

(1) list的节点(ListNode)

list 本身和 list 的节点是不同的结构,需要分开设计,以下是根据 STL 设计的 list 的节点(ListNode)结构:

template <class T>
struct ListNode // struct默认成员是公有的
{
	ListNode(const T& val = T())
		:_prev(nullptr)
		,_next(nullptr)
		,_data(val)
	{}

	ListNode<T>* _prev;
	ListNode<T>* _next;
	T _data;
};

(2) list的迭代器(ListIterator)

list 不能再像 vector 一样以普通指针作为迭代器,因为其节点不保证在存储空间里连续存在。list迭代器必须有能力指向list节点,并有能力进行正确的递增、递减、取值、成员存取等操作。所谓“list 正确的递增、递减、取值、成员存取”操作是指,递增时指向下一个节点,递减时指向下一个节点,取值时取的是节点的数据值,成员取用时取的是节点的成员

由于STL list是一个双向链表,迭代器必须具备迁移,后移的能力,所以 list 提供的是 Birdirectional Iterator(双向)

List 的迭代器

迭代器有两种实现方式,具体应根据容器底层数据结构实现:

1. 原生态指针,比如:vector

2. 将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法:

(1)指针可以解引用,迭代器的类中必须重载 operator*()

(2)指针可以通过->访问其所指空间成员,迭代器类中必须重载 oprator->()

(3)指针可以++向后移动,迭代器类中必须重载 operator++() 与 operator++(int)

至于 operator--() / operator--(int) 释放需要重载,根据具体的结构来抉择,双向链表可以向前 移动,所以需要重载,如果是forward_list(单向)就不需要重载--

(4)迭代器需要进行是否相等的比较,因此还需要重载 operator==() 与 operator!=()

//List的迭代器类模版,实例化了两个类
template<class T, class Ref, class Ptr>
class ListIterator
{
    typedef ListNode<T>* PNode;
    typedef ListIterator<T, Ref, Ptr> Self;

public:
    ListIterator(PNode pNode = nullptr)
        :_pNode(pNode)
    {}

    ListIterator(const Self& l)
        :_pNode(l._pNode)
    {}

    T& operator*()
    {//取得是节点的数值
        return (*_pNode)._data;
    }
    T* operator->()
    {
        return &(operator*());
    }

    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& l)
    {
        return !(_pNode == l._pNode);
    }
    bool operator==(const Self& l)
    {
        return _pNode == l._pNode;
    }

    PNode _pNode;//迭代器的内部要有一个普通指针,指向list的节点
};

(3) list的数据结构

STL list 不仅是一个双向链表,而且还是一个环状双向链表。所以它只需要一个指针,就可以完整表现整个链表。

//list类
template<class T>
class list // class 默认是私有的
{
    typedef ListNode<T> Node;
public:
    typedef Node* PNode;
    
    //实现...

private:
    PNode _pHead;//只需要一个指针,完成整个双向环状链表
    size_t _size;//STL中没有,自己实现为了方便加的

};

(4) list的构造与内存管理 

如果将指针刻意置于尾端的一个空白节点,_pHead便能符合STL对于“前闭后开”去区间的要求,成为 list 迭代器。

    // List的构造
    list()
    {
        CreateHead();//产生一个空链表(一个指向自己的空节点)
    }
    list(int n, const T& value = T())
    {
        CreateHead();
        while (n--)
        {
            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();
        list<T> tmp(l.begin(), l.end());
        swap(tmp);
    }
    list<T>& operator=(list<T> l)
    {
        swap(l);
        return *this;
    }
    ~list()
    {
        clear();
        delete[] _pHead;
        _pHead = nullptr;
    }

private:
    void CreateHead()
    {
        _pHead = new Node[1];//配置一个节点空间,头尾指向自己
        _pHead->_next = _pHead;
        _pHead->_prev = _pHead;
    }

当我们以 push_back( )将新元素插入于list尾端时,此时函数内部调用 insert( ) :

void push_back(const T& val){ insert(end(), val);}

insert( ) 是一个重载函数,有多种形式,其中最简单的一种如下,首先配置并构造一个节点,然后在尾端进行适当的指针操作,将新节点插入进去:

// 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T& val)
{
    PNode tmp = new Node[1];
    tmp->_data = val;
    PNode pcur = pos._pNode->_prev;
    pcur->_next = tmp;
    tmp->_prev = pcur;
    pos._pNode->_prev = tmp;
    tmp->_next = pos._pNode;
    ++_size;
    return tmp;
}

插入完成之后,新节点将位于插入点所指向之节点的前方—这是STL对于“插入操作”的标准规范。

(5) list的元素操作

list 提供的元素操作有很多,这里只实现部分操作。

// List Modify
void 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 tmp = new Node[1];
    tmp->_data = val;
    PNode pcur = pos._pNode->_prev;
    pcur->_next = tmp;
    tmp->_prev = pcur;
    pos._pNode->_prev = tmp;
    tmp->_next = pos._pNode;
    ++_size;
    return tmp;
}
// 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
{
    PNode pcur = pos._pNode->_prev;
    PNode pnext = pos._pNode->_next;
    pcur->_next = pnext;
    pnext->_prev = pcur;
    delete[] pos._pNode;
    --_size;
    return pnext;
}
//清除整个链表
void clear()
{
    auto cur = begin();
    while (cur != end())
    {
        cur = erase(cur);
    }
}

(6) 模拟实现.h

#include<iostream>
#include<list>
#include<algorithm>
#include<assert.h>
using namespace std;

namespace zyt
{
    //List的节点类
    template <class T>
    struct ListNode // struct默认成员是公有的
    {
        ListNode(const T& val = T())
            :_prev(nullptr)
            , _next(nullptr)
            , _data(val)
        {}

        ListNode<T>* _prev;
        ListNode<T>* _next;
        T _data;
    };

    //List的迭代器类模版,实例化了两个类
    template<class T, class Ref, class Ptr>
    class ListIterator
    {
        typedef ListNode<T>* PNode;
        typedef ListIterator<T, Ref, Ptr> Self;

    public:
        ListIterator(PNode pNode = nullptr)
            :_pNode(pNode)
        {}

        ListIterator(const Self& l)
            :_pNode(l._pNode)
        {}

        T& operator*()
        {
            return (*_pNode)._data;
        }
        T* operator->()
        {
            return &(operator*());
        }

        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& l)
        {
            return !(_pNode == l._pNode);
        }
        bool operator==(const Self& l)
        {
            return _pNode == l._pNode;
        }

        PNode _pNode;
    };


    //list类
    template<class T>
    class list // class 默认是私有的
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T&> const_iterator;
    public:
        ///
        // List的构造
        list()
        {
            CreateHead();//产生一个空链表(一个指向自己的空节点)
        }
        list(int n, const T& value = T())
        {
            CreateHead();
            while (n--)
            {
                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();
            list<T> tmp(l.begin(), l.end());
            swap(tmp);
        }
        list<T>& operator=(list<T> l)
        {
            swap(l);
            return *this;
        }
        ~list()
        {
            clear();
            delete[] _pHead;
            _pHead = nullptr;
        }


        ///
        // List Iterator
        iterator begin()
        {
            return ((*_pHead)._next);
        }
        iterator end()
        {
            return _pHead;
        }
        const_iterator begin() const
        {
            return ((*_pHead)._next);
        }
        const_iterator end() const
        {
            return _pHead;

        }

        ///
        // List Capacity
        size_t size()const
        {
            return _size;
        }
        bool empty()const
        {
            return _pHead->_next == _pHead;
        }

        
        // List Access
        T& front()
        {
            return *begin();
        }
        const T& front()const
        {
            return *begin();
        }
        T& back()
        {
            return *end();
        }
        const T& back()const
        {
            return *end();
        }

        
        // List Modify
        void 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 tmp = new Node[1];
            tmp->_data = val;
            PNode pcur = pos._pNode->_prev;
            pcur->_next = tmp;
            tmp->_prev = pcur;
            pos._pNode->_prev = tmp;
            tmp->_next = pos._pNode;
            ++_size;
            return tmp;
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            PNode pcur = pos._pNode->_prev;
            PNode pnext = pos._pNode->_next;
            pcur->_next = pnext;
            pnext->_prev = pcur;
            delete[] pos._pNode;
            --_size;
            return pnext;
        }
        //清除整个链表
        void clear()
        {
            auto cur = begin();
            while (cur != end())
            {
                cur = erase(cur);
            }
        }
        void swap(list<T>& l)
        {
            std::swap(_pHead, l._pHead);
            std::swap(_size, l._size);
        }
    private:
        void CreateHead()
        {
            _pHead = new Node[1];//配置一个节点空间,头尾指向自己
            _pHead->_next = _pHead;
            _pHead->_prev = _pHead;
        }
    private:
        PNode _pHead;
        size_t _size;

    };
}

(7) 测试.cpp

// 打印各类容器(类)
template<class T>
void PrintList(const zyt::list<T>& l)
{
    auto it = l.begin();
    while (it != l.end())
    {
        cout << *it << " ";
        ++it;
    }

    cout << endl;
}

void TestList3()
{
    int array[] = { 1, 2, 3, 4, 5 };
    //zyt::list<int> l(array, array + sizeof(array) / sizeof(array[0]));
    zyt::list<int> l;
    l.push_back(1);
    l.push_back(2);
    l.push_back(3);
    l.push_back(4);
    l.push_back(5);
    PrintList(l);

    auto pos = l.begin();
    l.insert(l.begin(), 0);
    PrintList(l);

    ++pos;
    l.insert(pos, 2);
    PrintList(l);

    l.erase(l.begin());
    l.erase(pos);
    PrintList(l);

    // pos指向的节点已经被删除,pos迭代器失效
    cout << *pos << endl;

    auto it = l.begin();
    while (it != l.end())
    {
        it = l.erase(it);
    }
    cout << l.size() << endl;
}


// PushBack()/PopBack()/PushFront()/PopFront()
void TestList2()
{
    // 测试PushBack与PopBack
    zyt::list<int> l;
    l.push_back(1);
    l.push_back(2);
    l.push_back(3);
    PrintList(l);

    l.pop_back();
    l.pop_back();
    PrintList(l);

    l.pop_back();
    cout << l.size() << endl;

    // 测试PushFront与PopFront
    l.push_front(1);
    l.push_front(2);
    l.push_front(3);
    PrintList(l);

    l.pop_front();
    l.pop_front();
    PrintList(l);

    l.pop_front();
    cout << l.size() << endl;
}

// 测试List的构造
void TestList1()
{
    zyt::list<int> l1;
    zyt::list<int> l2(10, 5);
    PrintList(l2);

    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    zyt::list<int> l3(array, array + sizeof(array) / sizeof(array[0]));
    PrintList(l3);

    zyt::list<int> l4(l3);
    PrintList(l4);

    l1 = l4;
    PrintList(l1);
}
int main()
{
    TestList1();
    return 0;
}

4.list与vector的区别 

vector 与 list 都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

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

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

相关文章

连续信号的matlab表示

复习信号与系统以及matlab 在matlab中连续信号使用较小的采样间隔来表四 1.单位阶跃信号 阶跃信号:一个理想的单位阶跃信号在时间 t 0 之前值为0&#xff0c;在 t 0 及之后值突然变为常数 A&#xff08;通常取 A 1&#xff09; %matlab表示连续信号,是让信号的采样间隔很小…

WebGIS面试题(第九期)

坐标系&#xff1a; 文章目录 **坐标系&#xff1a;**1、如何使用ArcGIS进行GIS坐标系转换&#xff1f;2、Cesium中的Cartesian3坐标系的原点在哪里&#xff1f;它的轴是如何定义的&#xff1f;3、如何在Cesium中使用矩阵进行坐标系转换。4、在Cesium中&#xff0c;如何将屏幕坐…

在VScode上写网页(html)

一、首先点进VScode&#xff0c;下载3个插件。 VScode安装&#xff1a;VScode 教程 | 菜鸟教程 二、新建 HTML 文件 作者运行的代码来自&#xff1a;http://t.csdnimg.cn/vIAQi 把代码复制粘贴进去&#xff0c;然后点击文件→另存为→选择html格式。 三、运行代码

笔试强训,[NOIP2002普及组]过河卒牛客.游游的水果大礼包牛客.买卖股票的最好时机(二)二叉树非递归前序遍历

目录 [NOIP2002普及组]过河卒 牛客.游游的水果大礼包 牛客.买卖股票的最好时机&#xff08;二) 二叉树非递归前序遍历 [NOIP2002普及组]过河卒 题里面给的提示很有用&#xff0c;那个马的关系&#xff0c;后面就注意&#xff0c;dp需要作为long的类型。 import java.util.Sc…

店匠科技携手Stripe共谋电商支付新篇章

在全球电商行业蓬勃发展的背景下,支付环节作为交易闭环的核心,其重要性日益凸显。随着消费者对支付体验要求的不断提高,以及跨境电商的迅猛发展,支付市场正经历着前所未有的变革与挑战。在这一充满机遇与竞争的领域,店匠科技(Shoplazza)凭借其创新的嵌入式支付解决方案—— Sho…

[米联客-XILINX-H3_CZ08_7100] FPGA程序设计基础实验连载-39 HDMI视频输入测试

软件版本&#xff1a;VIVADO2021.1 操作系统&#xff1a;WIN10 64bit 硬件平台&#xff1a;适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA 实验平台&#xff1a;米联客-MLK-H3-CZ08-7100开发板 板卡获取平台&#xff1a;https://milianke.tmall.com/ 登录“米联客”FPGA社区 http…

软考(计算机技术与软件专业技术资格(水平)考试)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

《JavaEE进阶》----11.<SpringIOCDI【Spring容器+IOC详解+DI介绍】>

本篇博客会详细讲解什么是Spring。 SpringIOC SpringID 五个类注解&#xff1a;Controller、Service、Repository、Component、Configuration 一个方法注解&#xff1a;Bean 什么是Spring IOC容器 Spring 是包含众多工具的IOC容器。能装东西的容器。 1.容器 如我们之前学的 Tom…

高效传输秘籍,揭秘Rsync和SCP的优劣,助你做出明智选择!

在日常的运维工作中&#xff0c;文件传输任务频繁出现&#xff0c;而选择合适的工具能显著提高工作效率。Rsync 和 SCP 是两款常见的文件传输工具&#xff0c;但它们各具优缺点&#xff0c;适合不同的场景。本文将通过深入分析这两款工具的特性、使用场景和性能&#xff0c;帮助…

Django+Vue3前后端分离学习(一)(项目开始时settings.py里的设置)

一、创建django项目 二、修改settings.py里的配置&#xff1a; 1、修改语言和时区&#xff1a; # 语言编码 LANGUAGE_CODE zh-hansTIME_ZONE UTCUSE_I18N True# 不用时区 USE_TZ False 2、配置数据库&#xff1a; DATABASES {default: {ENGINE: django.db.backends.m…

如何启动网络安全计划:首先要做的事情

目录 数据分类&#xff1a;网络安全的基石 为什么它很重要&#xff1f; 如何对数据进行分类&#xff1f; 风险分析 什么是风险分析&#xff1f; 如何进行风险分析&#xff1f; 业务影响分析 (BIA) BIA 的用途是什么&#xff1f; BIA 是如何进行的&#xff1f; 安全解…

文法—语法推导树例题

答案:C 解析&#xff1a; S推出A0&#xff0c;A0推出1&#xff0c;可以推出字符串01 S推出B1&#xff0c;B1推出0&#xff0c;可以推出字符串10 选项A&#xff0c;所有由0构成的字符串&#xff0c;不正确 选项B&#xff0c;所有由1构成的字符串。不正确 选项C&#xff0c…

探索Invoke:Python自动化任务的瑞士军刀

文章目录 探索Invoke&#xff1a;Python自动化任务的瑞士军刀背景&#xff1a;为何选择Invoke&#xff1f;invoke是什么&#xff1f;如何安装invoke&#xff1f;简单的invoke库函数使用方法场景应用&#xff1a;invoke在实际项目中的使用场景一&#xff1a;自动化测试场景二&am…

DAQmx Connect Terminals 路由的实际应用与注意事项

DAQmx Connect Terminals VI 是 NI 数据采集 (DAQ) 系统中的关键功能&#xff0c;用于在物理或虚拟终端之间建立信号路由。它允许用户自定义信号的硬件连接路径&#xff0c;以满足复杂的测试和测量需求。本文将详细介绍如何使用该功能&#xff0c;实际应用中的例子&#xff0c;…

UE5引擎工具链知识点

当我们提到“引擎工具链的开发”时&#xff0c;通常指的是为游戏开发或其他类型的软件开发创建一系列工具和技术栈的过程。这包括但不限于游戏引擎本身&#xff08;如Unity或Unreal Engine&#xff09;&#xff0c;以及围绕这些引擎构建的各种工具和服务&#xff0c;比如用于构…

基于SpringBoot+Vue+MySQL的宿舍维修管理系统

系统展示 前台界面 管理员界面 维修员界面 学生界面 系统背景 在当今高校后勤管理的日益精细化与智能化背景下&#xff0c;宿舍维修管理系统作为提升校园生活品质、优化资源配置的关键环节&#xff0c;其重要性日益凸显。随着学生规模的扩大及住宿条件的不断提升&#xff0c;宿…

Redis 篇-深入了解基于 Redis 实现分布式锁(解决多线程安全问题、锁误删问题和确保锁的原子性问题)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 分布式锁概述 1.1 Redis 分布式锁实现思路 1.2 实现基本的分布式锁 2.0 Redis 分布式锁误删问题 2.1 解决 Redis 分布式锁误删问题 3.0 Redis 分布式锁原子性问题…

高级法医视频分析技术 2024

高级法医视频分析技术 2024 如今&#xff0c;法医视频分析是数字取证的重要组成部分 &#xff0c;因为它可以帮助特工了解视频证据很重要的案件的重要信息。到 2024 年&#xff0c;该领域使用的工具和方法将以前所未有的速度发生变化。在这个领域工作的人需要了解这些变化。在本…

ctfshow-nodejs

什么是nodejs Node.js 是一个基于 Chrome V8 引擎的 Javascript 运行环境。可以说nodejs是一个运行环境&#xff0c;或者说是一个 JS 语言解释器 Nodejs 是基于 Chrome 的 V8 引擎开发的一个 C 程序&#xff0c;目的是提供一个 JS 的运行环境。最早 Nodejs 主要是安装在服务器…

线性代数 第六讲 特征值和特征向量_相似对角化_实对称矩阵_重点题型总结详细解析

文章目录 1.特征值和特征向量1.1 特征值和特征向量的定义1.2 特征值和特征向量的求法1.3 特征值特征向量的主要结论 2.相似2.1 相似的定义2.2 相似的性质2.3 相似的结论 3.相似对角化4.实对称矩阵4.1 实对称矩阵的基本性质4.2 施密特正交化 5.重难点题型总结5.1 判断矩阵能否相…