简体   繁体   English

使用指针而不是 position 在链表中插入元素

[英]Insert an element at a linked list by using a pointer and not position

I am using a custom Doubly Linked List to implement a heuristic algorithm for my thesis.我正在使用自定义双向链表为我的论文实现启发式算法。 I have already implemented the basic functions of a list (insert, delete etc.) and some more that are useful for the case of the problem that I am studying.我已经实现了列表的基本功能(插入、删除等),还有一些对我正在研究的问题有用的功能。

However I am a little bit troubled with the following implementation.但是,我对以下实现有点困扰。 Lets say I have a list: A->E->B->D and I want to insert the element C at the the best possible position inside the list.假设我有一个列表:A->E->B->D,我想将元素C插入列表中最好的 position。 The objective function that decides the optimal position is not relevant so I won't include it.决定最优 position 的目标 function 不相关,所以我不会包括它。

The current implementation is the following: I call the function findBestPos(C) which returns a pointer ptr that points to the left element of the best insert position. For example, if ptr points to E, then the best insert position will be between the elements E and B.当前的实现如下:我调用 function findBestPos(C),它返回一个指向最佳插入 position 左侧元素的指针ptr 。例如,如果ptr指向 E,那么最佳插入 position 将介于元素E和B。

After that, I call the function insertAt(leftElement, insertElement) which inserts the insertElement like this:之后,我调用 function insertAt(leftElement, insertElement) 插入 insertElement,如下所示:

if(leftElement->next != nullptr){
    leftElement->next->prev = insertElement;
}

insertElement->next = leftElement->next;
leftElement->next = insertElement;
insertElement->prev = leftElement;

However, I keep wondering if an implementation like this is "legal".但是,我一直想知道这样的实现是否“合法”。 I mean, from my little experience with C++ and other languages' containers we always use the position to define where we want to insert our new element inside a container.我的意思是,根据我对 C++ 和其他语言容器的一点经验,我们总是使用 position 来定义我们想要在容器中插入新元素的位置。 Surely, using position will take some more time cause of the comparisons but it seems more natural, safe and reusable.当然,使用 position 会花费更多时间进行比较,但它似乎更自然、更安全且可重复使用。

Thank you in advance.先感谢您。

Update更新

After @TedLyngmo's comment, I would like to include some clarifications regarding the findBestPos functionality and to demonstrate other cases that I am using similar optimizations.在@TedLyngmo 的评论之后,我想包括一些关于 findBestPos 功能的说明,并展示我正在使用类似优化的其他情况。 What I basically have is two lists:我基本上有两个列表:

  • Unvisited无人问津
  • Walk

The Unvisited list, contains all the nodes that will be considered to get inserted into the Walk list. Unvisited 列表包含将被视为插入到 Walk 列表中的所有节点。 The Walk list is basically a route of nodes that I will visit.步行列表基本上是我将访问的节点路线。 Each node has some constant values like a visitDuration and some variables like arrivalTime, departureTime etc. that change at runtime.每个节点都有一些常量值,如 visitDuration 和一些变量,如 arrivalTime、departmentTime 等,它们在运行时会发生变化。 Also, moving from one node to another, takes some time which in my case is constant for each pair of nodes.此外,从一个节点移动到另一个节点需要一些时间,在我的例子中,每对节点都是恒定的。

Let's say that I am considering to insert node k between nodes i and j of Walk.假设我正在考虑在 Walk 的节点 i 和 j 之间插入节点 k。 The insertion cost function is:插入成本 function 为:

shift(k) = travelTime(ik) + waitDuration(k) + visitDuration(k) + travelTime(kj) - travelTime(ij)

I calculate this cost for every possible position inside the Walk.我为 Walk 内每一个可能的 position 计算了这个成本。 The position that minimizes this cost is the optimal.最小化这个成本的position是最优的。

I have used this kind of the above optimization for other functions as well.我也对其他功能使用了上述优化。 For example, when I disconnect a node from the Unvisited list I do the following:例如,当我从未访问列表中断开节点时,我执行以下操作:

void disconnect(T* curr) {
    if (head == curr) {
        head = curr->next;
    }

    if (tail == curr) {
        tail = curr->prev;
    }

    if (curr->next != nullptr) {
        curr->next->prev = curr->prev;
    }
    if (curr->prev != nullptr) {
        curr->prev->next = curr->next;
    }
    curr->next = nullptr;
    curr->prev = nullptr;
}

As you can see, the disconnect function takes a pointer as a parameter instead of an id or a position. The head and tail pointers are private variables of the List class and they point to the first and last node of a list respectively.如您所见,断开连接 function 将指针作为参数,而不是 id 或 position。头指针和尾指针是列表 class 的私有变量,它们分别指向列表的第一个和最后一个节点。 So for example, if I run A.disconnect(n) but node n in reality is the first node of list B, then node n will cut loose from its next node of list B, but B.head pointer will continue to point at n .因此,例如,如果我运行 A.disconnect(n) 但实际上节点n是列表 B 的第一个节点,那么节点 n 将从列表 B 的下一个节点断开,但 B.head 指针将继续指向名词Of course, it is my responsibility to not let this happen, but still it seems a little bit "hackey" to me.当然,不让这种情况发生是我的责任,但对我来说这似乎有点“hackkey”。

You should learn about iterators.您应该了解迭代器。 Not a "pos" or or "pointer".不是“pos”或“pointer”。 Iterators are the standard way to solve your problem.迭代器是解决问题的标准方法。

Seeing the function void disconnect(T* curr) , it seems as if you are not implementing a DoublyLinkedList, but a bunch of Nodes that are connected via pointers.看到 function void disconnect(T* curr) ,似乎您没有实现 DoublyLinkedList,而是一堆通过指针连接的节点。

Normally, you would need a class "List" and hide all the pointer stuff in it.通常,您需要一个 class“列表”并将所有指针内容隐藏在其中。 Even the Node can be embedded in the "List".甚至节点也可以嵌入到“列表”中。 The outside world should not know about the Node.外界不应该知道节点。

I cannot refactor your code, because I do no know it.我无法重构您的代码,因为我不知道。 What I can do is to give you an example, how a "List" with Iterators can be implemented.我能做的是给你一个例子,如何实现带有迭代器的“列表”。

Maybe this will give you an idea.也许这会给你一个想法。

Just read the code and maybe take over one or the other concept.只需阅读代码,并可能接管一个或另一个概念。

#include <iterator>
#include <initializer_list>
#include <algorithm>
#include <iostream>
#include <type_traits>
#include <vector>

// ------------------------------------------------------------------------------------------------
// This would be in a header file -----------------------------------------------------------------

// Type trait helper to identify iterators --------------------------------------------------------
template<typename T, typename = void>
struct is_iterator { static constexpr bool value = false; };
template<typename T>
struct is_iterator<T, typename std::enable_if<!std::is_same<typename std::iterator_traits<T>::value_type, void>::value>::type> {
    static constexpr bool value = true;
};

// The List class ---------------------------------------------------------------------------------
template <typename T>
class List {
    // Sub class for a Node -----------
    struct Node {
        T data{};
        Node* next{};
        Node* previous{};
        Node() {}
        Node(Node* const n, Node* const p) : next(n), previous(p) {}
        Node(Node* const n, Node* const p, const T& d) : next(n), previous(p), data(d) {}
    };

    // Private list data and functions --------
    Node* head{};
    size_t numberOfElements{};
    void init() { head = new Node(); head->next = head; head->previous = head; numberOfElements = 0; }

public:
    struct iterator;    // Forward declaration

    // Constructor --------------------
    List() { init(); }
    explicit List(const size_t count) { init(); insert(begin(), count); }
    explicit List(const size_t count, const T& value) { init(); insert(begin(), count, value); };
    template <typename Iter>
    List(const Iter& first, const Iter& last) { init(); insert(begin(), first, last); }
    List(const List& other) { init(), insert(begin(), other.begin(), other.end()); };

    List(List&& other) : head(other.head), numberOfElements(other.numberOfElements) { other.init(); }
    List(const std::initializer_list<T>& il) { init(); insert(begin(), il.begin(), il.end()); }
    template <int N> List(T(&other)[N]) { init(); insert(begin(), std::begin(other), std::end(other)); }
    template <int N> List(const T(&other)[N]) { init(); insert(begin(), std::begin(other), std::end(other)); }


    // Assignment ---------------------
    List& operator =(const List& other) { clear(); insert(begin(), other.begin(), other.end()); return *this; }
    List& operator =(List&& other) { clear(); head = other.head; numberOfElements = other.numberOfElements; other.init(); return *this; }
    List& operator =(const std::initializer_list<T>& il) { clear(); insert(begin(), il.begin(), il.end()); return *this; }
    template <int N> List& operator =(const T(&other)[N]) { clear(); insert(begin(), std::begin(other), std::end(other)); return *this; }
    template <int N> List& operator =(T(&other)[N]) { clear(); insert(begin(), std::begin(other), std::end(other)); return *this; }

    template <typename Iter> void assign(const Iter& first, const Iter& last) { clear(); insert(begin(), first, last); }
    template <int N> void assign(const T(&other)[N]) { clear(); insert(begin(), std::begin(other), std::end(other)); return *this; }
    template <int N> void assign(T(&other)[N]) { clear(); insert(begin(), std::begin(other), std::end(other)); return *this; }
    void assign(const size_t count, const T& value) { clear(); insert(begin(), count, value); }
    void assign(const std::initializer_list<T>& il) { clear(); insert(begin(), il.begin(), il.end()); }

    // Destructor ---------------------
    ~List() { clear(); }

    // Element Access -----------------
    T& front() { return *begin(); }
    T& back() { return *(--end()); }

    // Iterators ----------------------
    iterator begin() const { return iterator(head->next, head); }
    iterator end() const { return iterator(head, head); }

    // Capacity -----------------------
    size_t size() const { return numberOfElements; }
    bool empty() { return size() == 0; }

    // Modifiers ----------------------
    void clear();

    iterator insert(const iterator& insertBeforePosition, const T& value);
    iterator insert(const iterator& insertBeforePosition);
    template <class Iter, std::enable_if_t<is_iterator<Iter>::value, bool> = true>
    iterator insert(const iterator& insertBeforePosition, const Iter& first, const Iter& last);
    iterator insert(const iterator& insertBeforePosition, const size_t& count, const T& value);
    iterator insert(const iterator& insertBeforePosition, const std::initializer_list<T>& il);

    iterator erase(const iterator& posToDelete);
    iterator erase(const iterator& first, const iterator& last);

    void pop_front() { erase(begin()); };
    void push_front(const T& d) { insert(begin(), d); }

    void pop_back() { erase(--end()); };
    void push_back(const T& d) { insert(end(), d); }

    void resize(size_t count, const T& value);
    void resize(size_t count);

    void swap(List& other) { std::swap(head, other.head); std::swap(numberOfElements, other.numberOfElements); }

    // Operations --------------------
    void reverse();

    // Non standard inefficient functions --------------------------
    T& operator[](const size_t index) const { return begin()[index]; }

    // ------------------------------------------------------------------------
    // Define iterator capability ---------------------------------------------
    struct iterator {

        // Definitions ----------------
        using iterator_category = std::bidirectional_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = T;
        using pointer = T*;
        using reference = T&;

        // Data -----------------------
        Node* iter{};
        Node* head{};

        // Constructor ----------------
        iterator(Node* const node, Node* const h) : iter(node), head(h) {};
        iterator() {};

        // Dereferencing --------------
        reference operator*() const { return iter->data; }
        reference operator->() const { return &**this; }

        // Arithmetic operations ------
        iterator operator++() { iter = iter->next; return *this; }
        iterator operator--() { iter = iter->previous; return *this; }
        iterator operator++(int) { iterator tmp = *this; ++* this; return tmp; }
        iterator operator--(int) { iterator tmp = *this; --* this; return tmp; }

        iterator operator +(const difference_type& n) const {
            iterator temp{ *this };  difference_type k{ n }; if (k > 0) while (k--)++temp; else while (k++)--temp; return temp;
        }
        iterator operator +=(const difference_type& n) {
            difference_type k{ n }; if (k > 0) while (k--)++* this; else while (k++)--* this; return *this;
        };
        iterator operator -(const difference_type& n) const {
            iterator temp{ *this };  difference_type k{ n }; if (k > 0) while (k--)--temp; else while (k++)++temp; return temp;
        }
        iterator operator -=(const difference_type& n) {
            difference_type k{ n }; if (k > 0) while (k--)--* this; else while (k++)++* this; return *this;
        };
        // Comparison ----------------- (typical space ship implementation)
        bool operator ==(const iterator& other) const { return iter == other.iter; };
        bool operator !=(const iterator& other) const { return iter != other.iter; };
        bool operator < (const iterator& other) const { return other.iter - iter < 0; };
        bool operator <= (const iterator& other) const { return other.iter - iter <= 0; };
        bool operator > (const iterator& other) const { return other.iter - iter > 0; };
        bool operator >= (const iterator& other) const { return other.iter - iter >= 0; };

        // Special non standard functions -----------------
        difference_type operator-(const iterator& other) const;
        reference operator[] (const size_t index);
    };
};


// ------------------------------------------------------------------------------------------------
// Implementation of list functions. This would normally go into a TCC file -----------------------

// List class functions ---------------
template <typename T>
void List<T>::clear() {

    for (Node* nextNode{}, * currentNode(head->next); currentNode != head; currentNode = nextNode) {
        nextNode = currentNode->next;
        delete currentNode;
    }
    init();
}
template <typename T>
typename List<T>::iterator List<T>::insert(const List<T>::iterator& insertBeforePosition, const T& value)
{
    Node* nodeInsertBeforePosition = insertBeforePosition.iter;
    Node* newNode = new Node(nodeInsertBeforePosition, nodeInsertBeforePosition->previous, value);
    nodeInsertBeforePosition->previous = newNode;
    (newNode->previous)->next = newNode;
    ++numberOfElements;
    return iterator(newNode, head);
}
template <typename T>
typename List<T>::iterator List<T>::insert(const List<T>::iterator& insertBeforePosition)
{
    Node* nodeInsertBeforePosition = insertBeforePosition.iter;
    Node* newNode = new Node(nodeInsertBeforePosition, nodeInsertBeforePosition->previous);
    nodeInsertBeforePosition->previous = newNode;
    (newNode->previous)->next = newNode;
    ++numberOfElements;
    return iterator(newNode, head);
}

template <typename T>
template <class Iter, std::enable_if_t<is_iterator<Iter>::value, bool>>
typename List<T>::iterator List<T>::insert(const List<T>::iterator& insertBeforePosition, const Iter& first, const Iter& last) {
    iterator result(insertBeforePosition.iter, head);
    if (first != last) {
        result = insert(insertBeforePosition, *first);
        Iter i(first);
        for (++i; i != last; ++i)
            insert(insertBeforePosition, *i);
    }
    return result;
}

template <typename T>
typename List<T>::iterator List<T>::insert(const List<T>::iterator& insertBeforePosition, const size_t& count, const T& value) {

    iterator result(insertBeforePosition.iter, head);
    if (count != 0u) {
        result = insert(insertBeforePosition, value);
        for (size_t i{ 1u }; i < count; ++i)
            insert(insertBeforePosition, value);
    }
    return result;
}

template <typename T>
typename List<T>::iterator List<T>::insert(const List<T>::iterator& insertBeforePosition, const std::initializer_list<T>& il) {
    return insert(insertBeforePosition, il.begin(), il.end());
}

template <typename T>
typename List<T>::iterator List<T>::erase(const List<T>::iterator& posToDelete) {

    iterator result = posToDelete;
    ++result;

    Node* nodeToDelete = posToDelete.iter;

    if (nodeToDelete != head) {

        nodeToDelete->previous->next = nodeToDelete->next;
        nodeToDelete->next->previous = nodeToDelete->previous;

        delete nodeToDelete;
        --numberOfElements;
    }
    return result;
}

template <typename T>
typename List<T>::iterator List<T>::erase(const List<T>::iterator& first, const List<T>::iterator& last) {
    iterator result{ end() };
    if (first == begin() && last == end())
        clear();
    else {
        while (first != last)
            first = erase(first);
        result = last;
    }
    return result;
}

template <typename T>
void List<T>::resize(size_t count) {
    if (numberOfElements < count)
        for (size_t i{ numberOfElements }; i < count; ++i)
            insert(end());
    else
        while (count--)
            pop_back();
}
template <typename T>
void List<T>::resize(size_t count, const T& value) {
    if (numberOfElements < count)
        for (size_t i{ numberOfElements }; i < count; ++i)
            insert(end(), value);
    else
        while (count--)
            pop_back();
}
template <typename T>
void List<T>::reverse() {
    const Node* oldHead = head;

    for (Node* nptr = head; ; nptr = nptr->previous) {
        std::swap(nptr->next, nptr->previous);
        if (nptr->previous == oldHead) // Previous was the original next
            break;
    }
}

// ------------------------------------
// Iterator functions -----------------
template <typename T>
typename List<T>::iterator::difference_type List<T>::iterator::operator-(const iterator& other) const {

    difference_type result{};
    Node* nptr = head;

    int indexThis{ -1 }, indexOther{ -1 }, index{};

    do {
        nptr = nptr->next;
        if (nptr == iter)
            indexThis = index;
        if (nptr == other.iter)
            indexOther = index;
        ++index;
    } while (nptr != head);

    if (indexThis >= 0 and indexOther >= 0)
        result = indexThis - indexOther;
    return result;
}
template <typename T>
typename List<T>::iterator::reference List<T>::iterator::operator[] (const size_t index) {
    Node* nptr = head->next;
    for (size_t i{}; i < index and nptr != head; ++i, nptr = nptr->next)
        ;
    return nptr->data;
}

// ------------------------------------------------------------------------------------------------
// This would be in a cpp file --------------------------------------------------------------------
int main() {


    List<int> list3{ 10,20 };
    List<int>::iterator l3 = list3.end();
    for (int k = 0; k < 10; ++k) {
        std::cout << *l3 << ' ';
        --l3;
    }
    std::cout << '\n';

    // Custom list
    List<int> list2{ 1, 2, 3, 4, 5 };

    for (int i : list2)
        std::cout << i << ' '; std::cout << '\n';

    // Delta works
    std::cout << list2.begin() - list2.end() << '\n';
    std::cout << list2.end() - list2.begin() << '\n';

    // Hopp Count works
    List<int>::iterator i = list2.end();
    while (i-- != list2.begin())
        std::cout << *i << ' '; std::cout << '\n';
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM