¿Cómo navegar por un vector usando iteradores? (C ++)

105

El objetivo es acceder al elemento "n-ésimo" de un vector de cadenas en lugar del operador [] o el método "at". Por lo que tengo entendido, los iteradores se pueden usar para navegar a través de contenedores, pero nunca antes había usado iteradores y lo que estoy leyendo es confuso.

Si alguien pudiera darme alguna información sobre cómo lograrlo, se lo agradecería. Gracias.

Kevin
fuente
¿No son los vectores exclusivos de STL de C ++? Lo editaré independientemente
kevin
kevin: vector es un término genérico que puede ser utilizado por cualquier idioma, en particular los relacionados con las matemáticas como Mathematica o Matlab.
Gabe
@michael, sí, jaja, lo edité después del comentario de gabe.
Kevin

Respuestas:

112

Debe hacer uso del método beginy endde la vectorclase, que devuelve el iterador que hace referencia al primer y último elemento respectivamente.

using namespace std;  

vector<string> myvector;  // a vector of stings.


// push some strings in the vector.
myvector.push_back("a");
myvector.push_back("b");
myvector.push_back("c");
myvector.push_back("d");


vector<string>::iterator it;  // declare an iterator to a vector of strings
int n = 3;  // nth element to be found.
int i = 0;  // counter.

// now start at from the beginning
// and keep iterating over the element till you find
// nth element...or reach the end of vector.
for(it = myvector.begin(); it != myvector.end(); it++,i++ )    {
    // found nth element..print and break.
    if(i == n) {
        cout<< *it << endl;  // prints d.
        break;
    }
}

// other easier ways of doing the same.
// using operator[]
cout<<myvector[n]<<endl;  // prints d.

// using the at method
cout << myvector.at(n) << endl;  // prints d.
codaddict
fuente
5
Esto pasa por alto el hecho de que std::vectortiene iteradores de acceso aleatorio.
sbi
24
Independientemente de si sabe que el tipo de iterador es de acceso aleatorio o no, la "mejor" manera de mover un iterador hacia adelante n espacios es no escribir su propio bucle, sino llamar std::advance(it, n). Está definido para hacer exactamente lo que desea y se usará automáticamente it + nsi el iterador está etiquetado como de acceso aleatorio, o hará el ciclo si es necesario.
Steve Jessop
61

Normalmente, los iteradores se utilizan para acceder a elementos de un contenedor de forma lineal; sin embargo, con los "iteradores de acceso aleatorio", es posible acceder a cualquier elemento de la misma forma que operator[].

Para acceder a elementos arbitrarios en un vector vec , puede utilizar lo siguiente:

vec.begin()                  // 1st
vec.begin()+1                // 2nd
// ...
vec.begin()+(i-1)            // ith
// ...
vec.begin()+(vec.size()-1)   // last

El siguiente es un ejemplo de un patrón de acceso típico (versiones anteriores de C ++):

int sum = 0;
using Iter = std::vector<int>::const_iterator;
for (Iter it = vec.begin(); it!=vec.end(); ++it) {
    sum += *it;
}

La ventaja de usar iterador es que puede aplicar el mismo patrón con otros contenedores :

sum = 0;
for (Iter it = lst.begin(); it!=lst.end(); ++it) {
    sum += *it;
}

Por esta razón, es realmente fácil crear un código de plantilla que funcione igual independientemente del tipo de contenedor . Otra ventaja de los iteradores es que no asume que los datos residen en la memoria; por ejemplo, se podría crear un iterador directo que pueda leer datos de un flujo de entrada, o que simplemente genere datos sobre la marcha (por ejemplo, un generador de rango o de números aleatorios).

Otra opción usando std::for_eachy lambdas:

sum = 0;
std::for_each(vec.begin(), vec.end(), [&sum](int i) { sum += i; });

Desde C ++ 11, puede utilizar autopara evitar especificar un nombre de tipo muy largo y complicado del iterador como se vio antes (o incluso más complejo):

sum = 0;
for (auto it = vec.begin(); it!=vec.end(); ++it) {
    sum += *it;
}

Y, además, hay una variante más simple para cada variante:

sum = 0;
for (auto value : vec) {
    sum += value;
}

Y finalmente, también hay std::accumulateque tener cuidado si está agregando números enteros o de punto flotante.

Michael Aaron Safyan
fuente
53

En C ++ - 11 puede hacer:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto i : v)
{
   // access by value, the type of i is int
   std::cout << i << ' ';
}
std::cout << '\n';

Consulte aquí las variaciones: https://en.cppreference.com/w/cpp/language/range-for

lashgar
fuente
4
¿POR QUÉ ESTO TIENE CERO GUSTOS? <3
jperl
3
@jperl Publicado 8 años tarde. Tomará los próximos 8 años para obtener suficientes votos a favor :)
lashgar
@jperl, bueno, la respuesta está fuera de tema. Aunque esta función de bucle es agradable, no te ayuda a saber cuándo estás en el enésimo elemento, que es la pregunta del OP. Además, cualquier respuesta que requiera O (n) tiempo de complejidad, como esta, es muy mala. Acceder al enésimo elemento de un vector siempre debe ser O (1).
Elliott
@lashgar Intenté esto con array pero fallé. ¿Funciona para matriz?
era s'q
@ eras'q, probé con gcc 7.5.0en Ubuntu 18.04 y funciona para la matriz de la misma manera.
Lashgar
17

Los iteradores de Vector son iteradores de acceso aleatorio, lo que significa que se ven y se sienten como punteros simples.

Puede acceder al enésimo elemento agregando n al iterador devuelto por el begin()método del contenedor , o puede usar el operador [].

std::vector<int> vec(10);
std::vector<int>::iterator it = vec.begin();

int sixth = *(it + 5);
int third = *(2 + it);
int second = it[1];

Alternativamente, puede utilizar la función de avance que funciona con todo tipo de iteradores. (Debería considerar si realmente desea realizar un "acceso aleatorio" con iteradores de acceso no aleatorio, ya que eso podría ser algo costoso).

std::vector<int> vec(10);
std::vector<int>::iterator it = vec.begin();

std::advance(it, 5);
int sixth = *it;
UncleBens
fuente
1
También se puede utilizar advancepara iteradores de acceso aleatorio, o iteradores de categoría desconocida, ya que se garantiza que operará en tiempo constante en ese caso. Esta es la razón por la que los iteradores definidos por el usuario deben etiquetarse correctamente.
Steve Jessop
De hecho, pero advancees realmente molesto de usar (debido al uso del parámetro out) si sabe que está tratando con iteradores de acceso aleatorio. Solo lo recomendaría en código genérico, y si no se usa mucho (si el algoritmo no admite bien los iteradores de acceso no aleatorio, que así sea, por ejemplo, std::sort podría ordenar un std::listpero no lo hace porque sería ridículamente ineficiente ).
UncleBens
Claro, el ejemplo clásico sería si su algoritmo solo necesita un InputIterator, pero por alguna razón, a veces se salta, por lo que desea que sea más eficiente si el iterador tiene acceso aleatorio. No vale la pena restringir su algoritmo al acceso aleatorio solo usando operator+. Pero la pregunta era explícitamente sobre el vector, por lo que no hay nada de malo en la primera parte de su respuesta. Solo pensé que la segunda parte podría implicar "no se puede usar el avance con iteradores de acceso aleatorio, incluso si lo desea" para alguien que nunca ha visto advanceantes.
Steve Jessop
Bien, reformulé ese bit y di el ejemplo con un vector.
UncleBens
segunda línea, Vectordebe estar en minúsculas
Lei Yang
0

A continuación, se muestra un ejemplo de cómo acceder al ithíndice de std::vectorun std::iteratordentro de un bucle que no requiere incrementar dos iteradores.

std::vector<std::string> strs = {"sigma" "alpha", "beta", "rho", "nova"};
int nth = 2;
std::vector<std::string>::iterator it;
for(it = strs.begin(); it != strs.end(); it++) {
    int ith = it - strs.begin();
    if(ith == nth) {
        printf("Iterator within  a for-loop: strs[%d] = %s\n", ith, (*it).c_str());
    }
}

Sin un bucle for

it = strs.begin() + nth;
printf("Iterator without a for-loop: strs[%d] = %s\n", nth, (*it).c_str());

y usando el atmétodo:

printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());
hmofrad
fuente