Question

J'ai lu que l'accès aux éléments de l'indice de position peut être fait en temps constant dans un STL deque. Pour autant que je sache, les éléments d'un deque peuvent être stockés dans plusieurs endroits non contigus, ce qui élimine l'accès en toute sécurité grâce à l'arithmétique des pointeurs. Par exemple:

ABC-> defghi-> jkl-> mnop

Les éléments de la file double est constitué au-dessus d'un seul caractère. L'ensemble de caractères dans un groupe indique qu'il est alloué dans la mémoire contiguë (par exemple abc est en un seul bloc de mémoire, defhi est situé dans un autre bloc de mémoire, etc.). Quelqu'un peut-il expliquer comment l'accès par index de position peut être fait en temps constant, surtout si l'élément à accéder est dans le deuxième bloc? Ou est-ce un deque ont un pointeur vers le groupe de blocs?

Mise à jour: Ou est-il une autre mise en œuvre commune pour un deque

Était-ce utile?

La solution

J'ai trouvé cette implémentation deque de Wikipedia :

Le stockage contenu dans plusieurs tableaux plus petits, allouer des tableaux supplémentaires au début ou à la fin, au besoin. L'indexation est mis en oeuvre en maintenant un tableau dynamique contenant des pointeurs pour chacune des matrices plus petites.

Je suppose que cela répond à ma question.

Autres conseils

Les données sont stockées dans deque par chuncks de vecteur de taille fixe, qui sont

pointered par un map (qui est également une partie de vecteur, mais sa taille peut changer)

Le code principal d'une partie de la deque iterator est comme ci-dessous:

/*
buff_size is the length of the chunk
*/
template <class T, size_t buff_size>
struct __deque_iterator{
    typedef __deque_iterator<T, buff_size>              iterator;
    typedef T**                                         map_pointer;

    // pointer to the chunk
    T* cur;       
    T* first;     // the begin of the chunk
    T* last;      // the end of the chunk

    //because the pointer may skip to other chunk
    //so this pointer to the map
    map_pointer node;    // pointer to the map
}

Le code principal d'une partie de la deque est comme ci-dessous:

/*
buff_size is the length of the chunk
*/
template<typename T, size_t buff_size = 0>
class deque{
    public:
        typedef T              value_type;
        typedef T&            reference;
        typedef T*            pointer;
        typedef __deque_iterator<T, buff_size> iterator;

        typedef size_t        size_type;
        typedef ptrdiff_t     difference_type;

    protected:
        typedef pointer*      map_pointer;

        // allocate memory for the chunk 
        typedef allocator<value_type> dataAllocator;

        // allocate memory for map 
        typedef allocator<pointer>    mapAllocator;

    private:
        //data members

        iterator start;
        iterator finish;

        map_pointer map;
        size_type   map_size;
}

Ci-dessous je vais vous donner le code de base de deque, principalement sur deux parties:

  1. iterator

  2. Comment l'accès aléatoire une realize de deque

1. itérateur (__deque_iterator)

Le principal problème de iterator est, quand ++, - iterator, il peut passer à un autre morceau (si ce pointeur vers le bord du morceau). Par exemple, il y a trois blocs de données: chunk 1, chunk 2, chunk 3

.

Les pointeurs de pointer1 au commencent de chunk 2, lorsque l'opérateur --pointer il pointeur vers la fin de chunk 1, de manière à la pointer2.

Ci-dessous je donnerai la fonction principale de __deque_iterator:

Tout d'abord, passez à tout morceau:

void set_node(map_pointer new_node){
    node = new_node;
    first = *new_node;
    last = first + chunk_size();
}

Notez que, la fonction chunk_size() qui calcule la taille de morceau, vous pouvez penser qu'il retourne 8 pour simplifier ici.

operator* obtenir les données dans le bloc

reference operator*()const{
    return *cur;
}

operator++, --

// formes de préfixe d'incrément

self& operator++(){
    ++cur;
    if (cur == last){      //if it reach the end of the chunk
        set_node(node + 1);//skip to the next chunk
        cur = first;
    }
    return *this;
}

// postfix forms of increment
self operator++(int){
    self tmp = *this;
    ++*this;//invoke prefix ++
    return tmp;
}
self& operator--(){
    if(cur == first){      // if it pointer to the begin of the chunk
        set_node(node - 1);//skip to the prev chunk
        cur = last;
    }
    --cur;
    return *this;
}

self operator--(int){
    self tmp = *this;
    --*this;
    return tmp;
}
iterator sauter n étapes / accès aléatoire
self& operator+=(difference_type n){ // n can be postive or negative
    difference_type offset = n + (cur - first);
    if(offset >=0 && offset < difference_type(buffer_size())){
        // in the same chunk
        cur += n;
    }else{//not in the same chunk
        difference_type node_offset;
        if (offset > 0){
            node_offset = offset / difference_type(chunk_size());
        }else{
            node_offset = -((-offset - 1) / difference_type(chunk_size())) - 1 ;
        }
        // skip to the new chunk
        set_node(node + node_offset);
        // set new cur
        cur = first + (offset - node_offset * chunk_size());
    }

    return *this;
}

// skip n steps
self operator+(difference_type n)const{
    self tmp = *this;
    return tmp+= n; //reuse  operator +=
}

self& operator-=(difference_type n){
    return *this += -n; //reuse operator +=
}

self operator-(difference_type n)const{
    self tmp = *this;
    return tmp -= n; //reuse operator +=
}

// random access (iterator can skip n steps)
// invoke operator + ,operator *
reference operator[](difference_type n)const{
    return *(*this + n);
}

2. éléments deque d'accès aléatoire

fonction commune de deque

iterator begin(){return start;}
iterator end(){return finish;}

reference front(){
    //invoke __deque_iterator operator*
    // return start's member *cur
    return *start;
}

reference back(){
    // cna't use *finish
    iterator tmp = finish;
    --tmp; 
    return *tmp; //return finish's  *cur
}

reference operator[](size_type n){
    //random access, use __deque_iterator operator[]
    return start[n];
}

Vous voyez aussi cette question qui donne le code principal de deque

https://stackoverflow.com/a/50959796/6329006

Une de l'application possible peut être un tableau dynamique de réseaux de const taille; un tel tableau de const taille peut être ajouté à l'une des extrémités lorsque plus d'espace est nécessaire. Tous les tableaux sont pleins, sauf, peut-être, pour la première et les derniers qui peuvent être en partie vide. Cela signifie connaître la taille de chaque tableau et le nombre des éléments utilisés dans le premier tableau, on peut facilement trouver une position d'un élément par index.
http: // CPP- tip-of-the-day.blogspot.ru/2013/11/how-is-stddeque-implemented.html

Si deque est implémenté comme un tampon en anneau

scroll top