遍历并非所有元素都应具有值的元素

时间:2018-07-02 17:25:11

标签: c++

我不确定该如何描述。我必须遍历通过指针相互连接的对象,但是第一个元素不应仅在指向下一个元素的指针中存储值。

我想出了一个小类层次结构。如果我那样使用它,则必须将基类转换为派生元素。它似乎只能与肮脏的演员一起使用。

有更好的解决方案吗?

下面是示例代码:

#include <iostream>

struct Basenode {
    Basenode* next;
};

struct Skipnode : Basenode {
    Skipnode(int in_key, int in_value);

    int key;
    int value;
};

Skipnode::Skipnode(int in_key, int in_value)
    : key{ in_key }, value{ in_value }
{
}

int main()
try {
    Basenode head;              // no key and value
    Skipnode first(4, 2);       // key and value
    Skipnode second(8, 2);

    Basenode* p = &head;

    head.next = &first;         // fine
    first.next = &second;       // fine

//  p = p->next->key;           // not accesible because is Basenode not derrived Skipnode

    std::cout << static_cast<Skipnode*>(p->next)->key;  // fine but diryt cast  slicing ?

    std::cin.get();
}
catch (std::runtime_error& e) {
    std::cerr << e.what() << "\n";
    std::cin.get();
}
catch (...) {
    std::cerr << "unknown error " << "\n";
    std::cin.get();
}

编辑:有人问我为什么我需要此注释。我想我举了一个有限的例子。

我需要它来实现一个跳过列表。它的许多算法都要求从第一个元素之前的一个元素开始。头元素。我可以将其设为普通节点,并为这些值输入虚拟值,但似乎不正确。因此,现在我从您的建议中提出了这个丑陋的解决方案,从头开始。

此处以复制构造函数为例。

class Skiplist {
public:
    //...
    Skiplist(const Skiplist& other);                // copy constructor
    //...
private:
    struct Skipnode;                // forward declaration so Basenode can have Skiplist*

    struct Basenode {                                       // Empty node, mainly created to represent head element. 
                                                            // Is there a way to get a empty head with no key / values without using this ?
        Basenode(int in_level);
        Basenode(const std::vector<Skipnode*>& in_next);

        std::vector <Skipnode*> next;
    };

    struct Skipnode : Basenode {                                    // derived so with Basenode* we can start the iteration of the node on head
        Skipnode(value_type val, int in_level);
        Skipnode(value_type val, const std::vector<Skipnode*>& in_next);

        value_type value;       // first key / second mapped type = value

                                //key_type key;
                                //mapped_type value;
    };

    Basenode head{ 0 };                 // element before first element containg pointers to all the first elements of each level

    //...
};


Skiplist::Skiplist(const Skiplist& other)       // copy constructor
    :head{ other.head }, top_level{ other.top_level }, random_engine{ other.random_engine }
    // on the first level let the other Skiplist present its elements and make a deep copy of them
    // now still the higher levels point to the other node so this is fixed in the second part
    // then the next level pointers are installed linked to the elements of the new node
{
    if (top_level == 0) return;     // no elements are present so dont bother to allocate nodes

    {
        // installment of lowest level, each element is located here
        Skipnode* other_node = other.head.next[0];
        Basenode* current_position = &head;

        while (other_node != nullptr) {
            Skipnode* new_node = new Skipnode{ other_node->value,other_node->next };

            current_position->next[0] = new_node;
            current_position = current_position->next[0];
            other_node = other_node->next[0];
        }
        current_position->next[0] = nullptr;
    }

    // installment of the other levels
    for (size_type curr = 1; curr < top_level; ++curr) {

        Basenode* current_position = &head;                         // the current position of the level[curr]
        Skipnode* next_position = current_position->next[curr];     // next position after curr containing still pointers to the other skiplist
        Basenode* lowest_position = &head;                          // lowest level position used to find the new pointers and attach them "behind" current

        while (lowest_position != nullptr && next_position != nullptr) {

            if (lowest_position->next[0]->value.first == next_position->value.first) {      // check by unique key, address of next pos is still of the other skiplist

                current_position->next[curr] = lowest_position->next[0];        // lowest is the valid address of new node
                current_position = current_position->next[curr];
                next_position = next_position->next[curr];                      // go to next element of other node

                if (next_position == nullptr) {                     // case end is reached
                    current_position->next[curr] = nullptr;
                    current_position = current_position->next[curr];
                }
            }
            else {      // forward position of lowest level until other key == next position key
                lowest_position = lowest_position->next[0];
            }
        }
    }
}

请参阅此处以获取有关如何组织跳过列表的基本说明: https://en.wikipedia.org/wiki/Skip_list

整个代码在codereview上: https://codereview.stackexchange.com/questions/197752/non-generic-skip-list-implementation-in-c-version-2

2 个答案:

答案 0 :(得分:2)

@SomeProgrammerDude所说的所有内容,或者:

我根本不需要课程BaseNode。为什么我们不能拥有(在所有其他条件都相等的情况下):

SkipNode *head = &first;
...

或者更好的是一个叫做SkipNodeList的类,它处理SkipNode列表中管理和迭代的所有方面。

当然,这总有点愚蠢,只需使用std::list(或std::forward_list)并从STL的所有优点中受益。

或者您可以从其中之一派生以添加自己的功能(例如,互斥体以使列表具有线程安全性或跟踪列表中当前元素的数量,如@iMajuscule所建议)。

答案 1 :(得分:1)

是的,有更好的方法:

向前声明Skipnode,然后在BaseNode中使用指向SkipNode的指针,这样就不必强制转换:

struct Skipnode;
struct Basenode {
    Skipnode* next;
};

另外,为了说明从Skipnode继承Basenode的设计如何有意义(与评论中的讨论有关),我们可以想象在Basenode中有一个成员来计算接下来是许多元素(计算next成员及其后继者中的一个)