编辑:问题:删除2个节点之间的节点,然后将外部节点相互链接。
在学习并构建了几个四叉树和八叉树,并且非常喜欢细分之后,我决定在我的服务器中使用细分。但是,只在自己内部放置节点。我的问题是,如果节点位于中间,我无法弄清楚如何将前一个节点链接到下一个节点。我想知道我是否正确地这样做了。下面是我的代码,我评论问题区域在哪里。任何帮助都会很棒!
bool deleteNode ( SOCKET s , client_information_class *prev ) {
bool return_value = false;
if ( node[0]->client_socket != s ) {
if ( node[0]->node != NULL ) {
return_value = node[0]->deleteNode ( s , *node );
} else {
cout << endl << "Can not call deleteNode anymore ... Did not find the node to delete ... " << endl;
return return_value;
}
} else {
if ( node[0]->client_state == 1 )
InterlockedDecrement(&connection_count);
if ( node[0]->node != NULL ) { // there is a next node
client_information_class **next = node[0]->node;
if ( next[0]->node != NULL ) {
// problem area
cout << endl << "next[0]->node is not null" << endl;
prev->node = next[0]->node[0]->node; // ??? I know its wrong
} else {
// problem area
cout << endl << "next[0]->node is null" << endl;
prev->node = next[0]->node; // ??? I know its wrong
}
delete node;
} else { // its the last one
prev->node = NULL;
delete node;
}
InterlockedDecrement(&socket_count);
return_value = true;
}
return return_value;
}
答案 0 :(得分:2)
因此,如果我的问题正确,则无法在链接列表中插入项目。我真的希望这是问题,否则我所做的所有打字都是徒劳的。
我并没有真正得到你所有的代码(因为它有点脱离上下文),但我会在一个例子中向你展示逻辑。我将使用具有以下结构的节点:
class Node
{
public:
Node * next_;
Node * prev_;
int i;//we need this to tell nodes apart.
//And whatever data you want.
}
因此,我们将创建一个链接列表,其中每个项目都有指向上一个和下一个项目的指针。更复杂结构的逻辑(例如,如果你有左 + 右 + 孩子 + 父指针)是一样的,唯一的区别是你必须设置所有这些。它并没有太大变化。
因此,要构造列表,我们需要一些能够处理不同操作的构造函数:
默认构造函数以创建一个空节点:
Node()//default constructor
{
prev_ = nullptr;
next_ = nullptr;
i = 0;
}
一个构造函数,它获取指向列表中最后一项的指针,并将自身附加到列表的末尾:
Node( Node * prev )//constructor for appending to the end of the list
{
prev_ = prev;
next_ = nullptr;//we append to the end, so there is no "next" element
prev->next_ = this;
i = 0;
}
最后在中间插入一个构造函数(我相信,这是你想要的):
Node( Node * prev, Node * next)//Inserting between prev and next nodes
{
prev->next_ = this;
next->prev_ = this;
this->prev_ = prev;
this->next_ = next;
i = 0;
}
现在我们拥有完整的仪器来做我们想做的任何事情。最后一件事是删除一个节点:
void deleteNode( Node * deleteMe )
{
deleteMe->prev_->next_ = deleteMe->next;
deleteMe->next_->prev_ = deleteMe->prev;
delete deleteMe;
}
或更可读的语法:
void deleteNode( Node * deleteMe )
{
Node * prevNode = deleteMe->prev_;
Node * nextNode = deleteMe->next_;
prevNode->next_ = deleteMe->next;
nextNode->prev_ = deleteMe->prev;
delete deleteMe;
}
让我们制作一个例如10个元素的样本列表:
int main()
{
Node * root = new Node();//the root of the list. it never changes.
Node * last = root;//The pointer to the last element
for(int i = 0; i < 10; i++)
{
Node * tmp = new Node( last );
last = tmp;
}
}
此处,列表中的所有元素都已正确链接,并且其 i 字段包含0值。让我们在中间某处插入另一个元素,例如,在第三个和第四个元素之间:
Node * thirdNode = root->next_->next_;//I know this sucks, but it's just an example.
Node * insertMe = new Node(thirdNode, thirdNode->next_);//inserting between the third and the fourth.
insertMe->i = 9;//this will indicate the one we inserted, since all the others will have this as 0.
现在我们可以轻松检查插入是否正确。让我们输入所有元素的数据:
for( Node * it = root; it!= last; it = it->next_)
{
std::cout << it->i << " ";
}
结果为0 0 0 9 0 0 0 0 0 0 0
。
最后,让我们删除刚刚插入的节点:
deleteNode( insertMe );
如果您不在同一个vay中输出值,您将只看到零,这意味着该节点已成功删除。
如果您的节点中有更多指针,您所要做的就是以相同的方式正确处理它们。另一个暗示是避免像this->next->next->prev
这样的结构。很难说你指的是元素的“prev”指针,它是当前元素的第三个。相反,使用类似的东西:
Node * target = this->next->next;//easy to read
或者
Node * target = this;
for(int i = 0; i < 5; i++)
target = target->next;
然后使用你得到的指针。
答案 1 :(得分:0)
如果你想轻易地从单链表中删除元素,那么使用指针指向这样的指针:
代码示例:
// Remove the node from the list:
Node* node_to_remove = *pointer_to_next_pointer;
*pointer_to_next_pointer = node_to_remove->next;
// Destroy the node.
destroy(node_to_remove);
这可以在不检查您是否从头部或类似的东西中移除的情况下完成。
完整示例:
// ------------------------------------------------------------------
// Start by pointing to the head pointer.
// ------------------------------------------------------------------
// (next_ptr)
// |
// v
// [head]----->[..]----->[..]----->[..]----->[to_remove]----->[....]
Node** next_ptr = &list->head;
// ------------------------------------------------------------------
// Search the list for the matching entry.
// After searching:
// ------------------------------------------------------------------
// (next_ptr)
// |
// v
// [head]----->[..]----->[..]----->[..]----->[to_remove]----->[next]
while (*next_ptr != to_remove) // or (*next_ptr)->val != to_remove->val
{
Node* next_node = *next_ptr
next_ptr = &next_node->next;
}
// ------------------------------------------------------------------
// Dereference the next pointer and set it to the next node's next
// pointer.
// ------------------------------------------------------------------
// (next_ptr)
// |
// v
// [head]----->[..]----->[..]----->[..]---------------------->[next]
*next_ptr = to_remove->next;
简化这一点的技巧是大多数人错过了导致他们用额外的分支编写代码2到3次的方法是使用指向节点的指针,而不是指向节点的指针。
此外,如果您不希望在迭代链接列表时遇到缓存未命中,请确保以获取链接列表通常缺少的空间位置的方式分配它们。在没有达到自定义内存分配器的情况下,一种简单的方法是使用索引而不是指针将节点链接在一个数组(或std::vector
中)。如果32位索引就足够了,那么也可以将64位链路的内存使用量减半。