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

Iterate over elements where not all the elements should have a value

本文关键字:元素 遍历      更新时间:2023-10-16

我不知道如何描述这一点。我必须遍历通过指针相互连接的对象,但是第一个元素不应该在其类中存储值,只是指向下一个元素的指针。

我想出了一个小的班级层次结构。如果我这样使用它,我必须将基类强制转换为派生元素。它似乎只适用于肮脏的演员阵容。

有没有更好的解决方案?

下面是示例代码:

#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

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

@SomeProgrammerDude所说的所有事情,或者:

我认为根本不需要类BaseNode。 为什么我们不能只拥有(所有其他条件相同(:

SkipNode *head = &first;
...

或者更好的是,一个名为(例如(SkipNodeList的类,它处理管理和迭代SkipNode列表的所有方面。

当然,无论如何,这一切都有点愚蠢,只需为此使用std::list(或std::forward_list(并从所有STL的好处中受益。

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

是的,有更好的方法:

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

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

此外,为了说明SkipnodeBasenode继承的这种设计如何有意义(与评论中的讨论相关(,我们可以想象让一个成员在Basenode计算下一个元素的数量(计算next成员及其后继者中的元素(