C++数据结构:Python风格双向链表Pylist的实现

news2025/1/18 8:49:36

文章目录

  • 前言
  • 一、目的
  • 二、代码详解
    • 1、Node类模板
    • 2、Pylist类构造
    • 3、内嵌迭代器
    • 4、Python风格insert方法
    • 5、Python风格append方法
    • 6、Python风格[]下标操作方法
    • 7、Python风格+、+= 方法
    • 8、Python风格pop方法
    • 9、Python风格remove方法
    • 10、length、get方法
  • 三、使用示例
  • 总结
    • 原创文章,未经许可、严禁转载


前言

前文:线性顺序表(链表) 已经完成了单向链表基本的插入、取值、删除等操作,本文将在前文的基础上更进一步完善其功能。参照Python的 List 列表的函数常用功能来实现。如+运算直接将两个列表合并,[] 运算赋值、取值。如:

a = [1, 2], b = [4, 5]
a[2] = 3
a += b
c = a + b

笔者是很喜欢python中列表这种骚操作的,本文就在C++中实现它。当然基本的C++语法规则笔者是没法改变的。赋值得用{},类型还是要声明的,泛型自然是可以的,但你也别想着在一个列表中同时塞入 string 和 int 。最终实现效果部分如下图:
在这里插入图片描述


一、目的

用 C++ 实现一个类似于 Python 中的 List 列表的链表,实现Python 中 list 的常用的几个功能,可以进一步加深对链表的理解。

  • 使用双向链表实现,用以加快首尾访问速度。
  • initializer_list 实现类似C++ STL中各种容器的初始化方法,了解不定数量参数的初始化、赋值方法。
  • 实现正反向迭代器,了解迭代器运算符重载。
  • 实现类Python的 [] 运算取值、赋值,了解class中的运算符重载,特别是=、+、+= 以及构造函数、复制构造函数、析构函数的实现方法、运用等知识。
  • 熟悉 Iterator 内嵌类的一般应用。

二、代码详解

1、Node类模板

#include <iostream>
using namespace std;

template <typename T> class Node{
public:
    T data;
    Node<T>* next;
    Node<T>* pre;
};

以上代码定义了一个名为 Node 的模板类,该类表示一个双向链表。下面部分 Pylist 类包含三个构造函数和一个析构函数。

2、Pylist类构造

template <typename T> class Pylist{
private:
    Node<T>* head;
    Node<T>* tail;
    int len;
public:
    Pylist(){                //tail和head必须先定义
        tail = new Node<T>;
        head = new Node<T>;
        tail->next = NULL;
        tail->pre = head;
        head->next = tail;
        head->pre = NULL;
        len = 0;
    }

    Pylist(initializer_list<T> vlist){     //不定数量节点构造
    tail = new Node<T>;
    head = new Node<T>;
    head->next = tail;
    head->pre = NULL;
    tail->next = NULL;
    tail->pre = head;
    len = 0;
    Node<T>* p = head;
    for (T v :vlist){
        while (p->next != tail){
            p = p->next;
        }
        Node<T>* q = new Node<T>;
        q->data = v;
        q->next = tail;
        tail->pre = q;
        q->pre = p;
        p->next = q;
        len++;
        }
    }

    Pylist(Pylist& other){    //复制构造函数,重载运算+需要,默认重载是二进制拷贝
        tail = new Node<T>;
        head = new Node<T>;
        head->next = tail;
        head->pre = NULL;
        tail->next = NULL;
        tail->pre = head;
        len = 0;
        for (auto v : other){
            append(v);
        }
    }

    ~Pylist(){
        Node<T>* p = head;
        while (p != tail){
            Node<T>* q = p->next;
            delete p;
            p = q;
        }
    }

第一个构造函数是默认构造函数,它不接受任何参数。在默认构造函数中,首先创建两个新的Node对象,分别表示链表的头节点和尾节点。然后将头节点和尾节点连接起来,并将链表的长度初始化为0。

第二个构造函数接受一个initializer_list<T>类型的参数,这是C++ 11提供的一个实现不定数量参数的初始化方法,它可以用于使用初始化列表来创建一个Pylist对象。在这个构造函数中,首先创建头节点和尾节点并将它们连接起来,然后遍历初始化列表中的每个元素,使用append函数(代码在下面)将它们添加到链表中。

第三个构造函数是复制构造函数,它接受一个Pylist对象作为参数。在复制构造函数中,运行方式与前两个基本上是相同的,也使用append函数将它们添加到新创建的链表中。

析构函数用于在销毁Pylist对象时释放内存。在析构函数中,遍历链表中的每个节点,并使用delete运算释放它们占用的内存。

3、内嵌迭代器

    class Iterator{
        private:
        Node<T>* node_;

        public:
        Iterator(Node<T>* node): node_(node){}     //初始化

        Iterator& operator++(){
            node_ = node_->next;
            return *this;
        }

        Iterator operator++(int){   //后置递增运算,int不会被使用,只是一个标记表示后增
        	Iterator tmp = *this;
            node_ = node_->next;
            return tmp;
        }

        Iterator& operator--(){
            node_ = node_->pre;
            return *this;
        }

        Iterator operator--(int){     //后置递减运算
        	Iterator tmp = *this;
            node_ = node_->pre;
            return tmp;
        }

        T& operator*() const{      //解引用迭代器
            return node_->data;
        }

        bool operator==(const Iterator& other) const{    //比较两个迭代器
            return node_ == other.node_;
        }

        bool operator!=(const Iterator& other) const{
            return !(*this == other);
        }
    };

    Iterator begin(){
        return Iterator(head->next);
    }
    Iterator begin() const{
        return Iterator(head->next);
    }

    Iterator end(){
        return Iterator(tail);
    }

    Iterator end() const{
        return Iterator(tail);
    }

    Iterator rbegin(){
        return Iterator(tail->pre);
    }

    Iterator rbegin() const{
        return Iterator(tail->pre);
    }

    Iterator rend(){
        return Iterator(head);
    }

    Iterator rend() const{
        return Iterator(head);
    }

以上迭代器部分,代码中定义了一个名为Iterator的内部类,该类表示Pylist类的迭代器。迭代器可以用于遍历链表中的元素。

Iterator类包含一个私有成员变量node_,表示当前迭代器所指向的节点。它还重载了若干运算符,以便更方便地使用迭代器。

operator++operator--运算符用于将迭代器向前或向后移动一个位置。它分为前置和后置两种形式,在代码中,两种形式的实现是不相同的。

代码中定义了一个后置递增运算符operator++(int),用于将迭代器向前移动一个位置。在该函数中,首先创建一个临时迭代器,用于保存递增之前的迭代器。然后将迭代器向前移动一个位置,并返回临时迭代器。

为了避免返回一个局部变量的引用,在编译时收到警告信息。在C++中,函数返回局部变量的引用是不安全的,因为当函数返回后,局部变量所占用的内存将被释放,此时返回的引用将指向一个无效的内存地址。所以带 int 后置递增运算符的返回类型更改为Iterator而不是Iterator&,将返回一个临时迭代器的副本。

operator*运算符用于解引用迭代器,获取迭代器所指向节点中存储的数据。

operator==operator!=运算符用于比较两个迭代器是否相等。需要注意的是,此处!=运算是调用了==运算实现的,!(*this == other)中的 other 实际上是一个参数,交给上一个 == 运算的,如此写法可以避免修改多处代码。

此外,还定义了若干个成员函数,用于获取链表的起始和结束位置的迭代器。这些函数分别是:

begin()end()函数分别返回链表起始位置和结束位置之后的迭代器。
rbegin()rend()函数分别返回链表末尾位置和起始位置之前的迭代器。这两个前后迭代器分别定义了const版本的重载。

4、Python风格insert方法

    bool insert(int i, T data){
        if (i < 0 || i > len){
            return false;
        }
        Node<T>* p = head;
        for (int j = 0; j < i; j++){
            p = p->next;
        }
        Node<T>* q = new Node<T>;
        q->data = data;
        q->next = p->next;
        p->next->pre = q;
        q->pre = p;
        p->next = q;
        len++;
        return true;
    }

此处代码中定义了一个名为insert的成员函数,该函数用于在链表中插入一个新的元素。insert函数接受两个参数,第一个参数表示插入位置的索引,第二个参数表示要插入的数据。

insert函数中,首先检查插入位置的索引是否有效。如果索引无效,则返回false。然后遍历链表,找到插入位置之前的节点。接下来创建一个新的节点,并将其插入到链表中。最后将链表的长度加1,并返回true表示插入成功。

5、Python风格append方法

    bool append(T data){
        Node<T>* p = head;
        while(p->next != tail){
            p = p->next;
        }
        Node<T>* q = new Node<T>;
        q->data = data;
        q->next = tail;
        tail->pre = q;
        q->pre = p;
        p->next = q;
        len++;
        return true;
    }

以上代码中定义了一个名为append的成员函数,该函数用于在链表末尾添加一个新的元素。append函数接受一个参数,表示要添加的数据。

append函数中,首先遍历链表,找到链表末尾之前的节点。然后创建一个新的节点,并将其插入到链表末尾。最后将链表的长度加1,并返回true表示添加成功。前面的构造函数等处也多次引用了这个函数用以简化代码。

使用append函数在链表末尾添加新元素,例如:

Pylist a;
a.append(1);
a.append(2);
a.append(3);
在执行完以上代码后,Pylist对象a将包含元素1, 2, 3。

6、Python风格[]下标操作方法

    T& operator[](int index){             //运算符[]重载
        if (index < 0 || index >= len){
            throw std::out_of_range("Index out of range");
        }
        Node<T>* p = head->next;
        for (int i = 0; i < index; i++){
            p = p->next;
        }
        return p->data;
    }
    
    const T& operator[](int index) const{
        if (index < 0 || index >= len){
            throw std::out_of_range("Index out of range");
        }
        Node<T>* p = head->next;
        for (int i = 0; i < index; i++){
            p = p->next;
        }
        return p->data;
    }

代码中重载了 [] 运算符,用于访问链表中指定位置的元素。operator[] 函数接受一个整数参数,表示要访问元素的索引。

operator[]函数中,首先检查索引是否有效。如果索引无效,则抛出一个std::out_of_range异常。然后遍历链表,找到指定位置的节点。最后返回该节点中存储的数据。

operator[]函数声明为非常量成员函数,可以调用它来修改对象中的元素,常量成员函数用来访问常量对象中的元素。

7、Python风格+、+= 方法

    Pylist operator+(const Pylist& other){    //运算符+重载,需要赋值=重载
        Pylist res = *this;
        for (auto v :other){
            res.append(v);
        }
        return res;
    }
	Pylist& operator+=(const Pylist& other){
        for (auto v : other){
            append(v);
        }
        return *this;
    }

    Pylist& operator=(const Pylist& other){   //必须是const
        if (this != &other){
            Node<T>* p = head->next;    //不能删除头节点
            while (p != tail){
                Node<T>* q = p->next;
                delete p;
                p = q;
            }
            head->next = tail;
            tail->pre = head;
            len = 0;
            for (auto v : other){
                append(v);
            }
        }
        return *this;
    }

代码中重载了+和+=运算符,用于连接两个Pylist对象。

operator+函数接受一个Pylist对象作为参数,并返回一个新的Pylist对象,该对象包含调用该函数的对象和参数对象中的所有元素。在该函数中,首先创建一个新的Pylist对象,并使用复制构造函数将调用该函数的对象中的所有元素复制到新创建的对象中。然后遍历参数对象中的所有元素,并使用append函数将它们添加到新创建的对象中。

operator+=函数也接受一个Pylist对象作为参数,但它不返回新的对象,而是直接修改调用该函数的对象。在该函数中,遍历参数对象中的所有元素,使用append函数将它们添加到调用该函数的对象中。

operator=用于将一个Pylist对象赋值给另一个Pylist对象。该函数接受一个Pylist对象作为参数,并返回调用该函数的对象的引用。它的删除部分和析构函数几乎一样,只是没有删除头、尾节点。

这样,就可以使用+和+=运算符将两个Pylist对象连接起来,例如:

Pylist a = {1, 2, 3};
Pylist b = {4, 5, 6};
Pylist c = a + b;
a += b;
在执行完以上代码后,Pylist对象c将包含元素1, 2, 3, 4, 5, 6,而对象a也将被修改为包含元素1, 2, 3, 4, 5, 6。

8、Python风格pop方法

    T pop(){
        Node<T>* p = tail->pre;
        Node<T>* tmp = p->pre;
        T q;
        q = p->data;
        tmp->next = tail;
        tail->pre = tmp;
        delete p;
        len--;
        return q;
    }

    T pop(int i){
        Node<T>* p = head;
        T tmp = (T)NULL;
        if (i < 0 || i>= len){
            throw std::out_of_range("Index out of range");
        }else{
            for (int j = 0; j <= i; j++){
                p = p->next;
            }
            tmp = p->data;
            Node<T>* q = p->next;
            q->pre = p->pre;
            p->pre->next = q;
            len--;
        }
        return tmp;
    }

代码中定义了两个名为pop的成员函数,它们用于删除链表中的元素。

第一个pop函数不接受任何参数,它用于删除链表末尾的元素,和Python中的 pop 一样。在该函数中,首先找到链表末尾之前的节点,然后将其从链表中删除,并释放它占用的内存。最后将链表的长度减1,并返回被删除元素中存储的数据。

第二个pop函数接受一个整数参数,表示要删除元素的索引,也和Python中的 pop 一样。在该函数中,首先检查索引是否有效。如果索引无效,则抛出一个std::out_of_range异常。否则,遍历链表,找到指定位置的节点。然后将该节点从链表中删除,并释放它占用的内存。最后将链表的长度减1,并返回被删除元素中存储的数据。

可以使用pop函数删除链表中的元素,例如:

Pylist a = {1, 2, 3};
int x = a.pop();
int y = a.pop(0);
在执行完以上代码后,Pylist对象a将只包含一个元素2,变量x的值为3,变量y的值为1。

9、Python风格remove方法

    bool remove(int i){
        if (i < 0 || i >= len){
            return false;
        }
        Node<T>* p = head;
        for (int j = 0; j < i; j++){
            p = p->next;
        }
        Node<T>* q = p->next;
        p->next = q->next;
        q->next->pre = p;
        delete q;
        len--;
        return true;
    }

代码中定义了一个名为remove的成员函数,该函数用于删除链表中指定位置的元素。remove函数接受一个整数参数,表示要删除元素的索引。

remove函数中,首先检查索引是否有效。如果索引无效,则返回false。然后遍历链表,找到指定位置之前的节点。接下来将指定位置的节点从链表中删除,并释放它占用的内存。最后将链表的长度减1,并返回true表示删除成功。

其实此处在和前面的insert、后面的get函数中以索引查找时可以进行一定的优化,先判断索引更靠近头或尾,然后再决定用正序或倒序查找,在 Pylist 对象元素很多的时候会使查找过程略快。

这样就可以使用remove函数删除链表中指定位置的元素,例如:

Pylist a = {1, 2, 3};
a.remove(1);
在执行完以上代码后,Pylist对象a将包含元素1, 3。

10、length、get方法

    size_t length() const{  //返回元素数量,类似size()
        return len;
    }

    T get(int i) const{
        if (i < 0 || i >= len) {
            throw std::out_of_range("Index out of range");
        }
        Node<T>* p = head->next;
        for (int j = 0; j < i; j++){
            p = p->next;
        }
        T data = p->data;
        return data;
    }
  
};

代码中定义了一个名为get的成员函数,代码逻辑很简单,和前面雷同。该函数用于获取链表中指定位置的元素。只有一个整数参数,表示要获取元素的索引。

三、使用示例

int main(){

    Pylist<int> L;
    int f;
    for (int i=0; i<3; ++i) L.append(i+1);
    Pylist<int> oth = {4, 5};
    L = L + oth;
    L += {6, 7};
    L[6] = 8;
    L.insert(6, 7);
    L.append(9);
    f = L.pop();
    cout << f <<endl;
    L += {9, 10};
    L.pop(9);
    for(auto it=L.begin(); it!=L.end(); it++){
        std::cout << *it << endl;
    }
    for(auto it=L.rbegin(); it!=L.rend(); --it){
        std::cout << *it << endl;
    }
    cout << "len = " << L.length() << endl;
    
    Pylist<char> cl = {'a', 'b', 'c'};
    cout << cl[0] << endl;
}

这浓浓的Python风,就不用解释了吧~


总结

代码实现了一个双向链表,具有基本的链表功能,如添加、删除、访问和修改元素等。此外,还重载了若干运算符,以便更方便地使用Pylist类。

从功能实用性的角度来看,Pylist类具有较高的实用性。它提供了丰富的接口,可以满足大多数使用场景的需求。

在代码中对各种异常情况进行了处理,例如检查索引是否有效、避免自赋值等。这些措施有助于提高代码的健壮性。

当然,代码中肯定也存在一些可以改进的地方,如插入删除成员函数的索引查找部分。笔者老说更喜欢Python是有道理的吧~,为了列表用得更舒服、更Python风,笔者尽力搞出这么个降低效率的链表类,实用的效率肯定是远不如STL中list 的,仅希望能给读者一定的启发,用以更深入的了解数据结构。
代码太长,全放在文章中怕是要被CSDN这傻傻的博文质量检测系统给出17分,以下是git和inscode链接:
github 链接
CSDN inscode 链接


原创文章,未经许可、严禁转载

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

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

相关文章

算法基础学习笔记——⑨C++STL使用技巧

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨CSTL简介 ✨CSTL使用技巧 前言&#xff1a;算法学习笔记记录日常分享&#xff0c;需要的看哈O(∩_∩)O&#xff0c;感谢大家的支持&#xff01; ✨CSTL简介 vector变长数组&#xff0c;倍增的思想//系统为…

六级备考24天|CET-6|翻译技巧4-1|翻译红楼梦|22:40~23:40

目录 1 ANSWER 2 PRACTICE ANSWER ​ 3​ PRACTICE ANSWER 合并 ​ 全文翻译​ 1 ANSWER depict / dɪˈpɪkt / v.描述&#xff0c;描绘 第三人称单数 depicts 现在分词 depicting 过去式 depicted 过去分词 …

【C++】STL——反向迭代器的模拟实现:迭代器适配器

文章目录 前言1. list 的反向迭代器模拟实现2. 思考3. 库里面反向迭代器的实现——迭代器适配器4. 反向迭代器模拟实现的改进——适配器模式5. 适配器模式的实现——一劳永逸 前言 反向迭代器的使用相信大家都已经比较熟悉了&#xff0c;那我们这篇文章具体讲什么呢&#xff1f…

LAMP平台搭建

文章目录 LAMP概述安装apache安装mysql安装php LAMP概述 LAMP架构是目前成熟的企业网站应用模式之一&#xff0c;指的是协同工作的一整套系统和相关软件&#xff0c;能够提供动态Web站点服务及其应用开发环境。LAMP是一个缩写词&#xff0c;具体包括Linux操作系统、Apache网站…

HCIA-单点故障-Smart Link

目录 单点故障&#xff1a; 单设备”链路备份“方案 —— Smart Link Smart Link端口状态&#xff1a; Smart Link基础命令配置&#xff1a; 单设备”链路备份“方案 —— Smart Link案列实现 单设备”链路备份“方案 —— Monitor Link Monitor link组 Monitor link 的使用…

Zookeeper快速入门(Zookeeper概述、安装、集群安装、选举机制、命令行操作、节点类型、监听器原理)

1、Zookeeper入门 1.1 概述 Zookeeper是一个开源的分布式的&#xff0c;为分布式框架提供协调服务的Apache项目。 1、Zookeeper工作机制 Zookeeper从设置模式角度来理解&#xff1a;是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责储存和管理大家都关心的数…

spring源码篇(八)事务的原理

文章目录 前言基本操作验证 Spring事务的传播机制特殊的机制说明NOT_SUPPORTEDNESTEDSUPPORTS 源码加载事务自动配置类要不要加注解&#xff1a;EnableTransactionManagement配置类说明 EnableTransactionManagement 做了什么AutoProxyRegistrar做了什么创建的代理类是jdk动态代…

UE DTDataTable 插件说明, 运行中操作CSV文件。

UDataTable的扩展对象&#xff0c;可以在Runtime模式下可以加载和保存CSV文件&#xff0c;并且可以进行数据的添加和删除&#xff0c;也可以使用系统DataTable的所有函数。 1. Create DT Data Table From CSV File 从CSV文件创建 DT Data Table 对象 File Path &#xff1a;文件…

MiniConda、CUDA、CUDnn以及pytorch环境的配置以及坑

文章目录 一、MiniConda安装、介绍1.1 Conda是什么&#xff1f;1.2 MiniConda是什么&#xff1f;1.3 安装方法1.4 Conda常用命令1.5 杂项 二、CUDA 以及 CUDnn三、Pytorch安装总结 首先需要说明一下&#xff0c;我想安装的是Pytorch GPU版&#xff0c;所以需要安装CUDA toolkit…

WPF 页面布局 DockPanel Grid StackPanel UniformGrid WrapPanel WPF布局入门 WPF布局资料

在布局常用的布局属性 HorizontalAlignment: 用于设置元素的水平位置VerticalAlignment: 用于设置元素的垂直位置 Margin: 指定元素与容器的边距 Height: 指定元素的高度 Width: 指定素的宽度 WinHeight/WinWidth: 指定元素的最小高度和宽度MaxHeight/MaxWidth: 指定元素的最大…

Selenium自动化测试(基于Java)

目录 一. 了解Selenium ✅1.1 概念 ✅1.2 作用 ✅1.3 特点 ✅ 工作原理 二. Selenium Java 环境搭建 ✅2.1 下载 Chrome 浏览器 ✅2.2 查看浏览器的版本 ✅2.3 下载浏览器驱动 ✅2.4 验证环境是否搭建成功 三. Selenium 常用 API ✅3.1 定位元素 ✅3.2 操作对象 ✅…

给你的终端(windows 11的命令行 增加一个好看的背景)

1.win R 键入cmd 加回车 2.点击这个符号 进入设置 3. 找到命令提示符 4.往下滚动你的小鼠标 5.看见其他设置 点击外观 6.继续滚动小鼠标 找到背景图像 路径 最后选择 自己的喜欢图片的存放路径就可以啦 啦啦啦

宠物医院小程序开发需具备哪些功能?

想要开发一款专业好用的宠物医院小程序系统&#xff0c;需要具备哪些基本功能呢&#xff1f; 1、在线预约。用户可以在线上预约宠物医院的服务&#xff0c;包括医生、服务的具体内容、预约的时间、具体的宠物医院地址等&#xff0c;不仅可以帮助用户合理安排好时间还能让…

consul命令总结

1. consul members -http-addrxxxxx 查看指定地址consul集群的所有节点 举例&#xff1a;查看地址192.168.5.47上consul集群的所有consul节点 如下图&#xff0c;该集群一共有三个节点 2. consul info -http-addrxxxxx 查看指定地址consul集群的详细信息 举例&#xff1a;查看…

python:根据红光波段和近红外波段的遥感图像计算NDVI

作者:CSDN @ _养乐多_ 本文将介绍使用python中的GDAL库,以及红光波段和近红外波段两个单波段影像计算NDVI影像的方法和代码。并使用matplotlib库将NDVI绘制成图片。 结果如下图所示, 文章目录 一、代码1,主函数2,执行函数二、使用matpoltlib绘图一、代码 1,主函数 im…

自然科学领域期刊分区——什么是核心期刊(核心A、B、C)

目录 前言 1、什么是核心期刊 2、期刊来源数据库的大致排名 3、什么是顶刊、权威期刊、核心A、核心B、核心C 4、JCR分区与中科院分区 5、中科院2023年预警国际期刊 前言 本文只做一个简单的科普&#xff0c;当然每所院校或者科研单位对期刊分区或者认定有一套自己的认定…

搭建stm32电机控制代码框架(一)——Stm32CubeMx入门

也是挑战一下自己吧&#xff0c;看看多久能够把自己的代码框架搭建起来&#xff0c;今天是5月23日&#xff0c;看看最终搭建成功的时候是什么时候&#xff0c;目标其实这个阶段很简单&#xff0c;电机转一个双闭环FOC就行。 这次的任务是基于stm32f405芯片进行展开&#xff0c…

如何看待现在的网络安全行业?

前言 网络安全是一个需要具备专业技术和能力的综合性行业&#xff0c;从业者需要具备扎实的技术功底和批判性思维&#xff0c;不断学习和更新知识&#xff0c;以保证随时能够应对威胁和攻击。 在现在的网络安全行业&#xff0c;从业者面临的挑战较多&#xff0c;如恶意代码、…

K8s环境部署Triton实现云端模型推理

前置条件&#xff1a;K8集群、helm 1、以模型名作为目录名&#xff0c;创建目录 mkdir resnet50_pytorch 2、将模型文件、配置文件&#xff08;输入、输出等&#xff09;存到刚创建的目录下&#xff0c;resnet50_pytorch目录下文件层级结构如下 model-respository/ └── …

攫取 RGB图像 和 PCM音频 数据

一、获取源码 1. 下载地址 Github: https://github.com/Gaaagaa/MediaGrabber 2. 编译提醒 这个测试程序&#xff0c;是使用 QtCreator 写的 Qt 界面程序&#xff0c;调用我封装好的 vlc_mgrabber_t 类实现了一个简单的播放器。MFC的我也写过相应的测试程序&#xff0c;这里…