C ++: Introducción y uso de la lista.


Introducción de la lista

Lista de introducción de documentos

  1. La lista es un contenedor serie que se puede insertar y eliminar en cualquier posición dentro de un rango constante, y el contenedor puede iterar de un lado a otro en ambas direcciones.

  2. La capa inferior de la lista es una estructura de lista doblemente vinculada. Cada elemento de la lista doblemente vinculada se almacena en nodos independientes que no están relacionados entre sí. En el nodo, el puntero apunta al elemento anterior y al siguiente elemento.

  3. list es muy similar a forward_list: la principal diferencia es que forward_list es una lista vinculada individualmente, que solo se puede iterar hacia adelante, lo que la ha hecho más simple y más eficiente.

  4. En comparación con otros contenedores serie (matriz, vector, deque), la lista suele ser más eficiente para insertar y eliminar elementos en cualquier posición.

  5. En comparación con otros contenedores serializados, el mayor inconveniente de list y forward_list es que el acceso aleatorio en cualquier posición no es compatible. Por ejemplo, para acceder al sexto elemento de la lista, debe iterarse desde una posición conocida (como la cabeza o la cola) a este Posición, iterar en esta posición requiere un costo de tiempo lineal; la lista también necesita espacio adicional para guardar la información asociada de cada nodo (esto puede ser un factor importante para listas grandes que almacenan elementos más pequeños) )

Inserte la descripción de la imagen aquí

Uso de la lista

Hay muchas interfaces en la lista, similar aquí, solo necesita dominar cómo usarlo correctamente y luego profundizar en los principios detrás de él, que ha alcanzado la escalabilidad. Las siguientes son algunas interfaces importantes comunes en la lista.

Estructura de la lista

Constructor ((constructor)) Descripción de la interfaz
lista() Construye una lista vacía
list (size_type n, const value_type & val = value_type ()) La lista construida contiene n elementos con valor val
lista (const list & x) Copia constructor
lista (InputIterator primero, InputIterator último) Lista de construcción con elementos en el intervalo (primero, último)

Ejemplo de código:

#include <iostream>
#include <list>

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

	// 以数组为迭代器区间构造l5
	int array[] = { 16, 2, 77, 29 };
	std::list<int> l5(array, array + sizeof(array) / sizeof(int));
	
	// 用迭代器方式打印l5中的元素
	for (std::list<int>::iterator it = l5.begin(); it != l5.end(); it++)
		std::cout << *it << " ";
	std::cout << std::endl;
	
	// C++11范围for的方式遍历 
	for(auto& e : l5)
	std::cout << e << " ";
	std::cout << std::endl;

	return 0;
}

Uso del iterador de lista

Aquí, el iterador puede entenderse como un puntero que apunta a un nodo en la lista.

Declaración de función Descripción de la interfaz
comienzo + fin Iterador que devuelve el primer elemento + iterador que devuelve la siguiente posición del último elemento
rbegin + rend Devuelve el reverse_iterator del primer elemento, es decir, la posición final, y devuelve el reverse_iterator de la siguiente posición del último elemento, es decir, la posición de inicio

Inserte la descripción de la imagen aquí

【Nota】

  1. begin y end son iteradores hacia adelante, realizan operaciones ++ en el iterador y el iterador se mueve hacia atrás
  2. rbegin (end) y rend (begin) son iteradores inversos, realizan operaciones ++ en el iterador y el iterador avanza

Ejemplo de código:

#include <iostream>
#include <list>
using namespace std;

void print_list(const list<int>& l)
{
	// 注意这里调用的是list的 begin() const,返回list的const_iterator对象 
	for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it) {
	cout << *it << " ";
	// *it = 10; 编译不通过 
	}
	cout << endl;
}

int main() {
	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	list<int> l(array, array + sizeof(array) / sizeof(array[0])); 
	
	// 使用正向迭代器正向list中的元素
	for (list<int>::iterator it = l.begin(); it != l.end(); ++it)
		cout << *it << " ";
	cout << endl;
	
	// 使用反向迭代器逆向打印list中的元素
	for (list<int>::reverse_iterator it = l.rbegin(); it != l.rend(); ++it)
		cout << *it << " ";
	cout << endl;
	return 0;
}

capacidad de la lista

Declaración de función Descripción de la interfaz
vacío Compruebe si la lista está vacía, devuelva verdadero, de lo contrario devuelva falso
Talla Devuelve el número de nodos válidos en la lista

acceso al elemento de lista

Declaración de función Descripción de la interfaz
frente Devuelve una referencia al valor en el primer nodo de la lista
espalda Devuelve una referencia al valor en el último nodo de la lista

modificadores de lista

Declaración de función Descripción de la interfaz
push_front Inserte un elemento con un valor de val antes del primer elemento de la lista
frente_ pop Eliminar el primer elemento de la lista
hacer retroceder Inserte un elemento con valor val al final de la lista
pop_back Eliminar el último elemento de la lista
insertar Inserte un elemento con un valor de val en la posición de la lista
borrar Eliminar el elemento en la posición de la lista
intercambiar Intercambiar elementos en dos listas
claro Borrar los elementos válidos en la lista

Ejemplo de código:

#include <list>
#include <vector>
void PrintList(list<int>& l)
{
	for (auto& e : l)
		cout << e << " ";
	cout << endl;
}
//=========================================================================================
// push_back/pop_back/push_front/pop_front
void TestList()
{
	int array[] = { 1, 2, 3 };
	list<int> L(array, array + sizeof(array) / sizeof(array[0]));

	// 在list的尾部插入4,头部插入0
	L.push_back(4); 
	L.push_front(0); 
	PrintList(L);

	// 删除list尾部节点和头部节点 
	L.pop_back(); 
	L.pop_front(); 
	PrintList(L);
}
//=========================================================================================
// insert /erase
void TestList3()
{
	int array1[] = { 1, 2, 3 };
	list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));

	// 获取链表中第二个节点 
	auto pos = ++L.begin(); 
	cout << *pos << endl;

	// 在pos前插入值为4的元素 
	L.insert(pos, 4); 
	PrintList(L);

	// 在pos前插入5个值为5的元素 
	L.insert(pos, 5, 5); 
	PrintList(L);

	// 在pos前插入[v.begin(), v.end)区间中的元素 
	vector<int> v{ 7, 8, 9 };
	L.insert(pos, v.begin(), v.end()); 
	PrintList(L);

	// 删除pos位置上的元素 
	L.erase(pos);
	PrintList(L);

	// 删除list中[begin, end)区间中的元素,即删除list中的所有元素 
	L.erase(L.begin(), L.end());
	PrintList(L);
}
// resize/swap/clear
void TestList4()
{
	// 用数组来构造list
	int array1[] = { 1, 2, 3 };
	int array2[] = { 4, 5, 6 };
	list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0])); 
	list<int> l2(array2, array2 + sizeof(array2) / sizeof(array2[0]));

	PrintList(l1);
	PrintList(l2);

	// 交换l1和l2中的元素 
	l1.swap(l2); 
	PrintList(l1); 
	PrintList(l2);

	// 将l2中的元素清空 
	l2.clear(); 
	
	cout<<l2.size()<<endl;
}

El iterador de lista no es válido

Como se mencionó anteriormente, aquí puede entender temporalmente el iterador como un puntero. El fallo del iterador significa que el nodo señalado por el iterador no es válido, es decir, el nodo se elimina. Debido a que la estructura subyacente de la lista es una lista enlazada doblemente circular con nodos iniciales, la inserción en la lista no hará que falle el iterador de la lista, y solo fallará cuando se elimine, y el único error es la iteración que apunta al nodo eliminado Iterador, otros iteradores no se verán afectados.

Ejemplo de código:

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 main() {
	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	list<int> l(array, array + sizeof(array) / sizeof(array[0]));
	auto it = l.begin();
	while (it != l.end())
	{
		l.erase(it++);
		// it = l.erase(it);
	}
}

resumen de la lista

  • El uso de funciones como construcción, inserción, borrado, retroceso, inicio y fin no es diferente del contenedor anterior

  • Diferencias:
    1. La función de inserción no hace que falle el iterador
    2. No se puede realizar el acceso aleatorio (no hay sobrecarga [])
    3. El concepto de no capacidad (no hay función como cambiar el tamaño de la capacidad de reserva)

  • ordenar: ordenar la lista vinculada

  • fusionar: fusionar dos listas vinculadas ordenadas en una nueva lista vinculada ordenada, y la lista vinculada sintetizada (el contenedor de la lista vinculada como parámetro) se vaciará.

Lista y comparación de vectores

Tanto el vector como la lista son contenedores serie muy importantes en STL. Debido a las diferentes estructuras subyacentes de los dos contenedores, sus características y escenarios de aplicación son diferentes. Las principales diferencias son las siguientes:

Contraste vector lista
Estructura subyacente Tabla de secuencia dinámica, un espacio continuo. Lista enlazada circular bidireccional con nodo principal
Acceso aleatorio Admite acceso aleatorio, acceso a un elemento de eficiencia O (1) No admite acceso aleatorio, acceso a un elemento eficiencia O (N)
Insertar y eliminar La inserción y eliminación en cualquier posición es ineficiente, los elementos deben ser movidos, la complejidad del tiempo es O (N), la capacidad puede incrementarse al insertar, el aumento de la capacidad: abrir un nuevo espacio, copiar elementos, liberar el espacio antiguo, lo que resulta en una menor eficiencia Alta eficiencia de inserción y eliminación en cualquier posición, sin necesidad de mover elementos, la complejidad temporal es O (1)
Utilización del espacio La capa inferior es espacio continuo, que no es fácil de causar fragmentación de memoria, alta utilización de espacio y alta utilización de caché El nodo inferior se desarrolla dinámicamente, los nodos pequeños son fáciles de causar fragmentación de memoria, baja utilización de espacio y baja utilización de caché
Iterador Puntero ecológico original Encapsular el puntero ecológico original (puntero de nodo)
Fallo de iterador Al insertar elementos, todos los iteradores deben reasignarse, ya que insertar elementos puede causar una nueva expansión, lo que da como resultado la invalidación del iterador original. Al eliminar, el iterador actual debe reasignarse o no será válido. Insertar un elemento no invalidará el iterador. Al eliminar un elemento, solo invalidará el iterador actual, otros iteradores no se verán afectados
Usar escena Necesita almacenamiento eficiente, admite acceso aleatorio, no le importa la eficiencia de inserción y eliminación Operaciones masivas de inserción y eliminación, no importa el acceso aleatorio

Si tiene opiniones diferentes, ¡deje un mensaje para discutir!

152 artículos originales publicados · 45 elogiados · 10,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/AngelDg/article/details/105227494
Recomendado
Clasificación