链表中的节点是否可以指向自身? C ++

时间:2015-08-02 01:06:02

标签: c++ list linked-list

CS学生在这里。我不想试图隐瞒这是作业的事实。 我知道问题很尴尬所以让我解释一下,

我正在为C ++中的作业编写代码,该代码使用节点来模拟由指针链接的房间结构。基本上它是一个链表。我知道如何设计一个链表,这将是我的房间结构。 (只是你的平均堆栈或队列)

 +---+---+---+---+---+  
 | 1 | 2 | 3 | 4 | 5 |  
 +---+---+---+---+---+

但该结构是一维的,只需要一个/两个方向的链接。一个用于下一个节点的地址,另一个用于前一个节点的地址。我想创建一个看起来更像这样的二维结构:

    ____+---+  
    ____| R |  
    ____+---+  
    ____| H |  
+---+   +---+   +---+  
| L |. .| H |. .| L |   
+---+   +---+   +---+  
| L |  . . . . .| H |  
+---+  . . . . .+---+  
........... ......^  

当L是实验室时,H是走廊,R是反应堆(我们必须将至少3种不同类型的房间定义为基类房间的子类)。而^是您从哪里输入的。
在我的设计中我无法理顺的是,如果我想能够在4个方向上移动(比如N,S,E,W),我将如何解释在方向上没有空间用户选择?
当玩家进入时,我将指向所有相邻房间的指针。一个房间,但我该如何处理墙壁呢? 我应该指定他们指向玩家所在的同一个房间吗?然后打印错误声明?我应该将它们分配给NULL吗?任何建议都有帮助。

编辑@ 7 PST 8.1.15

到目前为止,我们的反应非常好! @cristophe:我喜欢选项1.是否有可能在程序开始时实例化整个链表,通过给出彼此的N,S,E,W指针将所有房间链接在一起,墙上有NULL?登记/> 假设我有一个基类/父类Room和派生类Lab,Hall,Reactor,它们就像节点一样,我可以在这些名为player的节点中有一个布尔成员变量,如果玩家在房间里,它会返回true吗?

编辑@4:20 PST 8.4.15

我至少编码了一个骨架版本的结构,这个方法很有效!每个房间都是一个链接节点,有4个指针用于4个主要方向。我将相邻房间的地址分配给指针和房间本身的地址到任何有墙的方向。这是我的课程定义,当它看起来更好时,我会发布更多代码。所有这些指针最终都是私有的,我还没有去编写访问器。

room.h:

#ifndef ROOM_H
 #define ROOM_H

使用namespace std;

class Room {
    //protected:
    public:
            string roomname;
            int roomtemp;
            int playertemp;
            Room *up;
            Room *down;
            Room *left;
            Room *right;
            Room *player;
            virtual void temp_change(int roomtemp);
            string get_name();
            void set_room(string nameval, Room* up, Room* down, Room* left, Room* right);
            void set_temp();
            void move_player(Room*& current, char action);

};

#ENDIF

4 个答案:

答案 0 :(得分:1)

您尝试实现的数据结构更多的是图形而不是链接列表。你有很多方法可以实现这一点。

第一种可能性:

  • 你有一个扁平的链表或房间向量
  • 每个房间都有一个指向相邻房间的链接列表
  • 问题:链接应与N,S,W,E
  • 相关
  • 问题解决:使用对(方向+指针),或使用地图而不是相邻房间的链表。
  • 墙是指链表中没有方向。

第二种可能性:

  • 你有一个扁平的链表或房间向量
  • 每个房间本身都有一个4个指针(N,S,W,E)到相邻房间的向量
  • 墙由nullptr
  • 表示

第三种可能性:

  • 你有一个扁平的链表或房间向量
  • 每个房间有四个指向相邻房间的指针(就像一棵树,除了它们可以是圆柱形)。
  • 墙由nullptr
  • 表示
  • 问题:操作比前一个操作更麻烦(即需要大量冗余代码来迭代4个方向)

第四种可能性:

  • 你有一个N房间的矢量
  • 你在房间之间有一个N×N邻接关系的矩阵
  • 房间i和房间j的矩阵值告诉您从i到j必须采取的方向,或表示房间未连接的中性值

答案 1 :(得分:0)

维护房间信息的某种收集(数组或矢量)当然是必须的。

关于您是否希望每个房间对象负责了解与其他房间共享的连接,或者该信息是否与房间数据分开,这是一个真正的设计选择。

房间之间指针的替代方法是矩阵a(对于n个房间,n * n),a[x][y]表示有关两个房间之间连接状态的一些信息。它可以是简单的布尔值(是的,有路由,没有没有信息),或者更复杂(0 - 没有连接,1 - 锁定连接......等等)。这会使您的级别地图更加复杂,但代码可能会更加复杂,如果您想要定义移动方向,那么找到对给定运动方向的响应的时间复杂度会随着n(在最坏的情况下必须检查矩阵中的整行,平均大约一半),而一组四个直指针将给出恒定的时间复杂度。值得考虑。

如果您沿着每个房间的路线了解其连接,使用空指针来表示墙壁是一个有效的选择。 从水平设计的角度来看,值得注意的是,允许自我引用可能很有趣 - 在自我重复的地铁中想想Neo! https://www.youtube.com/watch?v=lEWIn0p6OUM

答案 2 :(得分:0)

您可以尝试使用图形数据结构来解决此问题。 为4个方向使用4个不同的值,以实现您的目标。

答案 3 :(得分:0)

简短的回答是,它可以通过将正常列表与房间信息(包括指向其他​​节点的指针)相结合来实现。

您可以通过实际组合两个结构来解决您的问题:一个用于跟踪所有房间(常规列表),另一个用于跟踪房间数据,例如哪些房间相邻。您可以将这些结合到一个结构中:

struct room_list_element {
    // Use these for regular linked list stuff
    room_list_node *prev = nullptr;
    room_list_node *next = nullptr;

    // Use this for traversing the structure as 2 dimensions
    room_list_node *N = nullptr;
    room_list_node *E = nullptr;
    room_list_node *S = nullptr;
    room_list_node *W = nullptr;
    std::string room_name;
}

然后你可以像往常一样制作链接的房间列表,然后找到“Bridge”并将其“S”指针连接到“Engineering”,然后找到“Engineering”并将其“N”指针连接到“Bridge”等。墙由nullptr表示。

然后,您可以按prev / next和N / E / S / W访问列表。您可以使用prev / next搜索房间,分配/删除数据等,以及N / E / S / W来移动玩家指针。

然而,你基本上将两种不同的数据结构组合在一个混乱的kludge中。它可以工作,但它是糟糕的设计!如果要向房间添加更多内容,则必须将它们添加到列表节点中。这非常不合适。那么为什么不做呢?

我将使用一个有三个房间的地图的简单示例:Bridge,位于第二个房间的北面,工程。工程学北部的桥梁和东部的枪支。

struct Room {
    Room *N = nullptr;
    Room *S = nullptr;
    Room *E = nullptr;
    Room *W = nullptr;
    // whatever other information you want about rooms here
    std::string Name; // etc. etc.
};

// ... later on,
std::list<Room *> rooms;
Room new_room;

new_room = new Room;
new_room->name = "Bridge";
rooms.push_back(new_room); // Put it into the list
new_room = new Room;
new_room->name = "Engineering";
rooms.push_back(new_room); // Put it into the list again
new_room = new Room;
new_room->name = "Guns";
rooms.push_back(new_room); // Put it into the list again

// Connect them up. This is where it gets super-kludgy.
auto& room_to_connect_1 = rooms.begin();
auto& room_to_connect_2 = rooms.begin()++;
auto& room_to_connect_3 = (rooms.begin()++)++;

room_to_connect_1->S = room_to_connect_2; // Bridge->S = Engineering
room_to_connect_2->N = room_to_connect_1; // Engineering-> N = Bridge

room_to_connect_2->E = room_to_connect_3; // Engineering->E = Guns
room_to_connect_3->W = room_to_connect_2; // Guns->W = Engineering

Room player_room = room_to_connect_2; // Player starts in engineering

我们在这里所做的就是将额外的数据从列表结构中移出。它仍然是我们可以制作成2D地图的房间列表,而且我们可以使用std :: list,这比滚动我们自己更好。

在设计方面,这仍然在许多层面上都不尽如人意。首先,内存泄漏很多。其次,寻找和连接房间是一种痛苦! 让我们做一个更好的方式,这可能是家庭作业所不能接受的。

这是一个带有简单地图的演示程序,该地图的南面有一座桥梁和工程。

#include <string>
#include <list>
#include <map>
#include <memory>
#include <iostream>

// This structure holds our room info
struct Room {
    Room *N = nullptr;
    Room *S = nullptr;
    Room *E = nullptr;
    Room *W = nullptr;
    // whatever other information you want about rooms here
    std::string Name; // etc. etc.

    Room(std::string room_name) : Name(room_name) { } // sets Name to name
};

int main(int argc, char* argv[])
{
    // We make a list here to handle deleting rooms, and a map to to index rooms by name.
    // Because we use unique_ptr in the vector, it will handle deleting the objects when the vector is freed!
    std::list<std::unique_ptr<Room>> room_destroyer; // *This is actually the 2-dimensional linked list you wanted to make earlier,* just with better design. You can traverse it with prev/next and each element has pointers to other elements.
    std::map<std::string, Room *> room_map; 

    // This will hold track of where the player is for us. Could be part of a "player" object or whatever.
    Room *player_room = nullptr;

    // Let's make two rooms.
    std::unique_ptr<Room> new_room;

    // First the bridge
    new_room = std::make_unique<Room>("Bridge");
    room_map.emplace(new_room->Name, new_room.get());
    room_destroyer.push_back(std::move(new_room)); // Notice we do this last, because our "new_room" pointer will no longer point to or own anything. We use std::move() to "transfer ownership" to the pointer that the list creates when we push_back.

    new_room = std::make_unique<Room>("Engineering");
    room_map.emplace(new_room->Name, new_room.get());
    room_destroyer.push_back(std::move(new_room)); // Notice again that we do this last, because our "new_room" pointer will no longer point to or own anything

    // Now let's connect it so that the bridge is north of engineering and vice-versa
    auto& room_to_connect_1 = room_map.find("Bridge"); // auto& takes care of making an iterator for us without messy syntax.
    // Remember, std::maps return iterators where ->first is the key (i.e. name), and -> second is the value (a pointer to the room)
    auto& room_to_connect_2 = room_map.find("Engineering");
    if ((room_to_connect_1 == room_map.end()) || 
        (room_to_connect_2 == room_map.end()) ) {
        // We can't find one of the rooms? Ouch!
    }
    else {
        room_to_connect_1->second->S = room_to_connect_2->second; // Bridge's S = Engineering
        room_to_connect_2->second->N = room_to_connect_1->second; // Engineering's N = Bridge
    }

    // Now that we've done all that, let's put the player in engineering and print where we are.
    player_room = room_map.find("Engineering")->second; // find() returns an iterator. Map iterators return ->first as the key, and ->second as the value (the pointer to the room)
    if (player_room == nullptr) {
        std::cout << "Engineering not found while trying to put the player there! Why!?" << std::endl;
        return 0;
    }
    std::cout << "The player is in the " << player_room->Name << std::endl;

    // Let's try moving north. Remember, we set ourselves to be in Engineering, which has an "N" link to the Bridge
    std::cout << "Player tries to move north." << std::endl;
    if (nullptr != player_room->N) {
        player_room = player_room->N;
    }
    else {
        std::cout << "There is no room to the north!" << std::endl;
    }
    std::cout << "The player is in the " << player_room->Name << std::endl;

    // Let's try moving east.
    std::cout << "Player tries to move east." << std::endl;
    if (nullptr != player_room->E) {
        player_room = player_room->E;
    }
    else {
        std::cout << "There is no room to the east!" << std::endl;
    }

    std::cout << "The Player is in the " << player_room->Name << std::endl;

    std::cout << std::endl << "Press enter to exit" << std::endl;
    getc(stdin);
    return 0;
}

这是输出:

The player is in the Engineering.
Player tries to move north.
The player in the Bridge
Player tries to move east.
There is no room to the east!
The player is in the Bridge

Press enter to exit