Uno de los primeros artículos de C++ te enseña la lista (implementación de simulación)

inserte la descripción de la imagen aquí

Implementación de simulación de lista

tabla de tipos de miembros
inserte la descripción de la imagen aquí

Esta tabla enumera algunas definiciones de tipo de miembro del contenedor de lista en la biblioteca estándar de C++. Estas definiciones de tipos son para permitir que la lista funcione con otros componentes de la biblioteca estándar de C++ y para proporcionar algunas interfaces estándar comunes. La utilidad de cada tipo de miembro:

value_type: este tipo de miembro representa el tipo de datos almacenado en el contenedor de lista, es decir, el tipo del parámetro de plantilla T.

allocator_type: este tipo de miembro representa el tipo de asignador utilizado para la asignación y administración de memoria para el contenedor. De forma predeterminada, se usa allocator<value_type>como asignador.

reference: este tipo de miembro representa el tipo de referencia al elemento. Para el asignador predeterminado, es value_type&, es decir, una referencia al elemento.

const_reference: este tipo de miembro representa un tipo de referencia a un elemento constante. Para el asignador predeterminado, es const value_type&, es decir, una referencia al elemento constante.

pointer: este tipo de miembro representa el tipo de puntero al elemento. Para el asignador predeterminado, es value_type*, es decir, un puntero al elemento.

const_pointer: este tipo de miembro representa el tipo de puntero al elemento constante. Para el asignador predeterminado, es const value_type*, es decir, un puntero a un elemento constante.

iterator: este tipo de miembro es un tipo de iterador bidireccional que se puede usar para recorrer los elementos del contenedor. Se puede convertir implícitamente a const_iterator, lo que permite modificar los elementos durante el recorrido.

const_iterator: este tipo de miembro también es un tipo de iterador bidireccional, que se utiliza para atravesar los elementos del contenedor constante y no permite la modificación de los elementos.

reverse_iterator: este tipo de miembro es iteratorun iterador inverso que puede atravesar desde el final del contenedor hasta la cabeza.

const_reverse_iterator: este tipo de miembro es const_iteratorun iterador inverso para atravesar desde el final del contenedor de constantes hasta la cabeza.

difference_type: este tipo de miembro representa la distancia entre dos iteradores, generalmente se usa ptrdiff_t, es el mismo que el tipo de diferencia del puntero.

size_type: este tipo de miembro representa el tipo de entero no negativo, que generalmente se usa size_tpara representar el tamaño del contenedor.

La definición de estos tipos de miembros permite que el contenedor de listas interactúe con otros componentes de la biblioteca estándar de C++ y código definido por el usuario, para lograr funciones más generales y flexibles.

definición de estructura de nodo list_node

Defina la estructura de nodos de la lista vinculada list_node, que se utiliza para almacenar cada elemento en la lista vinculada. Vamos a explicar qué significa cada parte una por una.

template<class T>
struct list_node
{
    
    
    T _data;            // 存储节点的数据
    list_node<T>* _next;  // 指向下一个节点的指针
    list_node<T>* _prev;  // 指向前一个节点的指针

    // 构造函数
    list_node(const T& x = T())
        :_data(x)        // 使用参数 x 初始化 _data
        , _next(nullptr) // 初始化 _next 为 nullptr
        , _prev(nullptr) // 初始化 _prev 为 nullptr
    {
    
    }
};

T _data;: Los datos almacenados en el nodo, el tipo es un parámetro de plantilla T, que puede ser cualquier tipo de datos.

list_node<T>* _next;: Puntero al siguiente nodo, usado para construir la estructura de la lista enlazada. Establecido inicialmente nullptrpara indicar que el nodo actual no tiene un nodo sucesor.

list_node<T>* _prev;: Un puntero al nodo anterior, también utilizado para construir una estructura de lista enlazada. Establecido inicialmente nullptrpara indicar que el nodo actual no tiene un nodo predecesor.

Constructor list_node(const T& x = T()): El constructor puede recibir un parámetro xpara inicializar los datos del nodo. Si no se pasan parámetros, se construye un nodo vacío de forma predeterminada.

A través de esta estructura de nodos, puede crear una lista doblemente enlazada list, almacenar diferentes tipos de datos en los nodos y conectar los nodos para construir la estructura de la lista enlazada. Esta estructura de nodos proporciona la base para la implementación de listas enlazadas.

std::__reverse_iterator implementación del iterador inverso

#pragma once

namespace xzq
{
    
    
	// 复用,迭代器适配器
	template<class Iterator, class Ref, class Ptr>
	struct __reverse_iterator
	{
    
    
		Iterator _cur;
		typedef __reverse_iterator<Iterator, Ref, Ptr> RIterator;

		__reverse_iterator(Iterator it)
			:_cur(it)
		{
    
    }

		RIterator operator++()
		{
    
    
			--_cur;
			return *this;
		}

		RIterator operator--()
		{
    
    
			++_cur;
			return *this;
		}

		Ref operator*()
		{
    
    
			auto tmp = _cur;
			--tmp;
			return *tmp;
		}

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

		bool operator!=(const RIterator& it)
		{
    
    
			return _cur != it._cur;
		}
	};
}

ilustrar:

__reverse_iteratorLa plantilla de clase define un iterador inverso con tres parámetros de plantilla: Iterator( tipo de iterador ), Ref( tipo de referencia ) y Ptr( tipo de puntero ).
_curEs una variable miembro privada que ocupa la posición del iterador actual.
El constructor toma un iterador directo como parámetro y lo almacena en _curuna variable miembro.
operator++()El operador de incremento está sobrecargado para hacer avanzar el iterador una posición.
operator--()El operador de decremento está sobrecargado para mover el iterador una posición hacia atrás.
operator*()Sobrecarga el operador de desreferencia para devolver una referencia al elemento señalado por el iterador inverso.
operator->()El operador de acceso a miembros está sobrecargado para devolver un puntero al elemento señalado por el iterador inverso.
operator!=()El operador no igual está sobrecargado para determinar si dos iteradores inversos no son iguales.

Este iterador inverso se puede usar para admitir la iteración desde la cola del contenedor hasta la cabeza. En la biblioteca estándar de C++, std::reverse_iterator se usa para implementar una funcionalidad similar.

Este código implementa un adaptador iterador. Un adaptador de iterador es un contenedor que proporciona diferentes funciones basadas en iteradores existentes, lo que permite que los iteradores originales se adapten a nuevos escenarios de uso.

__reverse_iteratores un adaptador iterador inverso. Encapsula un iterador directo, pero al sobrecargar a los operadores, etc., puede iterar desde el final del contenedor hasta el encabezado. Dicho adaptador puede hacer que el iterador hacia adelante original sea más conveniente cuando se viaja hacia atrás.listEs necesario utilizar la función de iterador inverso en la siguiente implementación de simulación. Por supuesto, también se puede aplicar a la implementación de simulación de otros contenedores. Dependiendo del escenario, no se pueden aplicar todos los contenedores.

iterador de lista __list_iterator definición

Iterator __list_iterator, usado para recorrer los nodos en la lista enlazada. Vamos a explicar qué significa cada parte una por una.

template<class T, class Ref, class Ptr>
struct __list_iterator
{
    
    
    
	typedef list_node<T> Node;  // 定义一个类型别名 Node,用于表示 list 节点的类型

	typedef __list_iterator<T, Ref, Ptr> iterator;  // 定义一个类型别名 iterator,用于表示 list 迭代器的类型

	typedef bidirectional_iterator_tag iterator_category;  // 定义迭代器的分类,这里是双向迭代器
	
	typedef T value_type;  // 定义元素的类型,即模板参数 T

	typedef Ptr pointer;  // 定义指向元素的指针类型,用于迭代器

	typedef Ref reference;  // 定义对元素的引用类型,用于迭代器

	typedef ptrdiff_t difference_type;  // 定义表示迭代器之间距离的类型

    Node* _node;  // 指向链表中的节点

    __list_iterator(Node* node)
        :_node(node)  // 初始化迭代器,指向给定的节点
    {
    
    }

    bool operator!=(const iterator& it) const
    {
    
    
        return _node != it._node;  // 比较两个迭代器是否不相等
    }

    bool operator==(const iterator& it) const
    {
    
    
        return _node == it._node;  // 比较两个迭代器是否相等
    }

    Ref operator*()
    {
    
    
        return _node->_data;  // 返回当前节点的数据
    }

    Ptr operator->()
    {
    
     
        return &(operator*());  // 返回当前节点数据的地址
    }

    // ++it
    iterator& operator++()
    {
    
    
        _node = _node->_next;  // 迭代器自增,指向下一个节点
        return *this;
    }
    
    // it++
    iterator operator++(int)
    {
    
    
        iterator tmp(*this);  // 创建一个临时迭代器保存当前迭代器
        _node = _node->_next;  // 迭代器自增,指向下一个节点
        return tmp;  // 返回保存的临时迭代器
    }

    // --it
    iterator& operator--()
    {
    
    
        _node = _node->_prev;  // 迭代器自减,指向前一个节点
        return *this;
    }

    // it--
    iterator operator--(int)
    {
    
    
        iterator tmp(*this);  // 创建一个临时迭代器保存当前迭代器
        _node = _node->_prev;  // 迭代器自减,指向前一个节点
        return tmp;  // 返回保存的临时迭代器
    }
};

Esta estructura de iterador proporciona la capacidad para que las listas enlazadas atraviesen nodos. Se puede utilizar para recorrer cada elemento de la lista vinculada, lo que proporciona un comportamiento similar al de un puntero, lo que facilita recorrer la lista vinculada.

Aquí hay una mención de la clasificación de los tipos de iteradores:

Los iteradores de la biblioteca estándar de C++ se dividen en cinco tipos principales, cada uno de los cuales proporciona una funcionalidad diferente y operaciones admitidas. Estos tipos son:

Iterador de entrada (Input Iterator) : solo se permite leer elementos del contenedor, pero no puede modificar los elementos en el contenedor. Solo se admiten operaciones como mover uno por uno, desreferenciar, comparar y comparar por igualdad. Los iteradores de entrada a menudo se usan en algoritmos como std::find().

Iterador de salida : solo se permite escribir elementos en el contenedor, pero no leer los elementos en el contenedor. Se admiten operaciones como mover elementos uno por uno y escribir elementos uno por uno.

Iterador de avance (Iterador de avance) : similar al iterador de entrada, pero admite varias desreferencias. Los iteradores directos se pueden usar para operaciones que requieren múltiples iteraciones, como recorrer una lista enlazada individualmente.

Iterador bidireccional (Iterador bidireccional) : basado en el iterador hacia adelante, agrega la capacidad de atravesar hacia adelante. Además de admitir operaciones en iteradores de entrada, también se admiten operaciones como avanzar y mover elementos uno por uno.

Iterador de acceso aleatorio (Random Access Iterator) : es el tipo de iterador más poderoso. Además de admitir todas las operaciones de iteradores directos e iteradores bidireccionales, se admiten operaciones aritméticas similares a punteros, como suma, resta y acceso aleatorio a elementos en contenedores. Los iteradores de acceso aleatorio a menudo se usan en estructuras de datos que admiten el acceso aleatorio, como matrices.

En el código anterior, typedef bidirectional_iterator_tag iterator_category; significa que este iterador es un tipo de iterador bidireccional, por lo que debe admitir todas las operaciones de iteradores directos y bidireccionales, incluido el movimiento, la eliminación de referencias, la comparación, etc. Esto es para garantizar que este iterador listfuncione correctamente al atravesar elementos contenedores de la clase.

Definición de miembro de clase de lista

El siguiente código es la parte de definición de clase de la lista de clases de plantilla de lista doblemente enlazada en C++. Esta clase define algunos tipos de miembros públicos y variables de miembros privados para admitir operaciones de listas vinculadas. Vamos a explicar qué significa cada parte una por una.

template<class T>
class list
{
    
    
    typedef list_node<T> Node;
public:
    // 定义迭代器类型
    typedef __list_iterator<T, T&, T*> iterator;
    typedef __list_iterator<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;

private:
    Node* _head;
};

typedef list_node<T> Node;: Defina un alias Nodeque represente el tipo de nodo de la lista enlazada list_node<T>.

typedef __list_iterator<T, T&, T*> iterator;: define el tipo de iterador directo de la lista enlazada iterator, utiliza __list_iterator la plantilla de estructura y especifica el tipo de parámetro correspondiente.

typedef __list_iterator<T, const T&, const T*> const_iterator;: Define el tipo de iterador de avance constante de la lista enlazada const_iterator, que se utiliza para recorrer la lista enlazada sin modificar los elementos de la lista enlazada.

typedef __reverse_iterator<iterator, T&, T*> reverse_iterator;: Define el tipo de iterador inverso de la lista enlazada reverse_iterator, que va desde el final de la lista enlazada hasta el principio de la lista enlazada.

typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;: Define un tipo de iterador inverso constante para listas enlazadas const_reverse_iterator.

Node* _head;: La variable miembro privada de la lista vinculada, que apunta al nodo principal de la lista vinculada.

El código de esta parte define listlos tipos de miembros públicos y las variables de miembros privados de la clase, proporcionando una base para realizar el funcionamiento y la gestión de la lista enlazada.

definición de función de miembro de lista

1.begin()、end()、rbegin()和rend()

const_iterator begin() const
		{
    
    
			return const_iterator(_head->_next);
		}

		const_iterator end() const
		{
    
    
			return const_iterator(_head);
		}

		iterator begin()
		{
    
    
			return iterator(_head->_next);
		}

		iterator end()
		{
    
    
			return iterator(_head);
		}

		reverse_iterator rbegin()
		{
    
    
			return reverse_iterator(end());
		}

		reverse_iterator rend()
		{
    
    
			return reverse_iterator(begin());
		}

Esta parte del código trata sobre listla definición de la función miembro iteradora de la clase. Se utilizan para devolver diferentes tipos de iteradores, lo que permite al usuario iterar sobre listlos elementos del contenedor.

const_iterator begin() const: esta es una función miembro constante que devuelve un iterador constante que apunta al primer elemento (nodo) en el contenedor. Dado que es un iterador constante, no se permite modificar los elementos del contenedor a través de él.

const_iterator end() const: Esta es también una función miembro const que devuelve un iterador const que apunta a la posición "posterior a la cola" al final del contenedor, es decir, la posición de un elemento que no existe. Los iteradores constantes no se pueden utilizar para modificar elementos.

iterator begin(): esta es una función miembro no constante que devuelve un iterador no constante que apunta al primer elemento (nodo) en el contenedor. Permite modificar los elementos del contenedor a través de este iterador.

iterator end(): Esta también es una función miembro no constante que devuelve un iterador no constante que apunta a la posición "posterior a la cola" al final del contenedor, es decir, la posición de un elemento que no existe. Los iteradores que no son constantes se pueden usar para modificar elementos.

reverse_iterator rbegin(): Esta es una función que devuelve un iterador inverso. Pasa end()el iterador de como parámetro al reverse_iteratorconstructor, devolviendo así un iterador inverso que apunta al final del contenedor.

reverse_iterator rend(): Esta es una función que devuelve un iterador inverso.Pasa begin()el iterador de al reverse_iteratorconstructor, devolviendo así un iterador inverso que apunta al principio del contenedor.

El propósito de estas funciones es facilitar a los usuarios atravesar elementos contenedores en diferentes situaciones, incluido el recorrido hacia adelante y el recorrido inverso, y el uso de iteradores constantes o no constantes. A través de estos iteradores, los usuarios pueden acceder fácilmente a los elementos del contenedor de listas y manipularlos.

2.empty_init()

void empty_init()
{
    
    
    _head = new Node; // 创建一个新的节点作为链表头部
    _head->_next = _head; // 将头部节点的下一个指针指向自身,表示链表为空
    _head->_prev = _head; // 将头部节点的前一个指针也指向自身,表示链表为空
}

Esta es listuna función miembro privada de la clase empty_init()que inicializa una lista enlazada vacía.

_headEl propósito de esta función es inicializar el puntero correcto para el nodo principal al crear una nueva lista enlazada vacía, de modo que la lista enlazada esté vacía. El nodo principal de la lista vinculada _headse utiliza para identificar la posición inicial y la posición final de la lista vinculada.

En esta función, primero newcree un nuevo nodo como encabezado de la lista vinculada a través del operador. Luego, tanto _nextel puntero como _prevel puntero del nodo principal apuntan a sí mismos, lo que indica que la lista enlazada está vacía. En este caso, el nodo principal de la lista enlazada es tanto el primer nodo como el nodo final, formando una estructura de lista enlazada circular.

En listel constructor de la clase, empty_init()cree una lista enlazada vacía llamando a . Esto proporciona una base correcta para operaciones como la inserción y eliminación de la lista enlazada en operaciones posteriores.

3. Definición de constructor

template <class InputIterator>  
list(InputIterator first, InputIterator last)
{
    
    
    empty_init(); // 初始化一个空链表

    while (first != last)
    {
    
    
        push_back(*first); // 将当前迭代器指向的元素添加到链表尾部
        ++first; // 移动迭代器到下一个元素
    }
}

listPlantilla de constructor para una clase que toma un rango de elementos y agrega esos elementos a una lista vinculada.
Este constructor toma un parámetro de plantilla InputIteratorque representa el tipo del iterador de entrada. De esta forma, el constructor puede aceptar diferentes tipos de iteradores, como punteros ordinarios, listiteradores de , etc.

En el constructor, llame primero empty_init()a la función para inicializar una lista enlazada vacía para asegurarse de que el nodo que encabeza la lista enlazada _headse inicialice correctamente.

Luego, use un ciclo para iterar sobre los elementos dentro del rango del iterador de entrada. En cada ciclo, push_back()agregue el elemento señalado por el iterador actual al final de la lista vinculada a través de la función. Posteriormente, el iterador avanza una posición para que se pueda procesar el siguiente elemento.

list()
{
    
    
    empty_init(); // 初始化一个空链表
}

Este es listel constructor sin argumentos de la clase, que se usa para crear una lista enlazada vacía.

Este constructor no acepta ningún parámetro, simplemente inicializa el nodo como una lista enlazada vacía cuando se crea el objeto _head. Llamar empty_init()a la función creará una _headlista vinculada que contiene solo nodos, y el _nexty del nodo _prevapuntan a sí mismos, lo que indica que la lista vinculada está vacía.

list(const list<T>& lt)
{
    
    
    empty_init(); // 初始化一个空链表

    list<T> tmp(lt.begin(), lt.end()); // 使用迭代器从 lt 创建一个临时链表
    swap(tmp); // 交换临时链表和当前链表,完成拷贝操作
}

Este es el constructor de copia de la clase que se utiliza para crear una nueva lista vinculada que es idéntica lista otra lista vinculada ya existentelt

En este constructor, empty_init()primero se inicializa una lista enlazada vacía llamando a la función y luego se crea una lista enlazada temporal.Los tmpelementos de esta lista enlazada temporal son los mismos que los elementos de la lista enlazada y se crean desde el ámbito de ltel iterador ltLuego, swap()la lista enlazada actual se tmpintercambia con la lista enlazada temporal llamando a la función, realizando así la copia de la lista enlazada.

Este método puede lograr el efecto de la construcción de copias, porque en swap()la función, tmplos recursos de la lista enlazada temporal se transferirán a la lista enlazada actual, y la lista enlazada temporal tmpse destruirá, realizando así la copia del contenido de la lista enlazada.

4.intercambiar

void swap(list<T>& x)
{
    
    
    std::swap(_head, x._head); // 交换两个链表的头结点指针
}

Esta es listuna función miembro de la clase swapque se usa para intercambiar el contenido de dos listas enlazadas.

En esta función, std::swapel puntero del nodo principal de la lista enlazada actual se intercambia _headcon xel puntero del nodo principal de otra lista enlazada llamando a la función de biblioteca estándar. _headEsta operación hará que se intercambien los contenidos de las dos listas enlazadas, pero los elementos de la lista enlazada en realidad no se copian, sino que se intercambia la estructura de la lista enlazada.

Esta operación de intercambio generalmente se usa cuando se necesita intercambiar el contenido de dos objetos, y puede realizar el intercambio de contenido entre dos objetos sin copiar datos, mejorando así la eficiencia.

Cabe señalar que esta función solo intercambia el puntero del nodo principal de la lista vinculada, pero el orden de los elementos en la lista vinculada no cambia.

5. Definición de destructor

~list()
{
    
    
    clear();         // 清空链表中的所有元素
    delete _head;    // 删除头结点
    _head = nullptr; // 将头结点指针置为空指针
}

Este es listel destructor de la clase, usado para destruir el objeto de la lista enlazada.

En este destructor, primero se llama a la función miembro clear()para borrar todos los elementos en la lista vinculada para garantizar que todos los recursos se liberen antes de eliminar la lista vinculada. Luego, use deletela palabra clave para liberar la memoria del nodo principal. Finalmente, configure el puntero del nodo principal _headcomo un puntero nulo para evitar punteros salvajes.

De esta forma, cuando se destruya el objeto de la lista enlazada, la memoria que ocupa se liberará correctamente, evitando pérdidas de memoria.

6.claro()

void clear()
{
    
    
    iterator it = begin(); // 获取链表的起始迭代器
    while (it != end())    // 遍历链表
    {
    
    
        it = erase(it);    // 使用 erase() 函数删除当前元素,并将迭代器指向下一个元素
    }
}

Esta es una función miembro listde la clase clear(), que se usa para borrar todos los elementos en la lista enlazada.

En esta función, primero obtenga el iterador de inicio de la lista vinculada begin()y luego recorra todos los elementos de la lista vinculada a través de un bucle. Dentro del bucle, erase()se llama a la función para eliminar el elemento señalado por el iterador actual y actualizar el iterador para que apunte al elemento junto al elemento eliminado. Esto garantiza que todos los elementos de la lista vinculada se eliminen uno por uno.

Cabe señalar que erase()la función devuelve un iterador que apunta al siguiente elemento después de eliminar un elemento, por lo que es necesario actualizar el iterador en cada iteración del bucle para continuar recorriendo la lista enlazada correctamente.

En resumen, esta clear()función se utiliza para liberar todos los elementos de la lista enlazada y garantizar que la lista enlazada se convierta en una lista enlazada vacía.

7. push_back

void push_back(const T& x)
{
    
    
    Node* tail = _head->_prev; // 获取当前链表的末尾节点
    Node* newnode = new Node(x); // 创建一个新节点,保存新元素 x

    tail->_next = newnode; // 更新末尾节点的下一个指针,指向新节点
    newnode->_prev = tail; // 新节点的前一个指针指向原末尾节点
    newnode->_next = _head; // 新节点的下一个指针指向头节点
    _head->_prev = newnode; // 头节点的前一个指针指向新节点,完成插入操作
}

Esta es una función miembro listde la clase push_back()que se usa para agregar un nuevo elemento al final de la lista enlazada.

En esta función, primero obtenga el nodo final de la lista vinculada actual (el nodo final _prevapunta al último elemento de la lista vinculada), luego cree un nuevo nodo newnodey xalmacene el nuevo elemento en el nuevo nodo. Luego, actualice _nextel puntero del nodo final para que apunte al nuevo nodo y luego actualice _prevel puntero del nuevo nodo para que apunte al nodo final original. Al mismo tiempo, apunte el puntero del nuevo nodo _nextal nodo principal para completar la conexión de la lista circular enlazada. Finalmente, actualice el puntero del nodo principal _prevpara que apunte al nuevo nodo para asegurarse de que la conexión de la lista vinculada esté completa.

Cabe señalar que esta función agrega un nuevo elemento al final de la lista enlazada y no afecta las posiciones relativas de otros elementos.

8.push_front

void push_front(const T& x)
{
    
    
    insert(begin(), x); // 调用 insert 函数,在头部插入新元素 x
}

Esta función simplemente llama inserta la función para xinsertar el nuevo elemento al principio de la lista enlazada.

9.insertar

iterator insert(iterator pos, const T& x)
{
    
    
    Node* cur = pos._node; // 获取当前位置的节点
    Node* prev = cur->_prev; // 获取当前位置节点的前一个节点

    Node* newnode = new Node(x); // 创建一个新节点,保存新元素 x

    prev->_next = newnode; // 更新前一个节点的下一个指针,指向新节点
    newnode->_prev = prev; // 新节点的前一个指针指向前一个节点
    newnode->_next = cur; // 新节点的下一个指针指向当前位置节点
    cur->_prev = newnode; // 当前位置节点的前一个指针指向新节点,完成插入操作

    return iterator(newnode); // 返回指向新节点的迭代器
}

En insertla función, primero obtenga el nodo de posición actual posy su nodo anterior prev, luego cree un nuevo nodo newnodey xalmacene el nuevo elemento en el nuevo nodo. A continuación, actualice el puntero del nodo anterior _nextpara que apunte al nuevo nodo y luego actualice _prevel puntero del nuevo nodo para que apunte al nodo anterior. Al mismo tiempo, apunte _nextel puntero del nuevo nodo al nodo de posición actual curpara completar la operación de inserción. Finalmente, actualice el puntero del nodo en la posición actual _prevpara que apunte al nuevo nodo para asegurarse de que la conexión de la lista vinculada esté completa. Finalmente, la función devuelve un iterador que apunta al nuevo nodo, que representa la posición después de que se completa la operación de inserción.

10.borrar

iterator erase(iterator pos)
{
    
    
    assert(pos != end()); // 断言:确保 pos 不等于链表的结束迭代器

    Node* cur = pos._node; // 获取当前位置的节点
    Node* prev = cur->_prev; // 获取当前位置节点的前一个节点
    Node* next = cur->_next; // 获取当前位置节点的后一个节点

    prev->_next = next; // 更新前一个节点的下一个指针,指向后一个节点
    next->_prev = prev; // 更新后一个节点的前一个指针,指向前一个节点
    delete cur; // 删除当前位置的节点

    return iterator(next); // 返回指向后一个节点的迭代器,表示删除操作完成后的位置
}

Esta parte del código implementa la función de eliminar el elemento en la posición especificada de la lista vinculada.

En erasela función, primero se usa una aserción para garantizar que la posición de eliminación posno sea el iterador final de la lista vinculada. Luego, obtenga el nodo de posición actual pos, su nodo anterior prevy el siguiente nodo next. Luego, actualice el puntero del nodo anterior _nextpara que apunte al siguiente nodo y actualice _prevel puntero del último nodo para que apunte al nodo anterior. De esta forma, el nodo de posición actual se desconecta de la lista enlazada. Finalmente, libere el espacio de memoria del nodo en la posición actual y devuelva un iterador que apunte al siguiente nodo, indicando la posición después de que se complete la operación de eliminación.

11. pop_back() y pop_front()

void pop_back()
{
    
    
    erase(--end()); // 通过 erase 函数删除链表末尾的元素
}

pop_backeraseLa función realiza la operación de eliminar un elemento del final de la lista enlazada moviendo el iterador final de la lista enlazada una posición hacia adelante y luego llamando a la función para eliminar el elemento en esta posición.

void pop_front()
{
    
    
    erase(begin()); // 通过 erase 函数删除链表头部的元素
}

pop_frontLa función llama directamente erasea la función para eliminar el elemento en la cabeza de la lista enlazada, realizando la operación de eliminar un elemento de la cabeza de la lista enlazada.

Estas dos funciones corresponden a la operación de borrar elementos del final y del encabezado de la lista enlazada respectivamente, y erasela operación de borrado se completa llamando a la función, manteniendo así la conectividad de la lista enlazada.

12.vacío(), tamaño(), frontal() y posterior()

bool list<T>::empty() const
{
    
    
    return begin() == end(); // 判断 begin() 是否等于 end() 来确定是否为空
}

typename list<T>::size_t list<T>::size() const
{
    
    
    size_t count = 0;
    for (const_iterator it = begin(); it != end(); ++it)
    {
    
    
        ++count;
    }
    return count; // 遍历链表来计算元素个数
}

typename list<T>::reference list<T>::front()
{
    
    
    assert(!empty()); // 如果链表为空,则抛出断言
    return *begin(); // 返回链表的第一个元素
}

typename list<T>::const_reference list<T>::front() const
{
    
    
    assert(!empty()); // 如果链表为空,则抛出断言
    return *begin(); // 返回链表的第一个元素
}

typename list<T>::reference list<T>::back()
{
    
    
    assert(!empty()); // 如果链表为空,则抛出断言
    return *(--end()); // 返回链表的最后一个元素
}

typename list<T>::const_reference list<T>::back() const
{
    
    
    assert(!empty()); // 如果链表为空,则抛出断言
    return *(--end()); // 返回链表的最后一个元素
}

El código anterior implementa empty()la función para juzgar si la lista enlazada está vacía, size()la función para obtener el número de elementos de la lista enlazada, front()la función para obtener el primer elemento de la lista enlazada y back()la función para obtener el último elemento de la lista enlazada . Tenga en cuenta que las aserciones en la función se utilizan para garantizar que no se realicen operaciones ilegales si la lista vinculada está vacía.

13.cambiar tamaño

template<class T>
void list<T>::resize(size_t n, const T& val)
{
    
    
    if (n < size()) {
    
    
        iterator it = begin();
        std::advance(it, n); // 定位到新的尾部位置
        while (it != end()) {
    
    
            it = erase(it); // 从尾部截断,删除多余的元素
        }
    }
    else if (n > size()) {
    
    
        insert(end(), n - size(), val); // 插入足够数量的默认值元素
    }
}

Si nes menor que el tamaño de la lista vinculada actual size(), significa que desea reducir el tamaño de la lista vinculada. En este caso, la función itera a través de la lista enlazada, eliminando los elementos redundantes desde el final, hasta que el tamaño de la lista enlazada sea igual a n.

Primero, la función se usará para obtener begin()el iterador de inicio de la lista vinculada y luego usará std::advancela función para mover el iterador hacia adelante npor posiciones para que apunte a la nueva posición final.

A continuación, la función usa erasela función para eliminar todos los elementos desde la nueva posición final hasta el final de la lista vinculada, reduciendo el tamaño de la lista vinculada a n.

Si nes mayor que el tamaño de la lista vinculada actual, significa que desea aumentar el tamaño de la lista vinculada. En este caso, la función insertará una cantidad suficiente de valelementos con valor al final de la lista enlazada hasta que el tamaño de la lista enlazada sea igual a n.

La función usará la función para insertar elementos con un valor insertal final de la lista vinculada , aumentando así el tamaño de la lista vinculada a .n - size()valn

14. Sobrecarga del operador de asignación

list<T>& operator=(list<T> lt)
{
    
    
    swap(lt); // 交换当前对象和传入对象的内容
    return *this; // 返回当前对象的引用
}

Esta es una función de sobrecarga del operador de asignación, que utiliza la técnica de copia e intercambio (Copy and Swap) para lograr la operación de asignación.

Esta es una función miembro utilizada para asignar un list<T> objeto de un tipo al objeto actual.
El parámetro lt se pasa por valor, por lo que list<T>se llama al constructor de copia para crear una copia temporal.
Luego, intercambie los contenidos del objeto actual y la copia temporal swap(lt)llamando a la función. swapDespués del intercambio, los datos de la copia temporal se asignarán al objeto actual y la copia temporal se destruirá. Dado que el intercambio es una operación eficiente, se puede evitar una gran cantidad de copias de datos.
Finalmente, la función devuelve una referencia al objeto actual para admitir operaciones de asignación continua.
Este enfoque no solo evita la molestia de administrar manualmente la memoria, sino que también garantiza la seguridad excepcional, ya que la copia temporal se destruye correctamente al final de la función.

Simulación de lista para realizar todos los códigos

#pragma once

#include <assert.h>
#include <iostream>

namespace xzq
{
    
    
	template<class T>
	struct list_node
	{
    
    
		T _data;
		list_node<T>* _next;
		list_node<T>* _prev;

		list_node(const T& x = T())
			:_data(x)
			, _next(nullptr)
			, _prev(nullptr)
		{
    
    }
	};

	template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
    
    
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> iterator;

		typedef bidirectional_iterator_tag iterator_category;
		typedef T value_type;
		typedef Ptr pointer;
		typedef Ref reference;
		typedef ptrdiff_t difference_type;


		Node* _node;

		__list_iterator(Node* node)
			:_node(node)
		{
    
    }

		bool operator!=(const iterator& it) const
		{
    
    
			return _node != it._node;
		}

		bool operator==(const iterator& it) const
		{
    
    
			return _node == it._node;
		}

		Ref operator*()
		{
    
    
			return _node->_data;
		}

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

		// ++it
		iterator& operator++()
		{
    
    
			_node = _node->_next;
			return *this;
		}
		
		// it++
		iterator operator++(int)
		{
    
    
			iterator tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		// --it
		iterator& operator--()
		{
    
    
			_node = _node->_prev;
			return *this;
		}

		// it--
		iterator operator--(int)
		{
    
    
			iterator tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
	};

	template<class T>
	class list
	{
    
    
		typedef list_node<T> Node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<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;


		const_iterator begin() const
		{
    
    
			return const_iterator(_head->_next);
		}

		const_iterator end() const
		{
    
    
			return const_iterator(_head);
		}

		iterator begin()
		{
    
    
			return iterator(_head->_next);
		}

		iterator end()
		{
    
    
			return iterator(_head);
		}

		reverse_iterator rbegin()
		{
    
    
			return reverse_iterator(end());
		}

		reverse_iterator rend()
		{
    
    
			return reverse_iterator(begin());
		}

		void empty_init()
		{
    
    
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		template <class InputIterator>  
		list(InputIterator first, InputIterator last)
		{
    
    
			empty_init();

			while (first != last)
			{
    
    
				push_back(*first);
				++first;
			}
		}

		list()
		{
    
    
			empty_init();
		}

		void swap(list<T>& x)
		{
    
    
			std::swap(_head, x._head);
		}
		list(const list<T>& lt)
		{
    
    
			empty_init();
			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

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

		~list()
		{
    
    
			clear();
			delete _head;
			_head = nullptr;
		}

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

		void push_back(const T& x)
		{
    
    
			Node* tail = _head->_prev;
			Node* newnode = new Node(x);

			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;

			//insert(end(), x);
		}

		void push_front(const T& x)
		{
    
    
			insert(begin(), x);
		}

		iterator insert(iterator pos, const T& x)
		{
    
    
			Node* cur = pos._node;
			Node* prev = cur->_prev;

			Node* newnode = new Node(x);

			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;

			return iterator(newnode);
		}

		void pop_back()
		{
    
    
			erase(--end());
		}

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

		iterator erase(iterator pos)
		{
    
    
			assert(pos != end());

			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;
			delete cur;

			return iterator(next);
		}

		bool list<T>::empty() const
		{
    
    
			return begin() == end(); // 判断 begin() 是否等于 end() 来确定是否为空
		}

		typename list<T>::size_t list<T>::size() const
		{
    
    
			size_t count = 0;
			for (const_iterator it = begin(); it != end(); ++it)
			{
    
    
				++count;
			}
			return count; // 遍历链表来计算元素个数
		}

		typename list<T>::reference list<T>::front()
		{
    
    
			assert(!empty()); // 如果链表为空,则抛出断言
			return *begin(); // 返回链表的第一个元素
		}

		typename list<T>::const_reference list<T>::front() const
		{
    
    
			assert(!empty()); // 如果链表为空,则抛出断言
			return *begin(); // 返回链表的第一个元素
		}

		typename list<T>::reference list<T>::back()
		{
    
    
			assert(!empty()); // 如果链表为空,则抛出断言
			return *(--end()); // 返回链表的最后一个元素
		}

		typename list<T>::const_reference list<T>::back() const
		{
    
    
			assert(!empty()); // 如果链表为空,则抛出断言
			return *(--end()); // 返回链表的最后一个元素
		}
		void resize(size_t n, const T& val = T())
		{
    
    
			if (n < size()) {
    
    
				iterator it = begin();
				std::advance(it, n);
				while (it != end()) {
    
    
					it = erase(it); // 从尾部截断,删除多余的元素
				}
			}
			else if (n > size()) {
    
    
				insert(end(), n - size(), val); // 插入足够数量的默认值元素
			}
		}
	private:
		Node* _head;
	};
}

La diferencia entre lista y vector.

Al simular la implementación de listy vector, puede comprender mejor las diferencias y características entre ellos. Estos dos son contenedores secuenciales en la biblioteca estándar de C++, pero tienen implementaciones internas y escenarios de uso muy diferentes.

Implementación subyacente:

listPor lo general, una lista doblemente enlazada, cada nodo contiene datos y punteros a los nodos anterior y siguiente. Esto hace que las inserciones y eliminaciones en cualquier ubicación sean eficientes, pero el acceso aleatorio y la huella de memoria pueden ser relativamente pobres.
vectorEs una matriz dinámica cuyos elementos se almacenan de forma contigua en la memoria. Esto hace que el acceso aleatorio sea muy eficiente, pero las operaciones de inserción y eliminación pueden requerir mover una gran cantidad de elementos, lo cual es ineficiente.

Operaciones de inserción y borrado:

En list, las operaciones de inserción y borrado son eficientes, tanto en cualquier posición del contenedor como al principio y al final. Esto lo hace listideal cuando se requieren operaciones frecuentes de inserción y eliminación.
En vector, las operaciones de inserción y eliminación pueden requerir elementos móviles, especialmente en el medio o al principio del contenedor. vectorPor lo tanto, puede que no sea tan listeficiente cuando se trata de un gran número de operaciones de inserción y eliminación .

acceso aleatorio:

listNo se admite el acceso aleatorio, es decir, no se puede acceder directamente a los elementos a través de índices y se deben recorrer uno por uno a través de iteradores.
vectorAdmite el acceso aleatorio, puede acceder rápidamente a los elementos a través de índices y tener un buen rendimiento de acceso aleatorio.

Uso de memoria:

Debido al listuso de listas vinculadas para almacenar elementos, cada nodo requiere punteros adicionales para apuntar a los nodos anterior y siguiente, lo que puede resultar en un mayor uso de la memoria.
vectorSe almacena de forma contigua en la memoria y normalmente ocupa menos memoria.

invalidación del iterador:

En list, las operaciones de inserción y eliminación no invalidan los iteradores porque las relaciones entre los nodos no cambian.
En vector, las operaciones de inserción y eliminación pueden provocar una reasignación de memoria, invalidando así el iterador original.

En resumen, si necesita realizar operaciones frecuentes de inserción y eliminación, pero no tiene requisitos particularmente altos para el rendimiento del acceso aleatorio, entonces usar listes una buena opción. Y si presta más atención al rendimiento del acceso aleatorio, puede optar por utilizarlo vector. Por supuesto, en el desarrollo real, también es necesario sopesar qué contenedor usar según la situación específica.

epílogo

Los amigos interesados ​​​​pueden prestar atención al autor, si cree que el contenido es bueno, haga un enlace triple con un clic, ¡cangrejo cangrejo! ! !
No es fácil de hacer, por favor señale si hay alguna inexactitud
Gracias por su visita, mirar UU es la motivación para perseverar
. Con el catalizador del tiempo, ¡seamos mejores personas entre nosotros! ! !
(El aniversario de 128 días de la creación, aún no he descubierto cómo compartirlo, lo escribiré la próxima vez)

Supongo que te gusta

Origin blog.csdn.net/kingxzq/article/details/132256699
Recomendado
Clasificación