Uma breve introdução a cada contêiner de C++ STL

1. O que é STL?

1. STL (Standard Template Library), a biblioteca de modelos padrão, é uma biblioteca de programa C++ eficiente que contém muitas estruturas de dados básicos e algoritmos básicos comumente usados. Ele fornece uma estrutura de aplicativo escalável para a maioria dos programadores C++, que incorpora altamente a capacidade de reutilização do software.

2. Do nível lógico, a ideia de programação genérica é incorporada em STL . Nessa ideia, a maioria dos algoritmos básicos são abstraídos e generalizados, independentes das estruturas de dados correspondentes, e usados ​​para lidar com várias situações da mesma forma ou de maneira semelhante.

3. Do ponto de vista do nível de implementação, todo o STL é implementado de forma tipo parametrizada, baseado em templates .

STL tem seis componentes principais, mas inclui principalmente três partes: contêiner, iterador e algoritmo.

  • Containers : usados ​​para gerenciar uma coleção de objetos de um determinado tipo. Cada contêiner tem suas vantagens e desvantagens, portanto, para atender às diferentes necessidades do programa, a STL preparou sete tipos básicos de contêineres.
  • Iterators (Iterators): Usados ​​para executar ações de passagem nos elementos de uma coleção de objetos. Essa coleção de objetos pode ser um contêiner ou pode fazer parte de um contêiner. Cada tipo de contêiner fornece seus próprios iteradores, e esses iteradores conhecem a estrutura interna desse tipo de contêiner.
  • Algoritmos (Algorithms): usados ​​para processar os elementos na coleção de objetos, como Classificar, Pesquisar, Copiar, Apagar esses elementos. Com a ajuda de iteradores, só precisamos escrever um algoritmo uma vez e aplicá-lo a qualquer contêiner, porque os iteradores de todos os contêineres fornecem uma interface consistente.

O conceito básico do STL é separar dados e operações. Os dados são gerenciados pelo contêiner, a operação é executada pelo algoritmo e o iterador atua como a cola entre os dois, para que qualquer algoritmo possa interagir com qualquer contêiner.

2. Contêineres

Os contêineres são usados ​​para gerenciar certos tipos de objetos. Para atender às diferentes necessidades do programa, a STL preparou dois tipos de sete tipos básicos de contêineres:

  • Contêineres de sequência, que são clusters ordenáveis ​​nos quais cada elemento tem uma posição fixa – dependendo de quando e onde ele é inserido, independentemente do valor do elemento. Se você anexar seis elementos a um cluster, eles estarão na mesma ordem em que foram inseridos. O STL fornece três contêineres seriais: vetor (vetor), fila dupla (deque) e lista (lista).Além disso, você também pode usar string e array como contêiner serial.
  • Recipientes associativos (recipientes associativos), este é um cluster ordenado, a posição do elemento depende de um critério de classificação específico e valor do elemento e não tem nada a ver com a ordem de inserção. Se você colocar seis elementos em tal agrupamento, suas posições dependerão dos valores dos elementos, não da ordem de inserção. O STL fornece quatro contêineres associativos: coleção (conjunto), várias coleções (multiconjunto), mapeamento (mapa) e mapeamento múltiplo (multimapa).

O diagrama esquemático é mostrado na figura abaixo:

2.1 vetor

Vector (vetor): É um container serial, que na verdade é semelhante a um array, mas é superior a um array. De um modo geral, a matriz não pode ser expandida dinamicamente; portanto, quando o programa está em execução, ele desperdiça memória ou causa problemas fora dos limites. O vetor apenas compensa esse defeito.Quando o espaço de memória não é suficiente, é necessário solicitar novamente uma memória grande o suficiente e copiar a memória.

características

  • Ele tem um espaço de memória contínuo, portanto pode suportar muito bem o acesso aleatório, ou seja, o operador [] e .at(), e o acesso aleatório é rápido. (vantagem)
  • Ao inserir ou excluir elementos em sua cabeça ou meio, para manter a ordem relativa original, todos os elementos após o ponto de inserção ou exclusão devem ser movidos, portanto, a eficiência da inserção ou exclusão é relativamente baixa. (deficiência)
  • É mais rápido inserir e excluir elementos posteriormente e geralmente não há necessidade de mover a memória neste momento. (vantagem)
  • Resumo: Equivalente a um array expansível (array dinâmico), o acesso aleatório é rápido, a inserção ou exclusão na cabeça e no meio é ineficiente, mas a inserção ou exclusão no final é eficiente.

Cena aplicável

É adequado para cenas com objetos simples, pequenas alterações e acesso aleatório frequente.

exemplo

O exemplo a seguir define um vetor de inteiros, insere 6 elementos e imprime todos os elementos:

#include <iostream>
#include <vector>

  C++资料领取进裙:580475042
  
using namespace std;

int main(int argc, char* argv[])
{
	vector<int> vecTemp;

	for (int i = 0; i<6; i++)
		vecTemp.push_back(i);

	for (int i = 0; i<vecTemp.size(); i++)
		cout << vecTemp[i] <<" "; // 输出:0 1 2 3 4 5

	return 0;
}

2.2 e

Deque (fila de duas pontas) é um espaço de memória contínuo com aberturas bidirecionais (unindo dinamicamente vários espaços contínuos por meio de matrizes de ponteiros) e um novo espaço pode ser adicionado a qualquer momento. A maior tarefa do deque é manter a ilusão de continuidade geral nesses espaços contínuos segmentados e fornecer uma interface de acesso aleatório.

características

  • Uma vez que um novo espaço deve ser adicionado na cabeça e na cauda do deque, uma certa quantidade de espaço contínuo é configurada e amarrada na cabeça ou na cauda de todo o deque, de modo que inserir elementos na cabeça ou na cauda é muito rápido. (vantagem)
  • Colocar elementos no meio consome mais tempo porque outros elementos precisam ser movidos. (deficiência)
  • deque é um compromisso entre lista e vetor. Tem as vantagens da lista e as vantagens da alta eficiência de acesso aleatório do vetor.
  • Resumo: O acesso aleatório é suportado, mas a eficiência não é tão alta quanto o vetor. A inserção ou exclusão na cabeça e na cauda é eficiente, mas a inserção ou exclusão no meio é ineficiente.

Cena aplicável

É adequado para cenários que exigem acesso aleatório frequente e se preocupam com a inserção e exclusão de dados em ambas as extremidades.

exemplo

O exemplo a seguir declara um deque do tipo ponto flutuante, insere 6 elementos no final do contêiner e, finalmente, imprime todos os elementos.

#include <iostream>
#include <deque>

using namespace std;

int main(int argc, char* argv[])
{
	deque<float> dequeTemp;

	for (int i = 0; i<6; i++)
		dequeTemp.push_back(i);

	for (int i = 0; i<dequeTemp.size(); i++)
		cout << dequeTemp[i] << " "; // 输出:0 1 2 3 4 5

	return 0;
}

2.3 lista

A lista é implementada por uma lista duplamente encadeada, os elementos são armazenados no heap e cada elemento é colocado em um pedaço de memória. Não há hábito de reserva de espaço, portanto, cada alocação de um elemento será alocada da memória e cada exclusão de um elemento liberará a memória que ele ocupa.

características

  • O espaço de memória pode ser descontínuo, e os dados são acessados ​​através de ponteiros, o que torna seu acesso randômico muito ineficiente, pois não proporciona sobrecarga do operador []. (deficiência)
  • Devido às características da lista vinculada, a eficiência de inserção e exclusão em qualquer posição é alta. (vantagem)
  • Apenas o acesso direto ao primeiro e ao último elemento é suportado.Se você deseja obter outros elementos (com o mesmo tempo de acesso), precisa percorrer a lista encadeada. (deficiência)
  • Resumo: o acesso aleatório não é suportado e a inserção e exclusão em qualquer posição são mais eficientes.

Cena aplicável

É adequado para cenários em que as operações de inserção e exclusão são executadas com frequência e o acesso aleatório é pouco frequente.

exemplo

O exemplo a seguir produz uma lista vazia, pronta para colocar caracteres e, em seguida, insere todos os caracteres de 'a' a 'z' nela e usa um loop para imprimir e remover o primeiro elemento do conjunto a cada vez, imprimindo assim todos elementos:

#include <iostream>
#include <list>

using namespace std;

  C++资料领取进裙:580475042

int main(int argc, char* argv[])
{
	list<char> listTemp;

	for (char c = 'a'; c <= 'z'; ++c)
		listTemp.push_back(c);

	while (!listTemp.empty())
	{
		cout <<listTemp.front() << " ";
		listTemp.pop_front();
	}

	return 0;
}

O valor de retorno da função de membro empty() nos informa se há elementos restantes no contêiner e, desde que essa função retorne false, o loop continua. Dentro do loop, a função de membro front() retorna o primeiro elemento e a função pop_front() exclui o primeiro elemento.

Nota: list<pointer> é o método de menor desempenho. É melhor usar list<object> ou vector<pointer> diretamente, porque o ponteiro não tem construção e destruição e não ocupa muita memória.

2.4 conjunto

Conjunto (coleção), como o nome sugere, é uma coleção matemática - cada elemento aparece no máximo uma vez e os elementos do conjunto foram classificados do menor ao maior.

características

  • Ele é implementado usando uma árvore rubro-negra, e seus elementos internos são classificados automaticamente de acordo com seu valor.Cada valor de elemento pode aparecer apenas uma vez, não sendo permitida a duplicação.
  • Cada vez que um valor é inserido, a árvore rubro-negra precisa ser ajustada, o que tem um certo impacto na eficiência. (deficiência)
  • A eficiência de inserção ou exclusão de map e set é maior do que a de outros contêineres de sequência, porque para contêineres associativos, não há necessidade de fazer cópia de memória e movimentação de memória. (vantagem)
  • Resumo: É implementado por uma árvore rubro-negra e seus elementos internos são classificados automaticamente de acordo com seu valor. Cada valor de elemento pode aparecer apenas uma vez, nenhuma repetição é permitida e a eficiência de inserção e exclusão é maior que a de outra sequência containers.

Cena aplicável

É adequado para cenários em que geralmente é verificado se um elemento está em uma determinada coleção e precisa ser classificado.

exemplo

O exemplo a seguir demonstra duas características de set (coleção):

#include <iostream>
#include <set>

using namespace std;

int main(int argc, char* argv[])
{
	set<int> setTemp;

	setTemp.insert(3);
	setTemp.insert(1);
	setTemp.insert(2);
	setTemp.insert(1);

	set<int>::iterator it;
	for (it = setTemp.begin(); it != setTemp.end(); it++)
	{
		cout << *it << " ";
	}

	return 0;
}

Resultado de saída: 1 2 3. Um total de 4 números são inseridos, mas há apenas 3 números no conjunto e eles são ordenados.Pode-se observar que as duas características do conjunto mencionado acima são ordenadas e não repetitivas.

Quando os elementos na coleção de conjuntos são estruturas, a estrutura deve implementar a sobrecarga do operador '<':

#include <iostream>
#include <set>
#include <string>

using namespace std;

struct People
{
	string name;
	int age;

	bool operator <(const People p) const
	{
		return age < p.age;
	}
};

int main(int argc, char* argv[])
{
	set<People> setTemp;

	setTemp.insert({"张三",14});
	setTemp.insert({ "李四", 16 });
	setTemp.insert({ "隔壁老王", 10 });

	set<People>::iterator it;
	for (it = setTemp.begin(); it != setTemp.end(); it++)
	{
		printf("姓名:%s 年龄:%d\n", (*it).name.c_str(), (*it).age);
	}

	return 0;
}

  C++资料领取进裙:580475042
/*
输出结果
姓名:王二麻子 年龄:10
姓名:张三 年龄:14
姓名:李四 年龄:16 
*/ 

Você pode ver que os resultados estão organizados em ordem decrescente de idade. Além disso, a string precisa ser convertida usando c_str(), caso contrário, imprimirá caracteres distorcidos.

Além disso, Multiset é o mesmo que set, exceto que permite elementos repetidos, ou seja, multiset pode incluir vários elementos com o mesmo valor. Não farei muita introdução aqui.

2.5 mapa

O mapa é implementado por uma árvore rubro-negra, e seus elementos são um par (pares chave/valor) formado por "chave valor/valor real".

mapa é usado principalmente para mapeamento de dados um a um. Uma árvore rubro-negra é construída dentro do mapa. Essa árvore tem a função de classificar dados automaticamente, para que todos os dados dentro do mapa estejam em ordem. Por exemplo, em uma turma, há uma relação de mapeamento um-para-um entre o número e o nome de cada aluno.

características

  • Cada elemento tem uma chave e só pode aparecer uma vez, não são permitidas duplicatas.
  • Pesquisa rápida de registros com base no valor da chave. A complexidade da pesquisa é basicamente O(logN). Se houver 1000 registros, a pesquisa binária pode ser pesquisada até 10 vezes (1024). (vantagem)
  • Cada vez que um valor é inserido, a árvore rubro-negra precisa ser ajustada, o que tem um certo impacto na eficiência. (deficiência)
  • Adicionar e excluir nós tem pouco efeito no iterador, exceto para o nó de operação, não tem efeito em outros nós. (vantagem)
  • Para iteradores, os valores reais podem ser modificados, mas as chaves não podem ser modificadas.
  • Resumo: Elementos são pares chave-valor, chave e valor podem ser de qualquer tipo que você precisar, cada elemento tem uma chave, e só pode aparecer uma vez, não é permitida repetição, e encontre rapidamente os registros de acordo com a chave.

Cena aplicável

É adequado para cenários em que um dicionário de dados precisa ser armazenado e os valores podem ser facilmente encontrados com base em chaves.

exemplo

#include "stdafx.h"
#include <iostream>
#include <map>
#include <string>

using namespace std;

int main(int argc, char* argv[])
{
	map<int, string> mapTemp;

	mapTemp.insert({ 5,"张三" });
	mapTemp.insert({ 3, "李四"});
	mapTemp.insert({ 4, "隔壁老王" });

	map<int, string>::iterator it;
	for (it = mapTemp.begin(); it != mapTemp.end(); it++)
	{
		printf("学号:%d 姓名:%s\n", (*it).first, (*it).second.c_str());
	}

	return 0;
}

/*
输出结果:
学号:3 姓名:李四
学号:4 姓名:隔壁老王
学号:5 姓名:张三
*/

Multimap é o mesmo que map, mas permite elementos repetidos, ou seja, multimap pode conter vários elementos com o mesmo valor de chave (key). Não farei muita introdução aqui.

2.6 Adaptador de contêiner

Além das sete categorias básicas de contêineres acima, para atender às necessidades especiais, o STL também fornece alguns adaptadores de contêineres especiais (e predefinidos), que são implementados com base nas categorias básicas de contêineres. incluir:

1、pilha

O nome diz tudo, o contêiner de pilha adota uma estratégia de gerenciamento LIFO (último a entrar, primeiro a sair) para elementos.

2, fila

O contêiner de fila adota a estratégia de gerenciamento FIFO (primeiro a entrar, primeiro a sair) para os elementos. Em outras palavras, é um buffer comum (buffer).

3、prioridade_fila

Os elementos em um contêiner priority_queue podem ter prioridades diferentes. A chamada prioridade é definida com base nos critérios de classificação fornecidos pelo programador (operadores são usados ​​por padrão). O efeito da fila de prioridade é equivalente a tal buffer: "O próximo elemento é sempre o elemento com maior prioridade na fila". Se vários elementos tiverem a prioridade mais alta ao mesmo tempo, sua ordem é indefinida.

3. Resumo

Resumo das características de cada container

  • O vetor suporta acesso aleatório, inserir ou excluir na cabeça e no meio é ineficiente, mas inserir ou excluir na cauda é eficiente.
  • Ele suporta acesso aleatório, mas a eficiência não é tão alta quanto a do vetor.É eficiente para inserir ou excluir na cabeça e na cauda, ​​mas é ineficiente para inserir ou excluir no meio.
  • A lista não suporta acesso aleatório e a eficiência de inserção e exclusão em qualquer posição é alta.
  • O conjunto é implementado por uma árvore rubro-negra e seus elementos internos são classificados automaticamente de acordo com seu valor. Cada valor de elemento pode aparecer apenas uma vez, nenhuma repetição é permitida e a eficiência de inserção e exclusão é maior do que a de outros contêineres de sequência .
  • Os elementos do mapa são pares chave-valor. A chave e o valor podem ser de qualquer tipo que você precisar. Cada elemento tem uma chave e só pode aparecer uma vez. A duplicação não é permitida. Pesquise registros rapidamente com base na chave.

No processo de uso real, qual desses recipientes deve ser selecionado deve seguir os seguintes princípios:

1. Se você precisa de acesso aleatório eficiente e não se preocupa com a eficiência da inserção e exclusão, use vector.
2. Se você precisar de acesso aleatório e se preocupar com a eficiência de inserção e exclusão de dados em ambas as extremidades, use deque.
3. Se você precisa inserir e excluir um grande número de elementos e não se preocupa com a eficiência do acesso aleatório, use list.
4. Se você costuma verificar se um elemento está em um determinado conjunto e precisa ser classificado, use set se ele existir apenas e use multiset se ele não existir exclusivamente.
5. Se você planeja armazenar o dicionário de dados e precisa encontrar o valor convenientemente com base na chave, use map para situações um-para-um e multimap para situações um-para-muitos.

Análise de complexidade de tempo de cada contêiner

  • A complexidade de tempo de inserção e exclusão do vetor no início e no meio é O(N), a complexidade de tempo de inserção e exclusão na cauda é O(1), a complexidade de tempo do acesso aleatório é O(1) e a busca tempo A complexidade é O(N);
  • A complexidade de tempo de inserção e exclusão deque no meio é O(N), a complexidade de tempo de inserção e exclusão na cabeça e na cauda é O(1), a complexidade de tempo de acesso aleatório é O(1) e a busca tempo A complexidade é O(N);
  • A complexidade de tempo de inserção e exclusão de lista em qualquer posição é O(1), e a complexidade de tempo de busca é O(N);
  • Tanto o conjunto quanto o mapa são implementados por meio de árvores rubro-negras, portanto, a complexidade de tempo das operações de inserção, exclusão e pesquisa é O(log N).

Comunalidade de cada contêiner

Cada contêiner geralmente tem as seguintes funções: construtor padrão, construtor de cópia, destruidor, empty(), max_size(), size(), operator=, operator<, operator<=, operator>, operator>= , operator==, operator !=, trocar().

Os contêineres sequenciais e associativos compartilham as seguintes funções:

  • begin() : retorna o ponteiro do iterador do primeiro elemento do container;
  • end(): Retorna o ponteiro do iterador um bit atrás do último elemento do contêiner;
  • rbegin(): retorna um ponteiro iterador reverso, apontando para o último elemento do contêiner;
  • rend(): retorna um ponteiro iterador reverso, apontando para um bit antes do primeiro elemento do contêiner;
  • clear(): exclui todos os elementos do container;
  • erase(it): Exclua o elemento no ponteiro do iterador.

Acho que você gosta

Origin blog.csdn.net/QtCompany/article/details/131688999
Recomendado
Clasificación