C ++中的链接列表

时间:2013-01-27 04:50:23

标签: c++ linked-list

我正在尝试使用节点结构教自己链接列表,并希望有人可以帮助我。我会从命令行获取输入,它会使我成为一个嵌套列表,我可以输出它。

示例:

输入:“1 2 3 4 5”
输出:“1 2 3 4 5”

有两件事我遇到了麻烦: 1)当我运行程序时,我不断收到警告:在此声明中忽略了'typedef'[默认启用] 我怎么能摆脱这个?

编辑:我已将此更改为typedef struct Node* NodePtr;

2)我的代码无法正常工作。我怎样才能解决这个问题?我正在尝试用C ++教自己链接列表。

typedef struct Node;
typedef Node* NodePtr;
struct Node{
    int x;
    NodePtr next;
};

int main ()
{
    int n;
    NodePtr head, ptr = NULL;
    head = ptr;
    while (cin >> n){
        ptr = new Node;
        ptr->x = n;
        ptr->next = NULL;
        ptr = ptr->next;
    }

    NodePtr bling = head;
    while(bling != NULL){
        cout << bling->x << endl;
        bling = bling->next;
    }
    return 0;
}

理想情况下,我想做的是制作如下链接列表。

1 -> 2 -> 3 -> NULL.

4 个答案:

答案 0 :(得分:8)

首先,关于你的结构声明和你想要的指针typedef,有很多方法可以做到这一点。以下内容适用于C或C ++。

// declare NodePtr as a pointer to Node, currently an incomplete type
//  C and C++ both allow you to declare a pointer to damn-near anything
//  so long as there is an understanding of what it *will* be, in this
//  case, a structure called Node.
typedef struct Node *NodePtr;

// Now declare the structure type itself
struct Node
{
    int x;
    NodePtr next;
};

那就是说,我老实说不建议这样做。大多数工程师都希望 clear 和语法可见的定义向他们尖叫,“这是一个指针!”你可能会有所不同。我个人更喜欢这个:

struct Node
{
    int x;
    struct Node *next; // omit the 'struct' for C++-only usage
};

只要您,同样重要的是其他工程师阅读您的代码,了解您使用NodePtr作为指向节点的指针,那么请使用最适合您的代码情况。指针类型声明对某些人来说是近乎宗教的,所以请记住这一点。有些人更喜欢看到那些星号(我是一个),有些可能没有(听起来像 = P)。

注意:一个位置使用typedef ed指针类型可以有效避免潜在错误:多变量声明。考虑一下:

Node* a, b;     // declares one Node* (a), and one Node (b)

拥有typedef struct Node *NodePtr;允许:

NodePtr a, b;   // declares two Node*; both (a) and (b)

如果你花了足够的时间在C中编写代码,那么这些代码的前者会回来咬你足够的时间,你会学会不犯这个错误,但它仍然会偶尔发生一次。


加载循环

关于拼凑你的列表的加载循环,你没有正确地列出你的列表,坦率地说有一百万种方法,一个是下面的一个。这要求您清除“额外节点”。它也不需要任何if (head){} else{}块结构来避免相同的条件。考虑一下我们真正想要做的事情:创建节点并将其地址分配给正确的指针:

NodePtr head = NULL;     // always the head of the list.
NodePtr* ptr = &head;    // will always point to the next pointer to assign.
int n;
while (cin >> n)
{
    *ptr = new Node;
    (*ptr)->x = n;
    ptr = &(*ptr)->next;
}

// note this always terminates the load with a NULL tail.
(*ptr)->next = NULL;

如何运作

  1. 将头指针初始化为NULL
  2. 初始化器节点指针指针(是指向指针的指针)指向头指针。这个指向指针的指针始终保存 target 指针的地址,该地址用于接收下一个动态分配节点的地址。最初,这将是头指针。在上面的代码中,这个指向指针的变量是:ptr
  3. 开始while循环。对于读取的每个值,分配一个新节点,将其保存在ptr指向的指针中(因此*ptr)。在第一次迭代中,它保存head指针的地址,因此head变量将获得我们的新节点分配。在所有后续迭代中,它包含插入的最后一个节点的next指针的地址。顺便说一句,保存这个新目标指针的地址是在我们进入下一个分配周期之前在循环中完成的 last 事情。
  4. 循环完成后,插入的 last 节点需要将其next指针设置为NULL,以确保正确终止链接列表。 这是强制性的。我们方便地有一个指向该指针的指针(我们一直在使用的那个指针),因此我们将它指向“指向”的指针设置为NULL。我们的列表已终止,我们的负载已完成。 Brain Food:如果加载循环从未加载任何节点,它将指向的指针是什么?答案:&head,如果我们的列表为空,这正是我们想要的(NULL头指针)。

  5. <强>设计

    我希望这有助于通过循环的三次完整迭代更好地解释它是如何工作的。

    初始配置

          head ===> NULL;
    ptr --^
    

    一次迭代后:

    head ===> node(1)
              next
    ptr ------^
    

    经过两次迭代

    head ===> node(1)
              next ===> node(2)
                        next
    ptr ----------------^
    

    三次迭代后

    head ===> node(1)
              next ===> node(2)
                        next ===> node(3)
                                  next
    ptr --------------------------^
    

    如果我们在三次迭代时停止,最后的终止分配(*ptr = NULL;)给出:

    head ===> node(1)
              next ===> node(2)
                        next ===> node(3)
                                  next ===> NULL;
    ptr --------------------------^
    

    请注意,head在第一次迭代完成后永远不会更改(它始终指向第一个节点)。还要注意ptr总是保存要填充的下一个指针的地址,在初始迭代之后(它作为我们的头指针的地址开始),将始终是{{1的地址添加了 last 节点中的指针。

    我希望能给你一些想法。值得注意的是,将这两个指针(next指针和head指针)配对到它们自己的结构中并具有适当的管理功能来定义教科书Queue;其中一端仅用于插入(ptr),一个用于提取(ptr),容器不用允许随机访问。现在用标准的库容器适配器(如std::queue<>)并不需要这样的东西,但它确实提供了一个有趣的冒险,可以很好地利用指针到指针的概念。


    完成工作样本

    此示例仅使用20个元素加载我们的队列,打印它们,然后清除队列并退出。根据需要适应您的使用(提示:或许更改传入数据的来源)

    head

    <强>输出

    #include <iostream>
    using namespace std;
    
    // declare NodePtr as a pointer to Node, currently an incomplete type
    //  C and C++ both allow you to declare a pointer to damn-near anything
    //  so long as there is an understanding of what it *will* be, in this
    //  case, a structure called Node.
    typedef struct Node *NodePtr;
    
    // Now declare the structure type itself
    struct Node
    {
        int x;
        NodePtr next;
    };
    
    int main()
    {
        // load our list with 20 elements
        NodePtr head = NULL;
        NodePtr* ptr = &head;
        for (int n=1;n<=20;++n)
        {
            *ptr = new Node;
            (*ptr)->x = n;
            ptr = &(*ptr)->next;
        }
    
        // terminate the list.
        *ptr = NULL;
    
        // walk the list, printing each element
        NodePtr p = head;
        while (p)
        {
            cout << p->x << ' ';
            p = p->next;
        }
        cout << endl;
    
        // free the list
        while (head)
        {
            NodePtr victim = head;
            head = head->next;
            delete victim;
        }
    
        return 0;
    }
    

答案 1 :(得分:2)

您实际上并未将'head'变量设置为NULL(head = ptr)。你实际上从开始就丢失了你的清单。试试这个:

int n;
NodePtr head, ptr;
ptr = new Node;
head = ptr;
while (cin >> n){
    ptr->x = n;
    ptr->next = new Node;
    ptr = ptr->next;
}

然后,您可能希望删除最后一个ptr-&gt; next并将其设置为0以节省内存并避免打印出额外的值。

答案 2 :(得分:1)

您没有连接列表。每个新项目都会有->next NULL。

你说ptr = ptr->next(它是NULL)但是下一次迭代会用新分配的节点覆盖ptr。您需要重新排列代码以将节点串在一起......

这样做的一种方法是ptr->next = head使您的新节点指向旧的头部。 然后head = ptr将头部移动到新节点。 e.g:

要在开头插入(例如,对于LIFO,后进先出):

while (cin >> n){
    ptr = new Node;
    ptr->x = n;
    ptr->next = head;
    head = ptr;
}

要在末尾插入(对于FIFO):

while (cin >> n){
    if(!head) {
        head = new Node;
        ptr = head;
    }
    else
    {
        ptr->next = new Node;
        ptr = ptr->next;
    }
    ptr->next = NULL;
    ptr->x = n;
}

答案 3 :(得分:1)

首先,你的typedef:typedef struct Node;不正确。你使用sintaxe声明了一个结构:

struct st_Name{
    int field1;
    double field2;
};

Typedef就像名称归因功能。您应该(为了更容易阅读/工作)更改结构的名称,在声明之后,可以使用:

typedef struct Node_st Node;

或者,在一个声明中:

typedef struct node_st{
  int x;
  struct node_st *next;
}Node; 

在上面的部分中,请注意我也将NodePtr更改为struct node_st *Next,原因如下:如果您在一个细分中执行所有操作,则因为c ++代码是线性读取顶部&gt;向下(有点) ),如果您尝试在结构声明之前使NodePtr指向Node,则会出现错误,如果您稍后执行此操作并在结构中使用NodePtr,您将会也有错误。 编译器还不知道结构存在,所以它会说你试图命名一些不存在的东西。

然后,你的指针操作是错误的。

...
while (cin >> n){
    ptr = new Node;
    ptr->x = n;
    ptr->next = NULL;  // You're making the next slot inexistent/NULL
    ptr = ptr->next;   // You're just setting ptr to NULL
}
...

我认为你想要的是继续添加到最后一个保持头部在第一个数字。您只需使用第一种情况的if / else语句即可完成此操作:

 while (cin >> n){
     if(head == NULL){
        ptr = new Node;
        ptr->x = n;
        ptr->next = NULL;
        head = ptr;
     }else{ 
        ptr->next = new Node;
        ptr = ptr->next;
        ptr->x = n;
        ptr->next = NULL; 
     }
}

这样,您的插入最终会发生,因此您的打印循环应该可以正常工作。

希望这有帮助。