Estrutura de dados

Origem: Wikipédia, a enciclopédia livre.
Saltar para a navegação Saltar para a pesquisa
Evolution-tasks.png
Atenção: Este artigo foi listado como um artigo com problemas.
Ajude-nos na discussão deste artigo. O motivo da marcação foi a seguinte: problemas com a tradução
Ambox important.svg
Este artigo está escrito com linguagem técnica de forma complexa ou inacessível.
Por favor, melhore a linguagem tornando mais acessível para o público leigo, sem empobrecer o texto. A página de discussão pode conter sugestões.
Uma árvore binária é uma estrutura de dados.

Uma estrutura de dados (ED), em ciência da computação, é uma coleção tanto de valores (e seus relacionamentos) quanto de operações (sobre os valores e estruturas decorrentes). É uma implementação concreta de um tipo abstrato de dado (TAD) ou um tipo de dado (TD) básico ou primitivo. Assim, o termo ED pode ser considerado sinônimo de TD, se considerado TAD um hipônimo de TD, isto é, se um TAD for um TD.

Critérios para escolha e estudo de uma estrutura de dados incluem eficiência para buscas e padrões específicos de acesso, necessidades especiais para manejo de grandes volumes (veja big data), ou a simplicidade de implementação e uso. Ou seja, EDs eficientes são cruciais para a elaboração de algoritmos, diversas linguagens possuem ênfase nas EDs, como evidenciado pela POO, e aplicações distintas usufruem de ou requerem EDs específicas (e.g. um compilador usa uma tabela de dispersão para identificadores e namespaces, enquanto uma Árvore B ou Árvore AA (en) é apropriada para acessos randômicos).

Em termos de EDs, os TDs e TADs são definidos indiretamente pelas operações e usos, e propriedades destas operações e usos: e.g. o custo computacional e o espaço que pode representar e ocupa na memória.

Relevância para a Ciência da Computação[editar | editar código-fonte]

Na ciência da computação, uma ED é um modo particular de armazenamento e organização de dados em um computador de modo que possam ser usados eficientemente, facilitando sua busca e modificação.[1][2] EDs e algoritmos são temas fundamentais da ciência da computação, sendo utilizados nas mais diversas áreas do conhecimento e com os mais diferentes propósitos de aplicação. Sabe-se que algoritmos manipulam dados. Quando estes dados estão organizados (dispostos) de forma coerente, caracterizam uma forma, uma estrutura de dados. A organização e os métodos para manipular essa estrutura é que lhe confere singularidade (e vantagens estratégicas, como a minimização do espaço ocupado na memória RAM), além (potencialmente) de tornar o código-fonte mais enxuto e simples.

Conceito-chave: ponteiro[editar | editar código-fonte]

Um ponteiro é um objeto cujo valor aponta para outro valor através de um endereço de memória (e.g. da memória RAM). A forma como os ponteiros são usados em uma ED, seja explicitamente (como em uma lista ligada) ou implictamente (como em um vetor homogêneo), evidencia suas propriedades, usos e operações[3][4][5]. Por exemplo, em uma estrutura ligada, em que cada elemento possui um (ou mais) ponteiro(s) para outro(s) elemento(s), os valores podem assumir diferentes tipos e estruturas arbitrariamente complexas; já com a omissão dos ponteiros, por exemplo em um vetor (sequência de valores de um mesmo tipo), a representação fica compacta e muitas vezes favorece o processamento massivamente paralelo, como no caso de tensores e outras variantes multidimensionais tão comuns na física, engenharia e matemática aplicada em geral.

Mesmo quando ponteiros não são usados diretamente, como em linguagens que não utilizam distinção entre ponteiros e outras variáveis (veja o exemplo abaixo), a noção de referenciar a uma outra estrutura de dado arbitrária é usada, noção que é canonicamente abordada pela utilização do ponteiro.

Exemplo de discussão a respeito de ponteiros[editar | editar código-fonte]

É usual dizer que linguagens de alto nível, e.g. Python, não utilizam ponteiros. No entanto, pode-se argumentar que mais fiel aos fatos é considerar que, ao menos em Python, os ponteiros são utilizados por padrão[6]:

// C++
int a = 1;
int *b = &a;
a = 2;
cout << *b << endl; # 2
# Python 3.5.2:
a = 1
b = a
b = 2
print(b)  # 2

# além disso:
l = [3, 4, 5]
m = l
l[1] = 8
print(m)  # [3, 8, 5]

Estas variáveis em Python são referências no jargão de linguagens de programação.

EDs para a Web, computação científica e scripting em geral[editar | editar código-fonte]

Embora a teoria básica de EDs seja centrada no processamento sequencial e uso de ponteiros, em muitos contextos, em especial na programação para a web com uso de linguagens de alto nível como Javascript, Python, ou Ruby, há ênfase em padrões simples, reutilização de implementações otimizadas há décadas, e processamento paralelo/assíncrono, em contraste com a teoria básica de EDs. Nesta seção alguns aspectos selecionados são expostos. De forma a explicitar a pertinência da discussão, considere sistemas Unix (e.g. GNU/Linux, BSD, OSX) em que os dados são representados basicamente como arquivos[7]: onde está o ponteiro? o processamento é sequencial?

EDs na computação científica[editar | editar código-fonte]

Uma prática espúria, muitas vezes limitante, e (infelizmente) bastante frequente na computação científica, é a iteração sobre EDs multidimensionais. Veja a diferença de eficiência mesmo em um caso bem simples e isolado:

# IPython3 com Python 3.5.2, 13/Mar/2018, Ubuntu 16.04
 
In [1]: ll = list(range(int(10e5)))

In [2]: timeit for i in ll: (i**3) + (i+2)*i**2
1 loops, best of 3: 575 ms per loop

In [3]: aa = n.array(ll)

In [4]: timeit for i in aa: (i**3) + (i+2)*i**2
1 loops, best of 3: 545 ms per loop

In [5]: timeit aa**3 + (aa+2)*aa**2
10 loops, best of 3: 80.1 ms per loop

Já para a modelagem e otimização (e.g. através de métodos de aprendizagem de máquina), são convenientes abstrações de conceitos e a POO. Ou seja, na programação científica como encontrada nas ciências naturais e engenharias, por exemplo, a POO está associada ao uso de técnicas e modelos através dos TADs, ao passo que as EDs homogêneas e os TDs primitivos estão associados ao processamento eficiente de dados advindos de sistemas reais ou modelos.

Para um exemplo de computação natural, considere a otimização bio-inspirada através de um algoritmo ACO: as classes Formiga e Solução são potencialmente TADs, instanciam objetos inspirados em conceitos reais e encapsulam os dados em concordância com a POO. No processamento de dados multidimensionais (como no problema do caixeiro viajante e outros problemas NP-completos ou NP-difíceis, por exemplo), a implementação provavelmente recorrerá a TDs homogêneos para processamentos lineares/matriciais (utilizando rotinas como as do BLAS (en), LAPACK (en) e FFTW (en)) e massivamente paralelos (utilizando técnicas como thread, serviços/jobs[necessário esclarecer], message passing, basicamente para processamento assíncrono (en)). No ACO, estes TDs homogêneos estarão presentes, por exemplo, na evaporação do feromônio e nas escolhas de trajetórias de cada Formiga instanciada.[8]

JSON-compatível[editar | editar código-fonte]

Os dados são preferencialmente mantidos nas estruturas de listas (sequências heterogêneas, e.g. uma lista ligada) e de dicionários (tabelas de dispersão (hash tables), e há distinção entre os tipos básicos numéricos (e.g. floats e INT) e textuais (e.g. chars e strings).

A maior limitação prática desta abordagem é o processamento de vetores multidimensionais e dados muito volumosos, motivo pelo qual há bibliotecas com implementações otimizadas, como as citadas no exemplo sobre computação científica.

Quando um valor pode ser uma função/rotina, tal objeto permite a representação de objetos para a POO. A impedância neste contexto se refere à resistência que os objetos possuem de serem representados como TADs.

Embora o padrão lista e dicionário de representação de estruturas de dados esteja aqui utilizada de forma genérica, JSON quer dizer Javascript Object Notation, e é um padrão formalmente definido de representação de dados, ao qual e.g. Python, Vim language (VimL), e o próprio Javascript, com frequência se adequam para transferir ou armazenar dados.

Aspectos de visualização de dados[editar | editar código-fonte]

Para dados em lista, são apropriados métodos de visualização de séries temporais e sequenciais em geral. Já para pilhas, a visualização tende a ser feita por torres de hanoi ou características próprias do contexto. Há diversas visualizações de EDs apreciadas esteticamente ou com fins didáticos. Destaque a este respeito deve ser dado aos aplicativos online para escrutinização de EDs[9] e os vídeos em que operações, como de ordenação, são dançadas ao acompanhamento musical[10][11][12].

Na visualização dos dados, sejam eles e.g. módulos de componentes frequenciais (e.g. de Fourier) ou valores cujo valor semântico é desconhecido, é muitas vezes necessário utilizar escalas exponenciais/logarítmicas ou que sigam leis de potência[13] para que as estruturas possam ser audiovisualizadas informativamente[14]. De fato, a percepção humana e de outros animais utiliza lineariza estas escalas de modo a obter uma recepção de sinais informativa em um espectro/âmbito largo: ouvimos de forma útil desde um alfinete batendo no chão até um prédio implodindo, ouvimos frequências aproximadamente de 20Hz a 20kHz, enxergamos sob espectros igualmente generosos de intensidade e frequência).[15]

Considerações sobre processamento e transferência[editar | editar código-fonte]

Tome o seguinte contexto atual, bastante frequente e até paradigmático, da computação em nuvem. A recomendação da W3C para a representação de dados e conhecimentos é a utilização de dados ligados, representados em RDF e integrados à web semântica via ontologias OWL ou RDF's e vocabulários SKOS (en). Suponha que haja um cliente que é um navegador/browser de arquivos HTML via HTTP, a exemplo do Firefox, Chrome, ou Chromium. Suponha também que haja e um servidor no software Web em uso ou desenvolvimento, este serve o HTML para o navegador carregar quando recebe uma URL que especifica qual HTML deve entregar.

Neste contexto, descrito favorecendo protocolos e em detrimento à jargões da teoria, é razoável supor que as rotinas a serem executadas pelo navegador, no cliente, e.g. para poupar processamento no servidor ou para fins de interatividade[16][17], serão escritas em Javascript com bibliotecas como D3.js (en). Já as rotinas a serem executadas no servidor, e.g. para elaborar o HTML a ser enviado, acessar bancos de dados e análises estatísticas, serão implementadas em Python, por exemplo, utilizando RDFLib (en), SPARQL (en), NumPy, SciPy, Flask (en). Os casos que não dependem dos processamentos matriciais e massivamente paralelos podem dar preferência à utilização de Node.js para a escrita do servidor.

As seguintes questões dependem expressivamente das EDs escolhidas, seus volumes, processamentos, etc, e são dúvidas honestas, pertinentes e típicas na prática da web semântica:

  • Uma ordenação, ou média ou desvio padrão ou padrão de regex, é mais rápida no endpoint SPARQL (en) (e.g. Jena (computação) (en), Virtuoso (base de dados) (en), RDFLib (en)) ou no Python? Pode ser crucial decidir quais operações devem (ou podem) ser feitas no endpoint, no servidor e no cliente. Fatores:
    • processamento no endpoint SparQL é confiável? Até que ponto? Jena e Virtuoso são considerados estáveis e confiáveis mas ao visitar o histórico das listas são encontrados erros grosseiros de interpretação de chamadas e variações entre os protocolos SPARQL oficial[18], do Jena, Virtuoso e rdflib.[19].
    • Quanto mais dado é enviado para o python (pelo endpoint SPARQL), mais trafego na rede é utilizada.
    • Quanto mais dado é enviado pelo python (para o cliente em Javascript), mais trafego na rede é utilizada.
    • Como regra geral: procura-se maximizar o processamento nas etapas anteriores de acesso aos dados.
    • Como regra geral: procura-se maximizar o processamento nas etapas que possuem as ferramentas apropriadas. Por exemplo, as busca e seleção dos dados são feitas em SPARQL, as otimizações e reconhecimento de padrões em Python, a relatoria e visualização em Javascript. Cada uma destas etapas trasforma EDs em outras EDs para transferência e representação subsequênte. O HTML será muito provavelmente gerado em parte pelo servidor (em Python, e.g. usando Flask ou Django) e em parte pelo cliente (em Javascrit, e.g. usando Bootstrap e D3js).

Em consonância, nas consultas em SPARQL podem ser realizadas as operações:

  • Obtenção de redes/grafos junto a metadados de vértices e arestas.
  • Ordenação.
  • Obtenção de Data cube (en) (dq:Slice) ou dados em segundo algum critério (e.g. âmbito RDF Schema (en)).
  • Obtenção de descritores estatíticos: média, modo, mediana, valores máximos e mínimos, número de palavras, etc.
  • Escrita de dados derivados (gerados pelo servidor/Python ou cliente/Javascript) para consulta posterior.
  • Outras operações dependentes da aplicação, como busca por termos/tokens em dados textuais através de padrões de regex.

No servidor em Python:

  • Obtenção de histogramas, interpolações e obtenção de curvas/distribuições mais próximas (curve fitting), PCA, MDS.
  • Manutenção e registro de estado dos dados, análises, visualizações e manutenção da interface (e.g. dados sobre o usuário, sessão, etc) internamente (no servidor), no endpoint SPARQL e no cliente.
  • Cálculo de quantís e medidas estatísticas mais elaboradas que a descrição obtida diretamente nas consultas SPARQL (e.g. curtose).
  • Síntese de queries SparQL e escrita de arquivos RDF:
    • Para relacionamentos entre dados, análises, audiovisuaizações e interfaces (DAAI[20])
    • Para inferências de relações possíveis em DAAI.

No cliente em Javascript:

Exemplo de questão a ser investigada e para a qual provavelmente há diferentes soluções em uso dada a dependência da aplicação: como prevenir que o usuário faça queries que sobrecarreguem os servidores com leitura e escrita? Por exemplo, um dos princípios da AA estabelece

 "primeiro forneça um panorama geral, os detalhes são sob demanda" -- Ben Shneiderman[21]

Ou seja, há remoção de dados para ênfase no detalhamento ou aquisição de dados para possibilitar a resolução requisitada, e portanto um aplicativo de AA operante na web semântica estará lidando potencialmente com fluxo de dados em todas as direções e até de forma contínua (veja streaming).

Estruturas de dados canônicas[editar | editar código-fonte]

Como evidenciado neste artigo, reconhecida a utilidade da teoria, é pertinente que o programador pondere se há ganho no uso destas estruturas de dados e.g. em Python ou Javascript. De todo modo, a reimplementação destas estruturas é desaconselhada quando há EDs correspondentes disponibilizadas pela linguagem por padrão, pois costumeiramente apresentam otimizações diversas. Esta orientação é válida até mesmo para linguagens consideradas de baixo nível, como C, C++, e Fortran.

Taxonomia de EDs[editar | editar código-fonte]

Pode-se conceitualizar que, na PE, as variáveis são chaves cujos valores são TDs. enquanto na POO os valores são TDs ou TADs. Estes TDs são em geral classificados como[3]: ligados, quando um elemento possui um ou mais ponteiros para outros elementos; lineares, quando os valores se sucedem em sequência; homogeneos, quando os elementos são dos mesmo tipo; heterogêneos, quanto os elementos não são do mesmo tipo.

TADs em geral são concebidos com características similares, o que implica nos padrões de design (veja GoF). As EDs podem se combinar de formas diversas. Por exemplo, um grafo/rede pode ser implementado através de uma ED homogênea e linear (e.g uma matriz) ou de uma ED ligada e.g. em que cada vértice contém os ponteiros para os vértices vizinhos. A escolha dentre estas representações, por exemplo, passa pela observação da densidade do grafo, da necessidade de manipular metadados, vértices heterogêneos, métodos a serem utilizados (e.g. os ciclos são encontrados imediatamente quando a matriz de adjacências está disponível).

Note a severidade da escolha de uma ED para representar um grafo (que é outra ED): os ciclos são encontrados imediatamente quando a matriz de adjacências está disponível, o que facilita análises envolvendo fluxos de informação e a identificação de árvores ou até o estabelecimento de um dipolo árvore - grafo cíclico. Já uma ED ligada é conveniente para a lida com EDs heterogêneas, metadados, e para representação compacta de grafos esparsos (pouco conectados).

Outro exemplo de relacionamento entre EDs: uma ED linear é muitas vezes também homogênea (e.g. uma matriz de inteiros, pixels em triplas RGB hexadecimais, ou amostras de inteiros com sinal, little-endians de 16bits, em um áudio PCM com taxa de amostragem de 44,1kHz amostras por segundo), e é processada com bastante eficiência por processadores massivamente paralelos (en) como os utilizados nas placas gráficas usuais.

Dados relacionais é um termo ambíguo: pode se referir a dados representados em um banco de dados relacional (e.g. MySQL, PostgreSQL) ou a dados caracterizados pelas relações duais/binárias/dicotômicas, paradigmaticamente compreendidos como grafos/redes..

Algumas EDs são básicas/padrão para cada linguagem, e em geral há também suporte para estruturas compostas/elaboradas de dados, que se baseiam nos dados básicos.

Template para formalização de conhecimento sobre uma ED[editar | editar código-fonte]

Observada a literatura[3][4][5], um vocabulário sobre EDs pode seguir o seguinte padrão:

  • Vocabulário em português e inglês e definições e relações entre vocábulos.

Passível de formalização como vocabulário SKOS.

    • (CONCEITO)
    • termos pt-br
    • termos en
    • definição
  • Descrição geral da teoria básica relacionada ao item
  • Tipos de dados relacionados
    • características abstratas (sequência ou conjunto de valores, dicionário, etc)
    • proveniências (áudio, redes sociais, etc)
  • Importância do item e limitações
  • Demais notas teóricas
  • Implementações em Pseudocódigo
  • Ontologias: diagramas de conceitualização passíveis de formalização em OWL, utilizável por máquina para realizar inferências, tomada de decisão e consulta aos dados. Útil para discussões teoricas precisas.
  • Nota histórica
    • origens
    • percurso
    • estado atual da teoria e implementações
  • Problemas típicos e soluções canônicas
  • Aplicações clássicas
  • Exercícios selecionados
  • Usos em aprendizado de máquina, classificação e otimização; Medições
  • Visualizações de informação de dados relacionados ao item. Visualizações/diagramas/imagens pertinentes para a teoria do item.
  • Usos artísticos e educacionais de representações audiovisuais do item; música
    • encontrados na literatura e outros artefatos audiovisuais
    • potenciais
  • Software para implementação. Implementações em linguagens de programação (Python, Scheme, Javascript, Java, C/C++, Fortran, Bash, Vimscript, ChucK, SuperCollider, PD)
  • Bibliografia
    • Livros
    • Artigos recentes que utilizam o item ou sobre desenvolvimentos no item.

Exemplos de EDs canônicas[editar | editar código-fonte]

Edit-delete-not encyclopedic3.svg
Por apresentar uma opinião não atribuída, esta secção parece não ser de natureza enciclopédica.
Justifique o uso dessa marcação e tente resolver essas questões na página de discussão.
Favor adicionar a data dessa marcação, o que pode ser feito automaticamente substituindo essa predefinição por {{subst:não-enc}}.

As definições a seguir são encontradas na literatura com variações. Por recomendação da literatura especializada e da W3C, um vocabulário como o que segue é revisado e mantido por diversos especialistas, de modo a melhor lidar com os significados pois evoluem ao longo do tempo, não são consensuais, e são abundantes em polissemias e sinonímias.[22][23][24]

Um array (também vetor) é uma ED linear e usualmente homogênea. Os ponteiros ficam então implícitos e representados como inteiros, índices para recuperação randômica de valores na ED em tempo constante. A remoção e (inserção de novos valores sem a remoção) pode ser custosa.

Uma lista (abreviação de lista ligada) é uma estrutura de dados linear e heterogênea.

As filas são estruturas baseadas no princípio FIFO (first in, first out) e possuem duas funções básicas: ENQUEUE, que adiciona um elemento ao final da fila, e DEQUEUE, que remove o elemento no início da fila.

A pilha é uma estrutura de dados baseada no princípio LIFO (LAST in, FIRST out). Há duas operações que se aplicam a todas as pilhas: PUSH, que insere um dado no topo da pilha, e POP, que remove o item no topo da pilha.

Em uma árvore, os elementos podem ser ordenados topologicamente de forma consistente. Cada elemento pode possuir um único pai (ou mãe) e um ou mais filhos. Em uma árvore binária, cada nó possui no máximo dois filhos. Árvores com propriedades semelhantes são utilizadas como EDs para buscas (veja árvores de busca binária, árvores AVL e árvore-AA.

A estrutura de grafos é usada quando necessária a representação de sistemas complexos.

A tabela de dispersão implementa o mapeamento entre chaves e valores através de funções de espalhamento (funções hash).

Outras EDs: Deque, fila de prioridade, lista circular, lista ortogonal, artigo principal lista de estruturas de dados.

Tópicos avançados[editar | editar código-fonte]

  • EDs para processamento paralelo.
  • Consideração da Máquina de Turing como ED genérica para paradigma atual de computação por transístores[25].
  • Aprofundamentos sobre POO e EDs[26].
  • Estudo minucioso de propriedades de EDs bastante utilizadas.
  • Análise de novas EDs.

Referências

  1. Paul E. Black (ed.), Data structure. Dictionary of Algorithms and Data Structures. U.S. National Institute of Standards and Technology, 2004. Versão online .
  2. Data structure. Encyclopædia Britannica (2009) Online
  3. a b c CORMEN, T. H.; LEISERSON, C.E.; RIVEST, R.L.; Algoritmos: Teoria e Prática. Editora Campus, Tradução da 2ª edição americana, 2002.
  4. a b Dasgupta, Sanjoy, Christos H. Papadimitriou, and Umesh Vazirani. Algorithms. McGraw-Hill, Inc., 2006.
  5. a b ZIVIANI, N.; Projeto de Algoritmos com implementação em Java e C++. Editora Thomson, 1ª edição, 2006.
  6. https://stackoverflow.com/questions/13530998/python-variables-are-pointers
  7. The art of Unix Programming
  8. CASTRO, L. N. Fundamentals of Natural Computing: Basic Concepts, Algorithms, and Applications, CRC Press, 2006.
  9. https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
  10. https://www.youtube.com/watch?v=lyZQPjUT5B4
  11. https://www.youtube.com/watch?v=Xw2D9aJRBY4
  12. https://www.youtube.com/watch?v=nK_o13c-0lk
  13. Fabbri, R. Oliveira, O. N. O, "A simple model that explains why inequality is ubiquitous", 2017, disponível em: https://github.com/ttm/ubiquitousInequality/blob/master/plosOne/plos_latex_template.pdf
  14. Fabbri, R., Oliveira, M. C. F., Audiovisual Analytics of Social Linked Data", 2017, disponível em: https://github.com/ttm/aavo/raw/master/latex/nuvem/nuvem2.pdf
  15. Physics and psychophysics of music. Roederer
  16. a b artigo sobre o AAVVO
  17. Fabbri, Renato. "Enhancements of linked data expressiveness for ontologies." arXiv preprint arXiv:1710.09952 (2017).
  18. https://www.w3.org/TR/rdf-sparql-query/
  19. lista jena
  20. link para figura
  21. colocar o artigo certo do Ben
  22. Tom Heath and Christian Bizer (2011) Linked Data: Evolving the Web into a Global Data Space (1st edition). Synthesis Lectures on the Semantic Web: Theory and Technology, 1:1, 1-136. Morgan & Claypool.
  23. Allemang, D., & Hendler, J. (2011). Semantic web for the working ontologist: effective modeling in RDFS and OWL. Elsevier.
  24. https://arxiv.org/abs/1710.09952
  25. https://www.quora.com/Is-there-a-theory-of-data-structures
  26. http://www.cs.cornell.edu/people/kopylov/papers/thesis/thesis.pdf


Livro: Estruturas de Dados, Autor: Paulo Veloso/Cleusio dos Santos/Paulo Azeredo/Antonio Furtado, 1984, Editora Campus, ISBN 85-7001-352-3

Ver também[editar | editar código-fonte]