Is a Linked-List implementation without using pointers possible or not?

前端 未结 14 1543
再見小時候
再見小時候 2020-12-01 07:57

My question is very simple, can one using C++, implement a link-list data structure without using pointers (next nodes)? To further qualify my question, I\'m mean can one cr

相关标签:
14条回答
  • 2020-12-01 08:29

    Can one using C++, implment a link-list data structure without using pointers (next nodes)?
    No.

    0 讨论(0)
  • 2020-12-01 08:30

    Yes:

    class node { 
      std::string filenameOfNextNode;
      std::string filenameOfPrevNode;
      std::string data;
      node nextNode() {
        node retVal;
        std::ifstream file(filenameOfNextNode.c_str());
        retVal.filenameOfNextNode = file.getline();
        retVal.filenameOfPrevNode = file.getline();
        retVal.data = file.getline();
        return retVal;
      }
    };
    

    Inspired by a comment about the origins of linked lists

    0 讨论(0)
  • 2020-12-01 08:31

    Languages that do not support any type of reference can still create links by replacing pointers with array indices. The approach is to keep an array of records, where each record has integer fields indicating the index of the next (and possibly previous) node in the array. Not all nodes in the array need be used. If records are also not supported, parallel arrays can often be used instead.

    As an example, consider the following linked list record that uses arrays instead of pointers:

    record Entry {
    integer next; // index of next entry in array
    string data; // can be anything a struct also. }
    

    Creata an array with a high number. And point listHead to the first indice element at the array

    integer listHead;
    Entry Records[10000];
    

    Check wiki page: http://en.wikipedia.org/wiki/Linked_list for details, search for "Linked lists using arrays of nodes"

    0 讨论(0)
  • 2020-12-01 08:32

    A possible approach would be to use an array of Nodes where each node stores an (array) index to the prev and next Node. Thus, your Node would look something like:

    struct Node 
    {
        T data;
        int prev;    // index of the previous Node of the list
        int next;    // index of the next Node of the list
    }
    

    Additionally, you will probably have to dynamically allocate the Node array, implement some bookkeeping to get and free space in the array: for example a bool array that stores the unoccupied indexes in the Node array, along with two functions that will update it every time a new Node / index is added or removed (it will be fragmented as nodes won't be always contiguous); find an index of a Node in the array: for example by subtracting the address of the Node from the first address of the array.

    Here is how a possible interface of a doubly linked list using the above technique would look like:

    template <typename T>                          // T type Node in your case
    class DList
    {
        T** head;                                  // array of pointers of type Node
        int first;                                 // index of first Node
        int last;                                  // index of last Node
    
        bool* available;                           // array of available indexes 
        int list_size;                             // size of list
    
        int get_index();                           // search for index with value true in bool available
        void return_index(int i);                  // mark index as free in bool available
    
        std::ptrdiff_t link_index(T*& l) const;    // get index of Node
    
        void init();                               // initialize data members
        void create();                             // allocate arrays: head and available
    
        void clear();                              // delete array elements
        void destroy();                            // delete head
    
    public:
        DList();                                   // call create() and init()
        ~DList();                                  // call clear() and destroy()
    
        void push_back(T* l);
        void push_front(T* l);
        void insert(T*& ref, T* l);                // insert l before ref
    
        T* erase(T* l);                            // erase current (i.e. l) and return next
        T* advance(T* l, int n);                   // return n-th link before or after currnet
    
        std::size_t size() const;
        int capacity () const { return list_size; }
    };
    

    You could use that as a benchmark and implement something on your own.

    template <typename T>
    void DList<T>::push_back(T* l)
    {
        if (l == nullptr)
        {
            throw std::invalid_argument("Dlist::push_back()::Null pointer as argument!\n");
        }
    
        int index = get_index();
        head[index] = l;
    
        if (last != -1)
        {
            head[last]->next = index;
            head[index]->prev = last;
        }
        else
        {
            first = index;
            head[index]->prev = -1;
        }
    
        last = index;
        head[index]->next = -1;
    }
    
    0 讨论(0)
  • 2020-12-01 08:33

    One could create a list of cons-cells using temporaries, const references, and inheritance. But you'd have to be very careful not to keep any references to it beyond its lifetime. And you probably couldn't get away with anything mutable.

    This is based roughly on the Scala implementation of these lists (in particular the idea of using inheritance and a NilList subclass rather than using null pointers).

    template<class T>
    struct ConsList{
       virtual T const & car() const=0;
       virtual ConsList<T> const & cdr() const=0;
    }
    
    template<class T>
    struct ConsCell:ConsList{
       ConsCell(T const & data_, ConsList<T> const & next_):
            data(data_),next(next_){}
       virtual T const & car() const{return data;}
       virtual ConstList<T> const & cdr() const{return next;}
    
       private:
         T data;
         ConsList<T> const & next;
    }
    
    template<class T>
    struct NilList:ConsList{  
       // replace std::exception with some other kind of exception class
       virtual T const & car() const{throw std::exception;}
       virtual ConstList<T> const & cdr() const{throw std::exception;}
    }
    
    void foo(ConsList<int> const & l){
       if(l != NilList<int>()){
          //...
          foo(NilList.cdr());
       }
    }
    
    foo(ConsList<int>(1,ConsList(2,ConsList<int>(3,NilList<int>()))));
    // the whole list is destructed here, so you better not have
    // any references to it stored away when you reach this comment.
    
    0 讨论(0)
  • 2020-12-01 08:35

    As an addition to the existing answers of using a previous and a next vector/array, we could build on top of a more dynamically resizing structure, i.e. losing the amortization on the resizing operation.

    Why do I think this is suitable? Well, we have gained some advantages by using vectors/arrays, but we got the amortized resizing in return. If we can rid ourselves of the latter, we may have turned the trade entirely in our favor!

    Specifically, I am referring to Resizable Arrays in Optimal Time and Space. It's a very interesting data structure, particularly as the basis for other data structures such as the one we are talking about.

    Note that I have linked to the technical report, which, unlike the regular paper, also includes the (highly interesting) explanation of how doubly-resizable arrays were achieved.

    0 讨论(0)
提交回复
热议问题