STL之list容器的介绍与模拟实现+适配器

news2025/1/18 3:29:52

STL之list容器的介绍与模拟实现+适配器

  • 1. list的介绍
  • 2. list容器的使用
    • 2.1 list的定义
    • 2.2 list iterator的使用
    • 2.3 list capacity
    • 2.4 list element access
    • 2.5 list modifiers
    • 2.6 list的迭代器失效
  • 3. list的模拟实现
    • 3.1 架构搭建
    • 3.2 迭代器
      • 3.2.1 正向迭代器
      • 3.2.2反向迭代器+适配器
    • 3.3 空间控制模块
    • 3.4 数据的访问
    • 3.5 增加/删除数据
    • 3.6 构造/拷贝构造/析构
  • 4. 整体代码逻辑

所属专栏:C“嘎嘎" 系统学习❤️
🚀 >博主首页:初阳785❤️
🚀 >代码托管:chuyang785❤️
🚀 >感谢大家的支持,您的点赞和关注是对我最大的支持!!!❤️
🚀 >博主也会更加的努力,创作出更优质的博文!!❤️

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来说这
    可能是一个重要的因素)

2. list容器的使用

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

2.2 list iterator的使用

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

【注意】

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

2.3 list capacity

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

2.4 list element access

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

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中的有效元素

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())
	{
		it = l.erase(it);
	}
}

3. list的模拟实现

3.1 架构搭建

  • 首先,list容器的底层实现是一个双向循环链表。所以在实现本章节的前提下,我们首先要熟知我们学习C语言的时候是怎么实现一个带头双向循环链表的。核心的逻辑思维是一模一样的。
  • 所以在实现的前提下,我们可以先从C语言数据结构着手起步。
  • 整体的构架就是:1. 要有一个节点的类,里面包含了两个指针next和prev,和一个存放数据的变量val。2. 就是构建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 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<const_iterator, const T&, const T*> const_reverse_iterator;

  public:
      ///
      // List的构造
      list()
      {……}

      //拷贝构造
      list(const list<T>& l)
      {……}
      
      ~list()
      {……}
//………………
  private:
  	//创建新节点
      void CreateHead()
      {
          _pHead = new Node;
          _pHead->_pPre = _pHead;
          _pHead->_pNext = _pHead;
      }
      Node* _pHead;
}

3.2 迭代器

同样提供两个版本const 和 非const版本的。

  • 但是这里要注意一点就是,我们的迭代器在进行移动的时候无非就是++/–操作,但是我们可由直接进行(iterator)a++吗?如果这是个内置类型的话那自然是可以的。但是组成list容器并非是内置类型,而是自定义类型,每个节点都是一个类,而自定义类型是无法直接进行++/–等一些类的运算符操作的。要想对自定义类型进行运算符操作就必须要使用运算符重载函数。所以为了可以对迭代器进行运算符操作就也要定义一个迭代器类,并在类中包含要进行操作的自定义类型的对象,在类中进行运算符操作。

3.2.1 正向迭代器

template<class T, class Ref, class Ptr>
class ListIterator
{
public:
    typedef ListNode<T> PNode;
    typedef ListIterator<T, Ref, Ptr> Self; 

    PNode* _pNode;

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

    //ListIterator(const Self& l);
    Ref operator*()
    {
        return _pNode->_val;
    }

    Ptr operator->()
    {
        return &_pNode->_val;
    }
    Self& operator++()
    {
        _pNode = _pNode->_pNext;
        return *this;
    }

    Self operator++(int)
    {
        Self tmp(*this);//拷贝构造(浅拷贝)
        _pNode = _pNode->_pNext;
        return tmp;
    }
    Self& operator--()
    {
        _pNode = _pNode->_pPre;
        return *this;
    }
    Self& operator--(int)
    {
        Self tmp(*this);
        _pNode = _pNode->_pPre;
        return tmp;
    }
    bool operator!=(const Self& l)
    {
        return _pNode != l._pNode;
    }
    bool operator==(const Self& l)
    {
        return _pNode == l._pNode;
    }
};
  • 注:这里我们可以看到我们创建ListIterator类的时候使用了类模板,并且看到模板参数中不止一个参数,而是多了两个Ref和Ptr。至于理由是:我们要实现两个版本的迭代器,一个是const和非const版本的,而这两个版本的区别无非就是返回的引用值是否能被修改该,也就是重载*解引用的时候,const版本返回的是const T&,非const版本返回的就是T&,除了这个其他的地方都一样。那如果想两个都实现是不是就要copy一份呢?一下写两个出来呢?所以这个时候模板参数起作用了。定义Ref模板参数,不管是T&还是const T&我们都返回Ref只是当我们想调用const版本的时候就给模板传const T&的类型,我们想调用非cosnt版本的时候就传T&类型的给模板就行,这样就可以避免代码重复问题,提高复用度。
  • 同样的既然我们定义是一个自定义类型的节点,迭代器可以看作是一个指针,有了自定义类型和之指针我们就可以通过指针+ (->)的方式拿到节点里面的val值,但是->同样也有两个版本,所以做法和上面的是一样的,只需要多个模板参数传一个值Ptr就行,具体调用什么版本的就传什么类型的过去就行。
  • 这里同样也要注意的一点就是重载(->)的时候,我们是这样定义的:
    Ptr operator->() { return &_pNode->_val; }
    本来我们使用的时候应该it->->val这样有两个箭头使用,也就是it.operator->()->val这样去使用的,但是编译为了方便使用做了优化只需要一个箭头就可进行访问了。

3.2.2反向迭代器+适配器

适配器:
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总
结),该种模式是将一个类的接口转换成客户希望的另外一个接口
在这里插入图片描述
用简单话来概括适配器就是——用现有的东西适配出一个新的东西。

我们的反向迭代器其实就是用到了适配器的概念,用到就是正向迭代器适配出来的。
在这里插入图片描述

  • rbegin的++就是end的–,rend的–就是begin的++,要取rbegin指向的值就是–end()在解引用得到。
    所以我们只需要讲正向迭代器的类型当作参数传个我们的反向迭代器的类模板参数就行。
  • 同样的这里也需要传三个模板参数,原理和上面是一样的。
template<class Iterator, class Pef, class Ptr>
struct Reverse_iterator
{
	typedef Reverse_iterator<Iterator, Pef, Ptr> self;

	Iterator cur;

	Reverse_iterator(Iterator it)
		:cur(it)
	{}

	Ptr operator->()
	{
		return &(operator*());
	}

	self& operator++()
	{
		--cur;
		return *this;
	}

	self operator++(int)
	{
		Iterator tmp = cur;
		--cur;
		return tmp;
	}

	self& operator--()
	{
		++cur;
		return *this;
	}

	self operator--(int)
	{
		Iterator tmp = cur;
		++cur;
		return tmp;
	}

	Pef operator*()
	{
		Iterator tmp = cur;
		--tmp;
		return *tmp;
	}

	bool operator!=(const self& l)
	{
		return cur != l.cur;
	}

	bool operator==(const self& l)
	{
		return cur == l.cur;
	}
};

3.3 空间控制模块

  1. 返回list容器的大小
size_t size() const
{
    size_t len = 0;
    const_iterator it = begin();//这里要用const_iterator迭代器,this被const修饰了
    while (it != end())
    {
        ++len;
        ++it;
    }
    return len;
}
  1. list容器是否为空
bool empty()const
{
    return begin() == end();
}
  1. resize
void resize(size_t newsize, const T& data = T())
{
	size_t oldsize = size();
	if (newsize <= oldsize)
	{
		// 有效元素个数减少到newsize
		while (newsize < oldsize)
		{
			pop_back();
			oldsize--;
		}
	}
	else
	{
		while (oldsize < newsize)
		{
			push_back(data);
			oldsize++;
		}
	}
}

3.4 数据的访问

  1. 访问头节点的值
T& front()
{
    return (begin()._pNode)->_pNext->_val;
}
const T& front()const
{
    return (begin()._pNode)->_pNext->_val;

}
  1. 访问尾节点的值
T& back()
{
    return (end()._pNode)->_pPre->_val;
}
const T& back()const
{
    return (end()._pNode)->_pPre->_val;
}

3.5 增加/删除数据

  1. 在pos位置前插入值为val的节点
iterator insert(iterator pos, const T& val)
{
    Node* newnode = new Node;
    newnode->_val = val;
    Node* cur = pos._pNode;
    Node* prev = cur->_pPre;

    prev->_pNext = newnode;
    newnode->_pPre = prev;
    newnode->_pNext = cur;
    cur->_pPre = newnode;

    return iterator(newnode);
}
  1. 删除pos位置的节点,返回该节点的下一个位置
iterator erase(iterator pos)
        {
            Node* cur = pos._pNode;
            Node* prev = cur->_pPre;
            Node* next = cur->_pNext;

            prev->_pNext = next;
            next->_pPre = prev;

            return iterator(next);
        }
  1. push_back/pop_back
void push_back(const T& val) 
{
    insert(end(), val); 
}
void pop_back() 
{ 
    erase(--end()); 
}
  1. push_front/pop_front
void push_front(const T& val) 
{ 
    insert(begin(), val); 
}
void pop_front() 
{ 
    erase(begin()); 
}
  1. 清除数据
void clear()
{
    iterator it = begin();
    while (it != end())
    {
        it = erase(it);
    }
}

3.6 构造/拷贝构造/析构

  1. 构造函数
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;
    }
}
  1. 拷贝构造
list(const list<T>& l)
{
    CreateHead();//先创建哨兵位
    const_iterator it = l.begin();
    while (it != l.end())
    {
        push_back(*it);
        ++it;
    }
}
  1. 赋值运算符重载
void swap(list<T>& l)
{
    std::swap(_pHead, l._pHead);
}

list<T>& operator=(list<T> l) 
{
    swap(l);
    return *this;
}
  1. 析构
~list()
{
    //iterator it = begin();
    //while (it != end())
    //{
    //    Node* cur = it._pNode;
    //    _pHead->_pNext = cur->_pNext;
    //    delete cur;
    //}

    clear();
    delete _pHead;
    _pHead = nullptr;
}

4. 整体代码逻辑

namespace qfw
{
    // 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>
    class ListIterator
    {
    public:
        typedef ListNode<T> PNode;
        typedef ListIterator<T, Ref, Ptr> Self; 

        PNode* _pNode;

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

        //ListIterator(const Self& l);
        Ref operator*()
        {
            return _pNode->_val;
        }

        Ptr operator->()
        {
            return &_pNode->_val;
        }
        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return *this;
        }

        Self operator++(int)
        {
            Self tmp(*this);//拷贝构造(浅拷贝)
            _pNode = _pNode->_pNext;
            return tmp;
        }
        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return *this;
        }
        Self& operator--(int)
        {
            Self tmp(*this);
            _pNode = _pNode->_pPre;
            return tmp;
        }
        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l)
        {
            return _pNode == l._pNode;
        }
    };

	template<class Iterator, class Pef, class Ptr>
	struct Reverse_iterator
	{
		typedef Reverse_iterator<Iterator, Pef, Ptr> self;
	
		Iterator cur;
	
		Reverse_iterator(Iterator it)
			:cur(it)
		{}
	
		Ptr operator->()
		{
			return &(operator*());
		}
	
		self& operator++()
		{
			--cur;
			return *this;
		}
	
		self operator++(int)
		{
			Iterator tmp = cur;
			--cur;
			return tmp;
		}
	
		self& operator--()
		{
			++cur;
			return *this;
		}
	
		self operator--(int)
		{
			Iterator tmp = cur;
			++cur;
			return tmp;
		}
	
		Pef operator*()
		{
			Iterator tmp = cur;
			--tmp;
			return *tmp;
		}
	
		bool operator!=(const self& l)
		{
			return cur != l.cur;
		}
	
		bool operator==(const self& l)
		{
			return cur == l.cur;
		}
	};

    //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<const_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();//先创建哨兵位
            const_iterator it = l.begin();
            while (it != l.end())
            {
                push_back(*it);
                ++it;
            }
        }

        void swap(list<T>& l)
        {
            std::swap(_pHead, l._pHead);
        }

        list<T>& operator=(list<T> l) 
        {
            swap(l);
            return *this;
        }
        ~list()
        {
            //iterator it = begin();
            //while (it != end())
            //{
            //    Node* cur = it._pNode;
            //    _pHead->_pNext = cur->_pNext;
            //    delete cur;
            //}

            clear();
            delete _pHead;
            _pHead = nullptr;
        }


        ///
        // List Iterator
        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);
        }

        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 Capacity
        size_t size() const
        {
            size_t len = 0;
            const_iterator it = begin();//这里要用const_iterator迭代器,this被const修饰了
            while (it != end())
            {
                ++len;
                ++it;
            }
            return len;
        }
        bool empty()const
        {
            return begin() == end();
        }

        void resize(size_t newsize, const T& data = T())
        {
            size_t oldsize = size();
            if (newsize <= oldsize)
            {
                // 有效元素个数减少到newsize
                while (newsize < oldsize)
                {
                    pop_back();
                    oldsize--;
                }
            }
            else
            {
                while (oldsize < newsize)
                {
                    push_back(data);
                    oldsize++;
                }
            }
        }

        
        // List Access
        T& front()
        {
            return (begin()._pNode)->_pNext->_val;
        }
        const T& front()const
        {
            return (begin()._pNode)->_pNext->_val;

        }
        T& back()
        {
            return (end()._pNode)->_pPre->_val;
        }
        const T& back()const
        {
            return (end()._pNode)->_pPre->_val;
        }


        
        // 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)
        {
            Node* newnode = new Node;
            newnode->_val = val;
            Node* cur = pos._pNode;
            Node* prev = cur->_pPre;

            prev->_pNext = newnode;
            newnode->_pPre = prev;
            newnode->_pNext = cur;
            cur->_pPre = newnode;

            return iterator(newnode);
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            Node* cur = pos._pNode;
            Node* prev = cur->_pPre;
            Node* next = cur->_pNext;

            prev->_pNext = next;
            next->_pPre = prev;

            return iterator(next);
        }
        void clear()
        {
            iterator it = begin();
            while (it != end())
            {
                it = erase(it);
            }
        }
    private:
        void CreateHead()
        {
            _pHead = new Node;
            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }
        Node* _pHead;
    };

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

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

相关文章

鸿蒙开发(六)布局概述

迄今为止&#xff0c;我还没有正式提到布局的概念。但其实我之前的demo里面&#xff0c;已经默认使用到了一种布局&#xff0c;那就是线性布局&#xff08;Row、Column&#xff09;&#xff0c;这也是DevEco创建项目默认页面里面默认采用的布局。那么本篇&#xff0c;带着大家一…

PyTorch 2.2 中文官方教程(十)

使用整体追踪分析的追踪差异 原文&#xff1a;pytorch.org/tutorials/beginner/hta_trace_diff_tutorial.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 作者: Anupam Bhatnagar 有时&#xff0c;用户需要识别由代码更改导致的 PyTorch 操作符和 CUDA 内核的变化…

python 动态数据 展示 ,数据是由51单片机发送过来的,温度传感器。

import tkinter as tk import randomimport seriallis[] for i in range(50):lis.append(i1) # 打开串行端口 ser serial.Serial(COM3, 9600) # 9600为波特率&#xff0c;根据实际情况进行调整# 初始化数据 lis [random.randint(15, 35) for _ in range(50)]def update_data…

【深度学习:Bard】我们 AI 之旅的重要下一步

【深度学习&#xff1a;AI 之旅】我们 AI 之旅的重要下一步 Bard简介将 AI 的优势带入我们的日常产品中帮助开发人员利用 AI 进行创新大胆负责 人工智能是我们今天正在研究的最深刻的技术。无论是帮助医生更早地发现疾病&#xff0c;还是使人们能够用自己的语言获取信息&#x…

2024年10 个好用的AI简历工具盘点推荐

在职场竞争激烈的今天&#xff0c;一份出色的简历就像是你的秘密武器&#xff0c;能帮你在众多候选人中脱颖而出&#xff0c;赢得面试宝座。随着 ChatGPT 引领的 AI 浪潮席卷而来&#xff0c;各式各样的 AI 简历工具如雨后春笋般涌现。面对这样的背景&#xff0c;神器集今天为大…

立面效果图为何要用云渲染100?渲染100邀请码1a12

建筑设计是一门艺术&#xff0c;而立面效果图是艺术的展现&#xff0c;它在设计中非常重要。 1、立面效果图的重要性 立面效果图能用来展示建筑物的风格、材质、色彩以及环境等因素&#xff0c;通过它&#xff0c;设计师可以检验项目质量&#xff0c;评估效果是否达到预期&…

【开源】基于JAVA+Vue+SpringBoot的课程案例资源库系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员需求分析2.2 用户需求分析 三、系统设计3.1 业务流程设计3.1.1 管理员业务流程设计3.1.2 用户业务流程设计3.1.3 首页功能模块及业务流程分析3.1.4 案例资源中心功能模块及业务流程分析3.1.5 用户信息中心功能模块…

C语言笔试题之求出二叉树的最大深度(递归解决)

实例要求&#xff1a; 1、给定一个二叉树 root &#xff0c;返回其最大深度&#xff1b;2、二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数&#xff1b; 案例展示&#xff1a; 实例分析&#xff1a; 1、判断根节点是否为空&#xff1b;2、分别递归处理左…

Python环境下基于指数退化模型和LSTM自编码器的轴承剩余寿命预测

滚动轴承是机械设备中关键的零部件之一&#xff0c;其可靠性直接影响了设备的性能&#xff0c;所以对滚动轴承的剩余使用寿命(RUL)进行预测是十分必要的。目前&#xff0c;如何准确地对滚动轴承剩余使用寿命进行预测&#xff0c;仍是一个具有挑战的课题。对滚动轴承剩余寿命评估…

C语言中的数据类型-强转

强制类型转换 概念&#xff1a;将某种类型的数据转化我们需要的数据类型&#xff0c;注意强制类型转化是临时强转&#xff0c;不会改变本身的数据类型。 强转又分为显式强转和隐式转化 显示强转是按照我们的要求进行转化 格式&#xff1a;(需要转化数据类型)变量名 #inclu…

VUE学习——事件处理

事件分为内联事件和方法事件。 我们可以使用【v-on】&#xff08;简写&#xff1a;&#xff09;来处理。 内联 <button v-on:click"count">按钮</button><button click"count">按钮</button><p>{{ count }}</p>方法

YouTrack 用户登录提示 JIRA 错误

就算输入正确的用户名和密码&#xff0c;我们也得到了下面的错误信息&#xff1a; youtrack Cannot retrieve JIRA user profile details. 解决办法 出现这个问题是因为 YouTrack 在当前的系统重有 JIRA 的导入关联。 需要把这个导入关联取消掉。 找到后台配置的导入关联&a…

图灵日记之java奇妙历险记--抽象类和接口

目录 抽象类概念抽象类语法 接口概念规则使用特性实现多个接口接口的继承接口使用实例Clonable接口和深拷贝抽象类和接口的区别 Object类 抽象类 概念 在面向对象的概念中,所有对象都是通过类来描述的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够…

java SpringBoot2.7整合Elasticsearch(ES)7 带条件分页查询与不带条件分页查询演示讲解

上文 java SpringBoot2.7整合Elasticsearch(ES)7 进行文档增删查改 我们带着大家 整合了 Elasticsearch 对索引中的文档做了 各方面操作 然后 我们来说说 分页查询 这里 为了方便大家看 我加了五条数据进去 这里 我们仍然需要带个条件 这里 我们用name Mapper 接口 加一个这…

【开源】SpringBoot框架开发桃花峪滑雪场租赁系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 游客服务2.2 雪场管理 三、数据库设计3.1 教练表3.2 教练聘请表3.3 押金规则表3.4 器材表3.5 滑雪场表3.7 售票表3.8 器材损坏表 四、系统展示五、核心代码5.1 查询教练5.2 教练聘请5.3 查询滑雪场5.4 滑雪场预定5.5 新…

高职单招怎么搜答案? #经验分享#微信#笔记

当今社会&#xff0c;随着信息技术的迅猛发展&#xff0c;大学生们在学习过程中面临着各种各样的困难和挑战。而在这些挑战中&#xff0c;面对繁重的作业和复杂的题目&#xff0c;大学生搜题软件应运而生 1.题老大 这是一个公众号 亿级数量题库&#xff0c;可截图搜题&#…

戴上HUAWEI WATCH GT 4,解锁龙年新玩法

春节将至&#xff0c;华为WATCH GT 4作为一款颜值和实力并存的手表&#xff0c;能为节日增添了不少趣味和便利。无论你是钟情于龙年表盘或定制属于自己的表盘&#xff0c;还是过年用来抢红包或远程操控手机拍全家福等等&#xff0c;它都能成为你的“玩伴”。接下来&#xff0c;…

ArcGIS学习(四)坐标系-1

ArcGIS学习(四)坐标系 大家平时在处理数据的时候肯定经常遇到坐标系相关的问题。最常见的就是同一个地区的两个数据,导入ArcGIS内却对不上;也肯定听到过坐标系相关的一些词语,比如地理坐标系投影坐标系、投影、WGS1984坐标、CGCS2000坐标系、火星坐标系、百度坐标系等。 …

C#在设备数据采集中的应用

设备数据采集在现代工业生产中扮演着至关重要的角色。随着工业互联网的发展&#xff0c;设备数据采集技术已经成为了智能制造的基础之一。在这篇文章中&#xff0c;我们将探讨C#语言在设备数据采集中的应用。 首先&#xff0c;让我们来了解一下设备数据采集的概念。设备数据采集…

linux 07 存储管理

02. ext4是一种索引文件系统 上面是索引节点inode&#xff0c;存放数据的元数据 下面是存储块block&#xff0c;主要存放有关的信息 03.linux上的inode 查看文件中的inode ll -i 文件名 磁盘中的inode与文件数量 向sdb2中写文件&#xff1a; 结果&#xff1a; df -i 磁…