list的模拟实现

全部代码

#pragma once
namespace HQJ
{template<class T>struct __list_node//节点类{T __data;__list_node<T>* __prev;__list_node<T>* __next;__list_node(const T& x = T())//由于不知道要存储的数据类型,使用匿名对象进行初始化:__data(x), __prev(nullptr), __next(nullptr){}};template<class T, class REF, class PTR>//const迭代器和普通迭代器只是在*和->运算符重载的返回值不同//我们添加REF作为T&和const T&,PTR作为T*和const T*struct __iterator//迭代器类,核心是节点指针{typedef __list_node<T> Node;typedef __iterator<T,REF,PTR> self;Node* _node;__iterator(Node* node)//指向头结点:_node(node){}self& operator++(){_node = _node->__next;return *this;//不是返回_node}self& operator++(int){self tmp(_node);_node = _node->__next;return tmp;//不是返回_node}self& operator--(int){self tmp(_node);_node = _node->__prev;return tmp;//不是返回_node}self& operator--(){_node = _node->__prev;return *this;}PTR operator->(){return &_node->__data;}REF operator*(){return _node->__data;}bool operator!=(const self& s)//这里也要是迭代器参数,不能是Node*{return _node != s._node;}};template<class T>//这里也要是模板函数,实现的时候别忘记了class list//list类{public:typedef __list_node<T> Node;typedef __iterator<T, T&, T*> iterator;typedef __iterator<T, const T&, const T*> const_iterator;template<class T>iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* newnode = new Node(x);newnode->__next = cur;newnode->__prev = cur->__prev;cur->__prev->__next = newnode;cur->__prev = newnode;return newnode;}const_iterator begin()const{return const_iterator((_head->__next));//首元节点而不是头结点}const_iterator end()const{return const_iterator(_head);//不是_head->__next,因为迭代器是左闭右开}iterator begin(){return iterator((_head->__next));//首元节点而不是头结点}iterator end(){return iterator(_head);//不是_head->__next,因为迭代器是左闭右开}void empty_init(){_head = new Node;_head->__next = _head;_head->__prev = _head;}/* template<class T>void push_back(const T& x){Node* newnode = new Node(x);newnode->__prev = _head->__prev;_head->__prev->__next = newnode;_head->__prev = newnode;newnode->__next = _head;}*/template<class T>void push_back(const T& x){insert(end(), x);}template<class T>void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(end());}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}void pop_front(){erase(begin());}bool empty(){return (_head->__next) == _head;}void swap(list<T> lt){std::swap(_head, lt._head);}iterator erase(iterator pos){assert(!empty());Node* cur = pos._node;Node* next = cur->__next;cur->__prev->__next = cur->__next;cur->__next->__prev = cur->__prev;delete cur;return next;}list<T>& operator=(const list<T>& lt){swap(lt);return *this;}list(){empty_init();}list(const list<T>& lt){empty_init();for (auto e : lt){push_back(e);}}private:Node* _head;};template<typename T>//告知编译器,这是个未实例化的模板,等实例化之后再去取void print_list(const list<T>& lt){typename list<T>::const_iterator it = lt.begin();while (it != lt.end()){cout << *it << endl;it++;}}template<typename Container>//告知编译器,这是个未实例化的模板,等实例化之后再去取void print_list(const Container& con){typename Container::const_iterator it = con.begin();while (it != con.end()){cout << *it << endl;it++;}}
} 

节点类的模拟实现

  • 这是一个双向链表的节点类,包含了数据成员__data(存储节点的数据)、__prev(指向前一个节点的指针)和__next(指向后一个节点的指针)。节点类还定义了一个构造函数,在创建节点时可以传入数据值进行初始化,如果没有传入数据值,则使用默认构造函数创建一个匿名对象进行初始化。这样设计的好处在于可以在创建节点时灵活地指定要存储的数据类型,并且可以通过指针链接起各个节点,形成一个双向链表的结构。

template<class T>struct __list_node//节点类{T __data;__list_node<T>* __prev;__list_node<T>* __next;__list_node(const T& x = T())//由于不知道要存储的数据类型,使用匿名对象进行初始化:__data(x), __prev(nullptr), __next(nullptr){}};

迭代器类的模拟实现

template<class T, class REF, class PTR>//const迭代器和普通迭代器只是在*和->运算符重载的返回值不同//我们添加REF作为T&和const T&,PTR作为T*和const T*struct __iterator//迭代器类,核心是节点指针{typedef __list_node<T> Node;typedef __iterator<T,REF,PTR> self;Node* _node;__iterator(Node* node)//指向头结点:_node(node){}self& operator++(){_node = _node->__next;return *this;//不是返回_node}self& operator++(int){self tmp(_node);_node = _node->__next;return tmp;//不是返回_node}self& operator--(int){self tmp(_node);_node = _node->__prev;return tmp;//不是返回_node}self& operator--(){_node = _node->__prev;return *this;}PTR operator->(){return &_node->__data;}REF operator*(){return _node->__data;}bool operator!=(const self& s)//这里也要是迭代器参数,不能是Node*{return _node != s._node;}};

类型重定义

  • typedef __list_node Node; 定义了一个类型别名 Node,它表示 __list_node 类型。这样做的目的是为了简化代码中使用 __list_node 的地方,可以直接使用 Node。

  • typedef __iterator<T,REF,PTR> self; 定义了一个类型别名 self,它表示 __iterator<T,REF,PTR> 类型。同样地,这样做的目的是为了简化代码中使用 __iterator<T,REF,PTR> 的地方,可以直接使用 self。

  • 使用 typedef 关键字可以为已有的类型或者组合类型定义一个新的名称,方便在代码中使用并增加可读性。

typedef __list_node<T> Node;
typedef __iterator<T,REF,PTR> self;

构造函数

  • 在该构造函数中,_node(节点指针)可以指向链表中的任意一个节点,但是这里是将其直接指向头结点。头结点是一个不存储具体数据的虚拟节点,它的作用主要是简化链表的各种操作,比如在头部插入一个元素或者删除一个元素时,无需特判链表为空的情况,只需要处理头结点和第一个实际节点即可。

  __iterator(Node* node)//指向头结点:_node(node){}

++运算符重载

  • self& operator++() 是前置自增运算符的重载函数,它用于将迭代器指向链表中的下一个节点。在函数体内,首先将 _node 指针移动到下一个节点 _node->__next,然后返回自身的引用 *this。这样做是为了支持连续的自增操作,例如 ++i1; ++i1; 。

  • self& operator++(int) 是后置自增运算符的重载函数,它与前置自增运算符功能相同,但是通过传入一个额外的 int 参数来区分前置和后置自增运算符。为了保持后置自增运算符的语义,需要创建一个临时的迭代器 tmp 来保存当前迭代器的值,然后再将迭代器指向下一个节点 _node->__next,最后返回临时迭代器 tmp。这样做是为了实现 C++ 中后置自增运算符的规定行为,即返回自增前的值。

  • 需要注意的是,这两个重载函数并不直接返回节点指针 _node,而是返回迭代器对象本身的引用或临时对象。这是为了保持迭代器的一致性和语义,在使用迭代器进行链表操作时更加直观和方便。

   self& operator++(){_node = _node->__next;return *this;//不是返回_node}self& operator++(int){self tmp(_node);_node = _node->__next;return tmp;//不是返回_node}

--运算符重载

  • self& operator--(int) 是后置自减运算符的重载函数,用于将迭代器指向链表中的前一个节点。在函数体内,首先创建一个临时的迭代器 tmp 来保存当前迭代器的值,然后将 _node 指针移动到前一个节点 _node->__prev,最后返回临时迭代器 tmp。这样做是为了实现 C++ 中后置自减运算符的规定行为,即返回自减前的值。

  • self& operator--() 是前置自减运算符的重载函数,与后置自减函数功能相同,但是不需要返回临时对象。在函数体内,直接将 _node 指针移动到前一个节点 _node->__prev,然后返回自身的引用 *this。这样做是为了支持连续的自减操作,例如 --i1; --i1; 。

  self& operator--(int){self tmp(_node);_node = _node->__prev;return tmp;//不是返回_node}self& operator--(){_node = _node->__prev;return *this;}

->运算符重载

  • 在函数体内,首先获取 _node 指针所指向的节点中存储的数据成员的指针 _node->__data,然后将其取地址返回。由于 _node->__data 的类型为 T,因此返回的指针类型为 T*。

  • 通过重载箭头运算符,可以使得迭代器对象在使用时像指针一样简洁明了,例如 iter->data_member; 可以直接访问节点存储的数据成员 data_member。而不需要通过 (*iter).data_member; 这样的方式来访问。

  PTR operator->(){return &_node->__data;}

*(解引用)运算符重载

  • 在函数体内,首先获取 _node 指针所指向的节点中存储的数据成员 _node->__data,然后直接返回其值。由于返回的是数据成员的引用,因此返回类型为 REF,可能是 T& 或者 const T&,取决于节点数据成员的类型和迭代器的常量性。

  • 通过重载解引用运算符,可以使得迭代器对象在使用时可以直接获取节点存储的数据,并且可以修改数据(若不是 const 迭代器)。例如 *iter = new_data; 可以直接将新的数据 new_data 赋值给节点。而不需要通过 iter._node->__data = new_data; 这样的方式来修改。

    REF operator*(){return _node->__data;}
    

    !=运算符重载

  • 在函数体内,将当前迭代器对象 _node 和参数传入的迭代器对象 s._node 进行比较。如果它们指向的节点地址不相等,则返回 true,表示两个迭代器不相等;否则返回 false,表示两个迭代器相等。

  • 通过重载不等于运算符,我们可以直接使用 iter1 != iter2 来判断两个迭代器是否相等,而不需要显式地比较节点地址。这样可以提高代码的可读性和简洁性。

    bool operator!=(const self& s)//这里也要是迭代器参数,不能是Node*{return _node != s._node;}
    

    list类的模拟实现

    template<class T>//这里也要是模板函数,实现的时候别忘记了class list//list类{public:typedef __list_node<T> Node;typedef __iterator<T, T&, T*> iterator;typedef __iterator<T, const T&, const T*> const_iterator;template<class T>iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* newnode = new Node(x);newnode->__next = cur;newnode->__prev = cur->__prev;cur->__prev->__next = newnode;cur->__prev = newnode;return newnode;}const_iterator begin()const{return const_iterator((_head->__next));//首元节点而不是头结点}const_iterator end()const{return const_iterator(_head);//不是_head->__next,因为迭代器是左闭右开}iterator begin(){return iterator((_head->__next));//首元节点而不是头结点}iterator end(){return iterator(_head);//不是_head->__next,因为迭代器是左闭右开}void empty_init(){_head = new Node;_head->__next = _head;_head->__prev = _head;}/* template<class T>void push_back(const T& x){Node* newnode = new Node(x);newnode->__prev = _head->__prev;_head->__prev->__next = newnode;_head->__prev = newnode;newnode->__next = _head;}*/template<class T>void push_back(const T& x){insert(end(), x);}template<class T>void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(end());}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}void pop_front(){erase(begin());}bool empty(){return (_head->__next) == _head;}void swap(list<T> lt){std::swap(_head, lt._head);}iterator erase(iterator pos){assert(!empty());Node* cur = pos._node;Node* next = cur->__next;cur->__prev->__next = cur->__next;cur->__next->__prev = cur->__prev;delete cur;return next;}list<T>& operator=(const list<T>& lt){swap(lt);return *this;}list(){empty_init();}list(const list<T>& lt){empty_init();for (auto e : lt){push_back(e);}}private:Node* _head;};
    

    类型重定义

  • Node 是 __list_node 类型的别名,其中 __list_node 是链表节点的实现类。通过定义 Node 别名,可以使得在模板类中定义节点指针时更加方便,例如 Node* _node;。

  • iterator 是 __iterator<T, T&, T*> 类型的别名,其中 __iterator 是迭代器类的实现类,T& 是解引用后返回的数据成员类型的引用,T* 是箭头运算符返回的数据成员类型的指针。通过定义 iterator 别名,可以使得在模板类中定义迭代器变量时更加简洁,例如 iterator iter;。

  • const_iterator 是 __iterator<T, const T&, const T*> 类型的别名,其中 const T& 是解引用后返回的数据成员类型的常量引用,const T* 是箭头运算符返回的数据成员类型的常量指针。通过定义 const_iterator 别名,可以使得在模板类中定义常量迭代器变量时更加方便,例如 const_iterator iter_c;。

    typedef __list_node<T> Node;
    typedef __iterator<T, T&, T*> iterator;
    typedef __iterator<T, const T&, const T*> const_iterator;
    

    私有成员

  • 一个指向链表头节点的指针

    Node* _head;
    

    构造函数和拷贝构造函数

  • 默认构造函数 list() 会调用 empty_init() 函数进行初始化。empty_init() 函数实际上是一个私有函数,用于初始化链表的头节点和尾指针,将头节点的前后指针都指向自身,表示链表为空。

  • 拷贝构造函数 list(const list& lt) 接受一个常量引用类型的参数 lt,表示要进行拷贝的链表。

  • 在函数内部,首先调用 empty_init() 函数初始化空链表;然后通过遍历参数链表 lt,逐一将其元素插入到新的链表中,这里使用了 push_back(e) 函数将每个元素插入到链表的尾部。具体实现时,可以使用 C++11 引入的 for-range 循环,对链表 lt 进行遍历。

     list(){empty_init();}list(const list<T>& lt){empty_init();for (auto e : lt){push_back(e);}}
    

    insert函数

+- 首先,通过 pos._node 获取插入位置 pos 对应的链表节点。然后,使用 new 运算符创建一个值为 x 的新节点 newnode。

  • 接下来,将新节点 newnode 的指针指向当前位置 cur,即 newnode->__next = cur。

  • 然后,将新节点 newnode 的前驱指针指向当前位置的前一个节点,即 newnode->__prev = cur->__prev。

  • 随后,通过修改当前位置节点 cur 的前一个节点和后一个节点的指针,将新节点 newnode 插入到链表中。将当前位置节点的前一个节点的后继指针指向新节点 newnode,即 cur->__prev->__next = newnode,然后将当前位置节点的前驱指针指向新节点 newnode,即 cur->__prev = newnode。

  • 最后,返回插入的新节点 newnode。

    template<class T>iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* newnode = new Node(x);newnode->__next = cur;newnode->__prev = cur->__prev;cur->__prev->__next = newnode;cur->__prev = newnode;return newnode;}
    

    迭代器接口

  • 对于 begin() 函数,它返回一个指向链表首元节点的迭代器。由于该函数是在常量函数中定义的,因此返回的是一个 const_iterator 类型的常量迭代器。它通过 _head->__next 获取首元节点的地址,然后将其传递给 const_iterator 类的构造函数,返回一个指向首元节点的常量迭代器。

  • 对于 end() 函数,它返回一个指向链表尾后位置的迭代器。同样地,由于该函数是在常量函数中定义的,因此返回的是一个 const_iterator 类型的常量迭代器。它通过 _head 获取头结点的地址,并将其传递给 const_iterator 类的构造函数,返回一个指向尾后位置的常量迭代器。

  • 对于非常量版本的 begin() 和 end() 函数,它们的实现方法与常量版本相似,只不过返回的是一个 iterator 类型的迭代器,用于修改链表元素的值。因此,它们的实现中也是通过 _head->__next 和 _head 获取首元节点和尾后位置的地址,并将其传递给 iterator 类的构造函数,返回一个指向首元节点和尾后位置的迭代器。

    const_iterator begin()const{return const_iterator((_head->__next));//首元节点而不是头结点}const_iterator end()const{return const_iterator(_head);//不是_head->__next,因为迭代器是左闭右开}iterator begin(){return iterator((_head->__next));//首元节点而不是头结点}iterator end(){return iterator(_head);//不是_head->__next,因为迭代器是左闭右开}
    

    empty_init函数

  • 首先,代码通过创建一个新的节点 _head 来作为链表的头结点。然后,将 _head 的 __next 和 __prev 指针都指向自身,即 _head->__next = _head 和 _head->__prev = _head。这样就形成了一个循环链表,头结点的 __next 和 __prev 都指向自身,表示链表为空。

  • 这样的初始化操作是为了确保在链表为空时,头结点的 __next 和 __prev 都指向自身,以方便后续对链表的插入、删除等操作进行统一处理。同时,由于链表为空,头结点既是首元节点又是尾节点,它们之间相互指向自身,构成一个闭合的循环链表结构。

    void empty_init(){_head = new Node;_head->__next = _head;_head->__prev = _head;}
    

    push_back和push_front函数·

    • push_back() 函数通过调用 insert() 函数,在链表尾部插入元素。它接受一个常量引用 x,表示要插入的元素值。在具体实现中,通过调用 end() 函数获取指向链表尾后位置的迭代器,然后将该迭代器作为参数传递给 insert() 函数,同时传递要插入的元素值 x。这样就实现了将元素插入到链表尾部的功能。

  • push_front() 函数通过调用 insert() 函数,在链表头部插入元素。它接受一个常量引用 x,表示要插入的元素值。在具体实现中,通过调用 begin() 函数获取指向链表首元节点的迭代器,然后将该迭代器作为参数传递给 insert() 函数,同时传递要插入的元素值 x。这样就实现了将元素插入到链表头部的功能。

    template<class T>void push_back(const T& x){insert(end(), x);}template<class T>void push_front(const T& x){insert(begin(), x);}
    

    erase函数

  • 首先,代码使用断言 assert(!empty()) 来确保链表不为空,即在删除元素之前必须保证链表中至少有一个元素。

  • 接着,根据传入的参数 pos,获取要删除的节点 cur 和它的下一个节点 next。这里的 pos 是一个迭代器,指向要删除的节点。

  • 将 cur 的前一个节点的 __next 指针指向 cur 的下一个节点,将 cur 的后一个节点的 __prev 指针指向 cur 的前一个节点。这样就将 cur 从链表中断开了。

  • 使用 delete 关键字释放内存,删除节点 cur。

  • 最后,返回下一个元素的迭代器 next,作为删除操作后的迭代位置。

     iterator erase(iterator pos){assert(!empty());Node* cur = pos._node;Node* next = cur->__next;cur->__prev->__next = cur->__next;cur->__next->__prev = cur->__prev;delete cur;return next;}
    

    pop_back和pop_front函数

  • 在具体实现中,该函数通过调用 erase() 函数来删除链表中的最后一个元素。它首先调用 end() 函数获取指向链表尾部的迭代器,然后将该迭代器作为参数传递给 erase() 函数来删除其前一个节点,从而删除链表中的最后一个元素。

  • 在具体实现中,该函数通过调用 erase() 函数来删除第一个元素。它首先调用 begin() 函数获取指向链表首部的迭代器,然后将该迭代器作为参数传递给 erase() 函数来删除链表的第一个元素。

此时,由于 erase() 函数返回的是被删除节点的下一个节点的迭代器,因此返回值可以忽略。删除链表的第一个元素后,若链表不为空,则其首部会被新的首元节点所取代。

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

clear函数

  • 在具体实现中,该函数通过迭代调用 erase() 函数来逐个删除链表中的元素。它首先通过调用 begin() 函数获取指向链表首部的迭代器 it,并与 end() 的迭代器进行比较,如果不相等就执行循环操作。

  • 在每次循环中,它使用迭代器 it 作为参数调用 erase() 函数来删除当前节点,并将返回值赋给迭代器 it。由于 erase() 函数返回的是被删除节点的下一个节点的迭代器,所以在循环条件中不断判断 it 是否等于 end(),可以确保及时退出循环。

  • 当函数执行完毕后,链表中的所有元素都已被删除,其大小为 0。

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

    empty函数

  • 首先,代码通过 _head->__next 来获取链表的头节点的下一个节点。

  • 接着,将获取到的下一个节点与链表的头节点进行比较,如果相等,即 _head->__next == _head,则表示链表为空。

  • 最后,将比较结果作为布尔值返回,以表示链表是否为空。

    bool empty(){return (_head->__next) == _head;}

    swap函数

  • swap() 函数接受一个 list 类型的参数 lt,表示另一个要进行交换的链表。

  • 在函数内部,通过调用 std::swap() 函数来交换当前链表的 _head 与参数链表 lt 的 _head。std::swap() 是标准库中的函数,用于交换两个对象的值。

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

    =运算符重载

  • operator= 函数接受一个 const list& 类型的参数 lt,表示要进行赋值的链表。

  • 在函数内部,调用 swap() 函数将当前链表与参数链表 lt 进行交换。由于 swap() 函数是以引用方式传递参数的,因此实际上是交换了两个链表的头节点;同时,函数的返回值是当前链表的引用。

  • 最后,将当前链表的引用作为返回值返回,以支持链式赋值操作。

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

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

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

相关文章

HiveServer2 Service Crashes(hiveServer2 服务崩溃)

Troubleshooting Hive | 5.9.x | Cloudera Documentation 原因&#xff1a;别人用的都好好的&#xff0c;我的集群为什么会崩溃&#xff1f; 1.hive分区表太多(这里没有说具体数量。) 2.并发连接太多&#xff0c;我记的以前默认是200个连接 3.复杂的hive查询访问表的的分区…

(一)实现一个简易版IoC容器【手撸Spring】

一、前言 相信大家在看本篇文章的时候&#xff0c;对IoC应该有一个比较清晰的理解&#xff0c;我在这里再重新描述下&#xff1a;它的作用就是实现一个容器将一个个的Bean&#xff08;这里的Bean可以是一个Java的业务对象&#xff0c;也可以是一个配置对象&#xff09;统一管理…

JetPack系列:001-JetPack概要介绍

文章目录 1. 概念介绍2. 主要内容2.1 框架库2.2 UI界面库 3. 核心思想4. 内容总结 本章回是一起Talk AndroidJetpack吧专栏的第一章回&#xff0c;本章回中主要介绍JetPack的基本概念和编程思想&#xff0c;同时也会介绍它的基础知识。闲话休提&#xff0c;请我们一起Talk Andr…

趣味工具箱小程序源码

趣味工具箱小程序源码&#xff0c;支持功能去水印&#xff0c;精选壁纸&#xff0c;图片压缩&#xff0c;文字生成二维码&#xff0c;图片加水印&#xff0c;模拟来电&#xff0c;手持弹幕&#xff0c;掷骰子…等 使用小工具&#xff0c;一个小程序有几十个功能。 源码下载&am…

面试打底稿⑥ 项目一的第二部分

简历原文 抽查部分 计算运费模块板块扩展性优化&#xff0c;采用责任链模式&#xff0c;实现不同地区间寄件的运费模板扩展的优化&#xff0c;为模块解耦&#xff0c;提高了系统的扩展性 短信模块设计&#xff0c;设计了短信发送数据模板的数据化存储&#xff0c;规范了发送短…

uniapp微信小程序蓝牙连接与设备数据对接

蓝牙连接并通信方法封装大致步骤。 初始化蓝牙并搜索&#xff1b;获取并启用service服务&#xff1b;数据读取和监听设备返回数据 需要使用uniapp官方提供api&#xff1a; // 关闭蓝牙 uni.closeBluetoothAdapter({}) // 打开蓝牙 uni.openBluetoothAdapter({}) // 搜索附近…

服务器or虚拟机安装SSH和虚拟机or服务器设置远程服务权限

第一步 服务器/虚拟机安装SSH工具,这是外部SSH终端连接服务器/虚拟机的第一步! sudo apt update && sudo apt upgrade#更新apt sudo apt install openssh-server#安装SSH工具 service ssh status#查看SSh运行状态 sudo systemctl enable --now ssh#运行SSH工具第二步…

Pytorch-学习记录-1-Tensor

1. 张量 (Tensor): 数学中指的是多维数组&#xff1b; torch.Tensor data: 被封装的 Tensor dtype: 张量的数据类型 shape: 张量的形状 device: 张量所在的设备&#xff0c;GPU/CPU requires_grad: 指示是否需要计算梯度 grad: data 的梯度 grad_fn: 创建 Tensor 的 Functio…

Google Cloud dataflow streaming job简介

简单介绍 首先 gcp 的dataflow 是1个ETL 组件, 它是基于Apache beam的 Apache beam 是1个较新的开源ETL 框架。 对于我们常用的ETL tool Spring batch 有下面的区别 spring batch 更偏向batch &#xff08;后台处理&#xff09;的ETL&#xff0c; 而apache beam 同时支持bat…

[python 刷题] 3 Longest Substring Without Repeating Characters

[python 刷题] 3 Longest Substring Without Repeating Characters 题目&#xff1a; Given a string s, find the length of the longest substring without repeating characters. 这到提要求找的是最长的&#xff0c;没有重复符号的子字符串 解题思路是用双指针哈希表&…

mysql MVCC(多版本并发控制)理解

最近看MVCC相关资料&#xff0c;这边做一个记录总结&#xff0c;方便后续理解。 目录 一、MVCC相关概念 二、MVCC实现原理 1.隐藏字段 2.undo log 3.Read View 4.MVCC的整体处理流程 5. RC&#xff0c;RR级级别下的innoDB快照读有什么不同 6.总结 一、MVCC相关概念 1…

华为云云耀云服务器L实例评测|部署私有网盘 Nextcloud

华为云云耀云服务器L实例评测&#xff5c;部署私有网盘 Nextcloud 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 产品规格1.3 应用场景 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 Nextcloud3.1 Nextcloud 介绍3.2 Docker 环境搭建3.3 Nex…

LeetCode 面试题 08.06. 汉诺塔问题

文章目录 一、题目二、C# 题解 一、题目 在经典汉诺塔问题中&#xff0c;有 3 根柱子及 N 个不同大小的穿孔圆盘&#xff0c;盘子可以滑入任意一根柱子。一开始&#xff0c;所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以…

服务器的外网IP查阅方式

服务器的外网IP查阅方式 linux服务器查询外网ip地址&#xff0c;可以有以下三种方式查阅 查看服务器的外网IP 1、curl cip.cc IP : 183.1X.2XX.88 地址 : 中国 广东 深圳 运营商 : 电信 数据二 : 广东省深圳市 | 电信 数据三 : 中国广东省深圳市 | 电信 2、curl myip.ipip.n…

mysql面试题23:如果某个表有近千万数据,CRUD比较慢,如何优化?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:如果某个表有近千万数据,CRUD比较慢,如何优化? 当某个表存在近千万数据且CRUD(增删改查)操作比较慢时,可以考虑以下优化策略: 使用索引:索…

数百个下载能够传播 Rootkit 的恶意 NPM 软件包

供应链安全公司 ReversingLabs 警告称&#xff0c;最近观察到的一次恶意活动依靠拼写错误来诱骗用户下载恶意 NPM 软件包&#xff0c;该软件包会通过 rootkit 感染他们的系统。 该恶意软件包名为“node-hide-console-windows”&#xff0c;旨在模仿 NPM 存储库上合法的“node-…

Java中使用正则表达式

正则表达式 正则表达式&#xff08;Regular Expression&#xff09;是一种用于匹配、查找和替换文本的强大工具。它由一系列字符和特殊字符组成&#xff0c;可以用来描述字符串的模式。在编程和文本处理中&#xff0c;正则表达式常被用于验证输入、提取信息、搜索和替换文本等…

使用 Apache Camel 和 Quarkus 的微服务(一)

【squids.cn】 全网zui低价RDS&#xff0c;免费的迁移工具DBMotion、数据库备份工具DBTwin、SQL开发工具等 ​Apache Camel 绝非Java企业技术栈领域的新手。它由James Strachan在2007年创建&#xff0c;旨在实现著名的 "EIP 书"&#xff08;由Gregor Hohpe和Bobby W…

2.5 数字传输系统

笔记&#xff1a; 针对这一节的内容&#xff0c;我为您提供一个笔记的整理方法。将内容按重要性、逻辑关系进行组织&#xff0c;再进行简化。 ## 2.5 数字传输系统 ### 背景介绍&#xff1a; 1. **早期电话网**&#xff1a;市话局到用户采用双绞线电缆&#xff0c;长途干线采…

汇总开源大模型的本地API启动方式

文章目录 CodeGeex2ChatGLM2_6BBaichuan2_13Bsqlcoder开启后测试 CodeGeex2 from fastapi import FastAPI, Request from transformers import AutoTokenizer, AutoModel import uvicorn, json, datetime import torch import argparse try:import chatglm_cppenable_chatglm_…