所以我尝试自己在c ++中实现XOR链表,然后想到了
#include <iostream>
using namespace std;
struct Node {
int data;
Node* npx;
};
Node* XOR(Node* prev, Node* next) {
return (Node *)((uintptr_t)(prev) ^ (uintptr_t)(next));
}
// add a new node to linked list head is the beginign of the list
void Add(Node **head, int data) {
Node* newNode = new Node();
newNode->data = data;
if (head = NULL) {
cout << "no head";
}
else {
while (XOR(*head, (*head)->npx) != NULL) {
*head = XOR(*head, (*head)->npx);
}
(*head)->npx = XOR(XOR((*head)->npx, XOR(*head, (*head)->npx)), newNode);
}
}
//geting data from the list with given indx
int Get(int index, Node* head) {
for (int i = 0; i <= index; i++) {
if (XOR(head, head->npx) != NULL) {
head = XOR(head, head->npx);
}
else {
cout << "index out of range";
}
}
return head->data;
}
int main()
{
Node** newNode = new Node* ();
Add(newNode, 10);
Add(newNode, 2);
cout << Get(1, *newNode);
return 0;
}
但是即使进行了硬编码测试,它也不会返回任何内容,有人可以帮助我还是显示解决方案的外观?
答案 0 :(得分:2)
if (head = NULL)
立即破坏了head
指针。许多现代编译器都将标记此错误。这不是编译器错误,即使在head
语句中将NULL
设置为if
也是完全合法的,但是由于这几乎总是逻辑错误,因此好的编译器会将其带到您的注意。但是,有时您必须要求编译器执行此操作。尽可能提高警告级别,通常可以节省调试错别字和小麻烦的时间。
由于头是通过指针传递的,因此前进头会更新调用方的头指针,并丢失列表。 Add
确实需要更新head
指针,但前提是列表为空。
这使npx = previous ^ next
难以计算。您始终需要知道两个节点才能恢复第三个节点,并且可以恢复前一个节点,但是简单地握住它就容易得多。
我试图对此进行简化,所以如果代码愚蠢且优化不佳,请原谅我。关于我在做什么及其原因的注释已嵌入到代码中。
#include <iostream>
#include <stdexcept>
using namespace std; // reconsider using this. It can have negative side
// effects when your programs get more complicated
struct Node; // Forward declaration
// XOR needs Node, and a small change to Node meant Node needed XOR
// this struck me as the most-polite way to resolve the circle
// Unchanged
Node* XOR(Node* prev, Node* next) {
return (Node *)((uintptr_t)(prev) ^ (uintptr_t)(next));
}
struct Node {
int data;
Node* npx;
// add a constructor to make life easier. npx is always computed correctly
Node(int data, Node* prev, Node * next): data(data), npx(XOR(prev, next))
{
}
// TODO: GetNext and GetPrev functions could be useful here. eg:
Node * GetNext(Node * prev)
{
return XOR(prev, npx);
}
// Along with a link(prev, next) function this would let you hide the XOR and
// abstract away all external knowledge of how the linked list is connected from
// the user.
};
// add a new node to linked list head is the beginning of the list
void Add(Node * &head, int data) { // note: using reference rather than double pointer
if (head == nullptr) { // no head, not much to do
head = new Node(data, nullptr, nullptr); // there is no prev or next
// if there is only one node
}
else {
// book keeping
Node * prev = nullptr; // last node visited. On first node, so null
// NOTE: THIS IS A (sort of) LIE! This implementation
// CANNOT be called with a Node from the the middle of a
// a list. Sometimes you want this functionality (Why
// search the whole damn list if you're already part
// way through?) but to get it, you have to provide more
// information so that you can recover the next pointer.
Node * cur = head; // current node is head
Node * next = XOR(prev, cur->npx); // next node is prev XOR npx
// OR Node * next = cur->GetNext(prev);
while (next != nullptr) { // there is a node here. Advance to next
prev = cur;
cur = next;
next = XOR(prev, cur->npx);
}
// found last node. Append new node
Node * newNode = new Node(data, cur, nullptr); // new tail node, so
// npx = current node XOR null
cur->npx = XOR(prev, newNode); // current node now has a next. Update npx
// here is where a cur->link(prev, newNode) function
// would be handy.
}
}
//getting data from the list with given index
int Get(int index, Node* cur) {
Node * prev = nullptr; // is no previous node yet
while (index && // exit when we've iterated far enough
cur != nullptr) { // or we've run out of nodes
Node * next = XOR(prev, cur->npx); // find next node
prev = cur; // update book keeping
cur = next;
index--; // one less iteration
}
if (index != 0) // oops.
{
// throwing exception rather than allowing the function to return an
// incorrect value. Often the correct choice unless incorrect indexes
// is a common occurrence. If something happens often it is not
//exceptional and thus should not be an exception.
throw std::out_of_range("index out of range");
}
return cur->data;
}
int main()
{
Node* head = nullptr; //no need for dynamic allocation
Add(head, 10); //
Add(head, 2);
Add(head, 42); // added one more just to be sure
cout << Get(0, head) << '\n'; // testing that the program can read all elements
cout << Get(1, head) << '\n';
cout << Get(2, head) << '\n';
// TODO: iterate through list and free all of the allocated memory
return 0;
}