Listas ligadas com a STL

Started by blackwinner, 04 de July , 2008, 02:38:27 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

blackwinner

Bem, resolvi terminar esse tuto aqui.. já faz um tempo que eu to de terminar ele e mais alguns outros sobre a estrutura do win, como ocorrem bugs no win e etc.. mas tava sem tempo, agora que meu curso de manutenção industrial acabou, vo tentar terminar todos..
Mas já pesso desculpas por não ter posto a notação húngara nos códigos, eu fiz isso porque um amigo me pediu, ele disse que a notação h~ deixava ele confuso o0

Let's go..


===========================================

Uma lista é uma simples estrutura de dados..

Como quando declaramos a struct:

struct Lis
{
int valor;
};

Acabamos de declarar uma lista.
O que faz uma lista ser ligada ou não, é a sua capacidade de referenciar outra lista apontando o local na memória em que essa segunda lista está.. isso, através de um ponteiro de mesmo tipo da lista a ser referenciada.

Logo, se fossemos transformar o nosso primeiro exemplo em uma lista ligada, ele ficaria assim:

struct Lis
{
int valor;
Lis *proxima_lista;
};

Na verdade, a primeira lista ainda não está ligada a outra lista.. nós precisamos ainda, alocar no ponteiro "proxima_lista", uma lista.. fazemos isso através do operador new.(alocação dinâmica)
Vou colocar um código exemplo para facilitar o entendimento:

/**BIBLIOTECAS**/
#include <stdlib.h>
#include <iostream>


using namespace std;

/**NOSSA LISTA LIGADA**/
struct Lista{
       int valor;
       Lista *prox_list;
       };

/**MAIN**/

int main()
{
 
/****uma variavel do tipo lista é criada****/
 Lista List1;                     

/****atribui-se um valor inteiro a um de seus membros****/
 List1.valor = 1; 
 
/****o ponteiro recebe o endereço de uma nova lista****/
 List1.prox_list = new Lista;
 
/****O "valor" da nova lista é modificado****/
 List1.prox_list->valor = 2;   

/****endereço nulo para evitar erros*****/
 List1.prox_list->prox_list = 0;
 
 
 /**EXIBE O CONTEÚDO DE "VALOR" EM LIST1**/
 cout << "List1.valor: "
        << List1.valor
        << endl;
 
 /**EXIBE O CONTEÚDO DE "VALOR" EM PROX_LIST**/
 cout << "List1.prox_list->valor: "
        << List1.prox_list->valor
        << endl;
 

     
 delete List1.prox_list;
   
 system("pause > nul");
}

Graficamente, uma lista é representada basicamente por um valor, e um ponteiro para outra lista:




A primeira coisa que você deve estar pensando ao ver isso, é: "porque eu não uso um vetor para armazenar valores ao invés dessa papagaiada toda?"

Esse é o ponto.. matrizes(vetores) e listas tem seus pontos positivos e seus pontos negativos..

Imagine que você tem um vetor com 10 valores numéricos.. 0, 1, 2, 3 e assim por diante..


(não se importe com as imperfeições.. eu sou programador não designer gráfico =P)

Agora imagine que você precisa adicionar um elemento no antes do primeiro valor..
Você teria que copiar todo o vetor(vetor1) para um outro vetor(vetor2), desalocar e alocar novamente a memória do vetor1, dessa vez com espaço para mais um elemento e depois colocar esse novo valor numérico e logo após, copiar todos os elementos do vetor2 para o vetor1.

Enfim, nesse processo, você criaria um novo vetor só para alocar os antigos valores, iria mover todos os elementos uma casa.. nesse processo, a memória ocupada pelo vetor1 seria dobrada já que o vetor2 teria que ser movido.

Agora imaginemos a mesma situação, só que utilizando uma lista ligada.

Bem, eu vou mostrar essa situação na prática:

#include <stdlib.h>
#include <iostream>


using namespace std;


struct Lista{
       int valor;
       Lista *prox_list;
       };



void Desaloca(Lista *Prm)
{
     Lista *Crt = Prm->prox_list, **Crt2;
     int i = 1;
     Crt2 = 0;
     while (i)
      {
       if (!Prm->prox_list)
        {
         delete Prm;
         i = 0;
        }
       else if(!Crt->prox_list)
        {
       
         *Crt2 = 0;
         if (Crt == Prm->prox_list)
          {
           Crt2 = &Prm->prox_list;
           delete Crt;
           *Crt2 = 0;
          }
         else
          {
           delete Crt;
           Crt = Prm->prox_list;}
          }
       else
        {
         Crt2 = &Crt->prox_list;
         Crt = Crt->prox_list;
        }
      }
     
}

int main()
{
 

 Lista *Primeira, *Primeira2;
 
 Primeira = new Lista;
 Primeira->valor = 0;
 Primeira->prox_list = new Lista;
 Primeira->prox_list->valor = 1;
 Primeira->prox_list->prox_list = new Lista;
 Primeira->prox_list->prox_list->valor = 2;
 Primeira->prox_list->prox_list->prox_list = 0;
 
 Primeira2 = Primeira;
 Primeira = new Lista;
 Primeira->valor = 22;
 Primeira->prox_list = Primeira2;
 Primeira2 = 0;
 
 cout << "valor do novo primeiro elemento: " << Primeira->valor << endl;
 cout << "valor do antigo primeiro elemento: " << Primeira->prox_list->valor << endl;
 
 Desaloca(Primeira);
     

   
 system("pause > nul");
}

Esse código está um pouco mais difícil de entender, só coloquei a função desaloca para vocês poderem ver a dificuldade de acessar os elementos desse tipo de lista..(acesso sequencial)
Eu vou explica-lo parte-a-parte e foi por isso que eu omiti propositalmente linhas de comentário.

Eu vou continuar esse tuto ainda.. essa primeira parte está longe de acabar..
Na próxima vez que eu postar aqui, vou explicar o código acima, explicar os conceitos de listas singularmente/duplamente ligadas, listas de saltos e chegar finalmente a STL(biblioteca de formatos padrão) e a diferença entre list e deque.
Como eu disse, a primeira parte está longe de acabar.. mas por enquanto vou deixa-los se acostumarem com o que já aprenderam.. flws
sergaralho.blogspot.com --> a informação como ela deve ser.. pura!

blackwinner

Vamos continuar, dando início a explicação do código anterior..

============================================================================

#include <stdlib.h>
#include <iostream>


using namespace std;


struct Lista{
       int valor;
       Lista *prox_list;
       };

Até a seguinte parte, nós já conhecemos, então vamos pula-la.
Nós temos que lembrar que estamos frequentemente lidando com alocação dinâmica, então temos que liberar a memória alocada.

Eu defini essa função(Desaloca()), justamente para vocês entenderem o modo que os elementos de uma lista ligada podem ser acessados.
Nesse caso, nós estamos lidando com uma lista singularmente ligada, ou seja, que possui apenas um ponteiro que aponta para a próxima lista da chain(cadeia).
Por só termos um ponteiro, nós temos que acessar cada novo elemento de uma lista, de uma forma sequencial, um após o outro.

O ponteiro Crt, é a abreviação de current, e é o ponteiro que eu uso na função para mostrar a minha posição atual(current) na cadeia.
Então, a cada posição que eu me movo na cadeia, eu utilizo "*Crt" como uma flag que me diz a minha atual posição.

Isso parece estranho em uma lista com três elementos, agora imagine se eu tivesse que escrever 5 elementos?

delete Primeira->prox_list->prox_list->prox_list->prox_list->prox_list;
Seria algo assim, agora imagine uma lista com 50 elementos?
Nem, vou me preocupar em escrever o código inteiro.
Com "*Crt" apontando para minha posição, independente da posição na qual estou dentro da chain, para me referir ao próximo elemento, eu sempre usarei apenas:
Crt->prox_list;


Agora vamos dar uma segunda olhada na função desaloca, dessa vez, um pouco mais comentada e legível:

/**Desaloca-Declaração**/
void Desaloca(Lista *Prm)
{
     /*VARIAVEIS
     */
     Lista *Crt = Prm->prox_list, **Crt2;
     Crt2 = 0;
     
     while (1)
      {
       
       /**Se Prm for a única lista da chain
           Deleta Prm e sai do loop
       **/
       if (!Prm->prox_list)
        {
         delete Prm;
         break;
        }
       
       
        /**Se a minha atual posição na chain for a última**/
       else if(!Crt->prox_list)
        {
       

       
        if (Crt == Prm->prox_list)
         {
          delete Crt;
          Prm->prox_list = 0;
         }
       
        else
         {
          *Crt2 = 0;
          delete Crt;
          Crt = Prm->prox_list;
         }
       
        }//else if(!Crt->prox_list)
       
        /**Senão**/
       else
        {
         Crt2 = &Crt->prox_list;
         Crt = Crt->prox_list;
        }
      } //while(1)
     
}//Desaloca;

Primeiro nós temos a declaração das variáveis..
Nessa função, nós utilizamos 3 variaveis..

*Prm-> que é um ponteiro para o primeiro membro da chain

*Crt->é um ponteiro para minha posição atual dentro da chain, ele passa a apontar o mesmo setor da memória que o ponteiro prox_list do primeiro elemento da lista aponta.

**Crt-> que é um ponteiro para ponteiro, utilizado para atribuir o valor 0(zero) ao ponteiro da lista que foi "deletada".
(lembre-se que depois que a memória é liberada pela função delete, o ponteiro ainda existe e está apontando ainda para o mesmo setor da memória, o que é prejudicial para o nosso algoritmo)

----------------

Esse algoritmo se baseia na idéia de que o último ponteiro membro da última lista da chain, recebeu 0(zero) como valor.
Apartir dai, ele começa a procurar num loop aparentemente infinito, qual elemento da chain, possui o membro prox_list, apontando para valor algum(0/zero).

No primeiro passo, ele verifica se o primeiro membro da lista é também o último:
if (!Prm->prox_list)
        {
         delete Prm;
         break;
        }


Quero lembra-los que 0(zero) é o único valor que possui valor lógico FALSE(0/zero).. todos os outros valores, possuem o valor lógico TRUE(1/um).
Valor lógico é diferente de valor real.
Então, se o ponteiro estiver apontando para qualquer outro valor que não seja 0(zero), o bloco de comandos não será executado.
Nós poderímos trocar a expressão acima por:

if (Prm->prox_list == 0)
        {
         delete Prm;
         break;
        }

Se o primeiro elemento da chain for também o último, então a memória é liberada e saimos do loop e a função Desaloca é encerrada.

O segundo passo, é verificar se a minha posição atual dentro da chain é a posição do último elemento da chain.

else if(!Crt->prox_list)
        {
       
       
       
        if (Crt == Prm->prox_list)
         {
          delete Crt;
          Prm->prox_list = 0;
         }
       
        else
         {
          *Crt2 = 0;
          delete Crt;
          Crt = Prm->prox_list;
         }
       
        }//else if(!Crt->prox_list)

Se a minha posição atual é também a última, a função verifica se minha atual prosição é a segunda posição da chain.
Isso porque o ponteiro Crt recebe já na sua declaração, a segunda posição da chain.. se isso ocorre, o ponteiro para ponteiro **Crt2 não estará apontando para a posição na memória do ponteiro que referenciava a chain que será deletada.
Então utiliza -lo para fazer esse suposto ponteiro receber 0(zero) como valor, será uma estratégia nula.

Então nós simplesmente liberamos a memória:
delete Crt;
e fazemos o ponteiro que antes referenciava a segunda posição da chain, receber 0(zero) manualmente.
Prm->prox_list = 0;
Se a minha posição atual na chain não é a segunda:

else
         {
          *Crt2 = 0;
          delete Crt;
          Crt = Prm->prox_list;
         }

Então podemos fazer o ponteiro que referenciava o elemento da chain que será "apagado", receber o valor 0(zero) através do ponteiro para ponteiro **Crt2.

*Crt2 = 0;
Liberamos a memória antes ocupada pelo atual e último elemento da chain:

delete Crt;
e Crt recebe novamente o segundo elemento da chain.
Ou seja, ele sempre vai partir do segundo elemento até o último, deletar o último e voltar para o segundo.. o que torna essa função um pouco "primitiva".. mas eu escolhi ela justamente para insentivar vocês para criar funções mais "enxutas".

Crt = Prm->prox_list;
E chegamos a última parte dessa função..
Se nossa atual localização ainda não é o último elemento da chain:

else
        {
         Crt2 = &Crt->prox_list;
         Crt = Crt->prox_list;
        }

**Crt2, recebe o endereço do ponteiro que referencia o próximo elemento da chain:

Crt2 = &Crt->prox_list;
Crt recebe o próximo elemento da chain:

Crt = Crt->prox_list;
e a função volta ao começo do loop até que seja interrompido.

------------------------

O código inteiro do programa, dessa vez mais comentado, fica assim:

#include <stdlib.h>
#include <iostream>


using namespace std;


/**Lista principal**/
struct Lista{
       int valor;
       Lista *prox_list;
       };


/**Desaloca-portótipo**/
void Desaloca(Lista *);



/**MAIN**/
int main()
{
 
/**Ponteiros para Lista**/
 Lista *Primeira, *Primeira2;
 
 /**é alocada uma nova lista**/
 Primeira = new Lista;
 
 /**O membro "valor" dessa lista recebe 0(zero)**/
 Primeira->valor = 0;
 
 /*
 Novas listas são alocadas apartir da primeira
 e em cada nova lista, o membro "valor" recebe um um valor numérico
 Respectivamente:
                 0(já alocado anteriormente)
                 1
                 2
                 
 */
 Primeira->prox_list = new Lista;
 Primeira->prox_list->valor = 1;
 Primeira->prox_list->prox_list = new Lista;
 Primeira->prox_list->prox_list->valor = 2;
 
 /**Ultimo ponteiro recebe 0(zero)**/
 Primeira->prox_list->prox_list->prox_list = 0;
 

/*
  Aqui, começa a operação de inserção de uma nova lista no começo da chain..
 
  "Primeira2" "segura" o endereço da Primeira lista da chain;
  Em "Primeira" é alocada uma nova lista;
  O membro valor dessa nova lista é alterado para 22;
  Essa lista é ligada a antiga primeira lista através do membro prox_list e
   do ponteiro "primeira2" que estava "guardando" esse endereço;
  Assim que a operação termina, "Primeira2" volta a receber 0(zero);
*/
 Primeira2 = Primeira;
 Primeira = new Lista;
 Primeira->valor = 22;
 Primeira->prox_list = Primeira2;
 Primeira2 = 0;


/*
  Aqui são mostrados os valores da primeira lista da chain(nova)
   e da segunda respectivamente(que era a primeira)
*/
 cout << "valor do novo primeiro elemento: "
      << Primeira->valor
      << endl;
     
 cout << "valor do antigo primeiro elemento: "
      << Primeira->prox_list->valor
      << endl;
 
 
 
 /*
  A memória ocupada por cada membro da lista é desalocada
  E o programa termina sua execução
 */
 Desaloca(Primeira);

   
 system("pause > nul");
}



/**Desaloca-Declaração**/
void Desaloca(Lista *Prm)
{
     /*VARIAVEIS
     */
     Lista *Crt = Prm->prox_list, **Crt2;
     Crt2 = 0;
     
     while (1)
      {
       
       /**Se Prm for a única lista da chain
           Deleta Prm e sai do loop
       **/
       if (!Prm->prox_list)
        {
         delete Prm;
         break;
        }
       
       
        /**Se a minha atual posição na chain for a última**/
       else if(!Crt->prox_list)
        {
       
       
       
        if (Crt == Prm->prox_list)
         {
          delete Crt;
          Prm->prox_list = 0;
         }
       
        else
         {
          *Crt2 = 0;
          delete Crt;
          Crt = Prm->prox_list;
         }
       
        }//else if(!Crt->prox_list)
       
        /**Senão**/
       else
        {
         Crt2 = &Crt->prox_list;
         Crt = Crt->prox_list;
        }
      } //while(1)
     
}//Desaloca;

Aqui está descrita toda a operação de inserir um novo primeiro elemento na chain:

/*
  Aqui, começa a operação de inserção de uma nova lista no começo da chain..
 
  "Primeira2" "segura" o endereço da Primeira lista da chain;
  Em "Primeira" é alocada uma nova lista;
  O membro valor dessa nova lista é alterado para 22;
  Essa lista é ligada a antiga primeira lista através do membro prox_list e
   do ponteiro "primeira2" que estava "guardando" esse endereço;
  Assim que a operação termina, "Primeira2" volta a receber 0(zero);
*/
 Primeira2 = Primeira;
 Primeira = new Lista;
 Primeira->valor = 22;
 Primeira->prox_list = Primeira2;
 Primeira2 = 0;

Não vou me prender muito nesse caso porque ele é bem simples, então vamos prosseguir com o tuto já que criar listas ligadas não é o fóco dele..

=========================================================

Como eu já disse, o que vocês acabaram de ver é uma lista singularmente ligada, ou seja, só possui um ponteiro que referencia o próximo elemento da chain.
Existem vários tipos de listas, isso porque, o ponteiro não precisa sempre referenciar o mesmo tipo de lista.

Existem listas duplamente ligadas, que possuem dois ponteiros, um indicando o próximo membro da chain e outro indicando o membro anterior.

Existem listas de saltos, em que a cada n elementos, um elemento com n ponteiros é alocado, todos apontando membros posterioes da lista.(o que tira a impossibilidade de acesso não-sequencial para as listas ligadas)

Listas ligadas e matrizes como eu disse, possuem lá suas vantagens e desvantagens.

Cada elemento de uma matriz pode ser acessado individualmente, ou seja, enquanto em uma lista ligada eu precisaria percorrer uma boa parte dela para acessar um elemento entre o início e o fim da lista.. em uma matriz eu só precisaria fazer algo como:
matriz[5] = 1;
Matrizes ocupam exatamente o tamanho necessário.. Listas ligadas tem pelo menos um ponteiro, é algo insignificante quando estamos falando de 3 elementos em uma chain, mas se aumentarmos o valor, concerteza faz muita diferença.

Enquanto modificar o valor de um elemento no meio de uma matriz não custa muito, inserir um elemento no meio de uma matriz obriga que todos os elementos posteriores sejam movidos uma posição.. listas ligadas, o processo não tem proporções globais como em matrizes.

Por isso, cada caso é um caso, você deve avaliar bem antes de usar uma lista ligada, o mesmo eu digo para uma matriz.

Então agora, vamos ver como a STL trata listas ligadas.

A uma lista como uma lista duplamente ligada genérica, com ponteiros para o próximo elemento e para o anterior.
A classe list pode ser utilizada desde que você inclua o header
#include <list>
Veremos agora uma lista das funções membros incluidas no container:

list<T> lista;
---------------------------------------------------------------------------------------------------------------------------------------

void assign(primeiro, ultimo): Remove todos os nós da lista e insere nela os elementos a partir do intervalor indicado pelos iterators primeiro e ultimo.

void assign(n, elemento = T()): Remove todos os nós da lista e insere nela n cópias de elemento(default t()).

T& back(): Retorna o elemento do último nó da lista.

iterator begin(): Retorna um iterator que referencia o primeiro nó da lista.

void clear(): Remove todos os nós da lista.

bool empty(): Retorna TRUE se a lista estiver vazia, caso contrário retorna FALSE.

iterator end(): Retorna um iterator adiante do último nó da lista.

iterator erase(ite): Remove o nó referenciado pelo iterator ite e retorna um iterator para o elemento seguinte.

T& front():Retorna o elemento do primeiro nó da lista.

iterator insert(ite, elemento = T()): Insere elemento antes do nó referenciado pelo iterator ite e retorna o iterator referenciado pelo novo nó.

void insert(ite, n, el = T()): Insere n cópias de elemento antes do nó referenciado pelo iterator ite.

void insert(ite, primeiro, ultimo): Insere elementos apartir da posição referenciada por primeiro até a posição referenciada por ultimo antes do nó referenciado pelo iterator ite.

List(): Constrói uma lista vazia.
- os construtores seguem a mesma lógica de assign.

size_type max_size() const: Retorna o número máximo de nós para a lista.

void merge(lista): Ordena lista.

void merge(lista, func): Ordena lista na ordem especificada pela função func.

void pop_back(): Remove o último nó da lista.

void pop_front(): Remove o primeiro nó da lista.

void push_back(elemento): Insere elemento no final da lista.

void push_front(elemento): Insere elemento no início da lista.

reverse_iterator rbegin(): Retorna um iterator que referencia o último nó da lista.

void remove(elemento): Remove da lista todos os nós que incluem elemento.

void remove_if(func): Remove da lista todos os nós da lista para os quais a função func(booleana) retorna TRUE.

reverse_iterator rend(): Retorna um iterator antes do primeiro nó da lista.

void resize(n, elemento = T()): Faz uma lista ter n nós adicionando se necessário mais nós com elemento ou descartando nós excedentes.

void reverse(): Inverte a lista.

size_type size() const: Retorna o número de nós na lista.

void sort(): Ordenas os elementos da lista em ordem crescente.

void sort(func): Ordena os elementos da lista na ordem especificada pela função fun(booleana).

void splice(ite, lista): Remove os nós da lista e os insere em uma lista antes da posição referenciada pelo iterator ite.

void swap(lista): Troca o conteúdo da lista atual com o conteúdo da lista passada como parâmetro.

void unique(): Remove os elementor duplicados da lista ordenada.

void unique(func): Remove os elementos duplicados da lista ordenada onde o ser duplicado é especificado pela função func(booleana, dois argumentos).

---------------------------------------------------------------------------------------------------------------------------------------

Bem, está ae uma lista de algumas funções membro, mas não todas..
Mais tarde eu vou fazer um programa que explica a utilização de uma boa parte dessas funções..

Flws, até a próxima.
sergaralho.blogspot.com --> a informação como ela deve ser.. pura!

blackwinner

Usando Lists, a primeira perspectiva que se tem, é de que estamos usando um simples vector.. isso porque como foi dito, vetores e listas ligadas tem muita coisa em comum.

#include <list>
#include <algorithm>
#include <iostream>


using namespace std;

int main()
{
    list<int> List;
   
    for(int iCont = 0; iCont < 10; iCont++)
     List.push_back(iCont);
   
}

A STL é uma biblioteca ótima por causa disso.. em certos programas, você pode resolver problemas de velocidade ou alocação de memória, simplesmente mudando um tipo de uma estrutura.

Nesse caso, trocar a list por um vector ou por um deque daria no mesmo.. o que é importante perceber é que apesar de parecido, "focinho de porco não é tomada".. o container list não tem os métodos, at,  capacity e reserve encontrados no container vector.

A maior diferença entre listas ligadas e vetores fica pra parte teórica quando se fala da STL, mas o método push_front é um dos métodos que estão no container list e não estão no container vector..


Aqui vai um programa bem simples que mostra os valores numéricos de uma lista singularmente ligada(list).

#include <list>
#include <algorithm>
#include <iostream>
#include <stdlib.h>



using namespace std;

int main()
{
 list<int> List;
   
 for(int iCont = 0; iCont < 10; iCont++)
  List.push_back(iCont);
   
 for(int iCont = 0; iCont < 10; iCont++)
  List.push_back(iCont);
 
 list<int>::iterator it1 = List.begin();
 


 cout << *it1;

 List.push_front(32);
 
 it1 = List.begin();
 
 cout << endl << *it1; 
   
 cout << "\nMostrando todo conteudo da lista \n"  ;
 for (int iCont = 0; iCont < List.size(); iCont++)
 {
  cout << *it1 << endl;
  it1++;
 }
 
 it1 = List.begin();
 
 cout << "\n\nMais uma vez\n";
 List.remove_if(bind2nd(less<int>(), 5));
 
 for (int iCont = 0; iCont < List.size(); iCont++)
 {
  cout << *it1 << endl;
  it1++;
 }
 
 
   
 system("pause > nul");
}

Na próxima já começo a falar de deques pra encerrar.. fuiz
sergaralho.blogspot.com --> a informação como ela deve ser.. pura!

blackwinner

A deque(double ended queue), lista de duas extremidades.. como o nome já diz, é implementada como uma lista duplamente ligada.. a grande novidade da deque em relação a list é que ela combina a velocidade de inserção de novos elementos das listas ligadas com o acesso não sequencial dos vectors.

Eu vou passar rápido pela deque porque acredito que seja de fácilimo entendimento..

Vamos primeiro ver uma lista de seus métodos:

void assign(primeiro, ultimo)
Remove os elementos da deque e insere nela os elementos especificados no intervalo
- assign(n, elementos)
 insere n elementos

T& at(n)
Retorna o elemento da posição n

T& back()
Retorna o ultimo elemento da deque

bool empty()
Retorna true se a deque não contém elementos

T& operator []
Operador subscrito


É claro que o container deque contém muito mais métodos que isso mas eu enfatizei apenas os que o container list não possui..
Vamos ver uma pequena demonstração do seu uso:

#include <deque>
#include <algorithm>
#include <iostream>
#include <stdlib.h>

using namespace std;

int main()
{
 deque<char> String;
 
 for (int iCont = 0; iCont < 10; iCont++)
 {
  String.push_back('a'+iCont);
 }
 deque<char>::iterator it1 = String.begin();
 for (int iCont = 0; iCont < String.size(); iCont++)
 {
  cout << *it1;
  it1++;
 }
 
 cout << endl << String[3] << String.at(4) << "que";
 system("pause > nul");
}


Finished.. até que enfim. =P
sergaralho.blogspot.com --> a informação como ela deve ser.. pura!

#phobia

lol

Não sei como ninguém não comentou!

Parabéns pelo tuto cara, muito bem explicado...

Vlw!



Ponto Positivo pra ti pelo tuto e pela participação no fórum com outros posts interessantes.. ;)