简体   繁体   English

我怎样才能理解链表?

[英]How can I understand linked lists?

I'm trying to understand how they work, but I'm having a lot of difficulties. 我试图了解它们是如何工作的,但我遇到了很多困难。 Does anyone care to explain them intuitively, or offer resources they think work well for those who are just beginning with the topic? 是否有人愿意直观地解释它们,或者提供他们认为适合刚开始使用该主题的人的资源?

So let's say I have this: 所以让我说我有这个:

struct node
{
   int nodeNum;
   nodes *next;
}

To create the "head" node I'd do the following: node *head = new node; 要创建“head”节点,我将执行以下操作: node *head = new node; so that my linked list now looks like 所以我的链表现在看起来像 在此输入图像描述 . After assignment: 分配后:

head->nodeNum = 10;
head->next = NULL;

we have 我们有 在此输入图像描述 . Now, if I wanted to write a function that inserts a node, can I write: 现在,如果我想编写一个插入节点的函数,我可以写:

void insert(node *previousNode, int num)
{
    previousNode = new node;
    previousNode->nodeNum = num;
    previousNode->next = NULL;
}

So that if I were to do, say, insert(head, 20); 所以,如果我这样做,比如insert(head, 20); my new list looks like 我的新列表看起来像 在此输入图像描述 ?

If everything is correct, how can I use this information to search and/or remove nodes from the list? 如果一切正确,我如何使用此信息从列表中搜索和/或删除节点? Traversing through the nodes isn't really intuitive as described head = head->next; 遍历节点并不是非常直观,如head = head->next; , for example. , 例如。 How does this work? 这是如何运作的?

Any advice you can offer to make this topic easier to understand would be great. 您可以提供任何建议,使这个主题更容易理解将是伟大的。 Thanks for the help everyone! 感谢大家的帮助!

Your insert function doesn't work properly; 您的插入功能无法正常工作; it just creates a new node without adding it to the list, and loses it (giving a memory leak) when the function returns: 它只是创建一个新节点而不将其添加到列表中,并在函数返回时丢失它(给出内存泄漏):

head -> 10 -> NULL     becomes    head -> 10 -> NULL
                                  (lost)  20 -> NULL

Instead, it should link the old tail of the list to the new node, and insert the new node after the old one: 相反,它应该将列表的旧尾部链接到新节点,并在旧节点之后插入新节点:

void insert(node * prev, int num) {
    node * new_node = new node;
    new_node->nodeNum = num;
    new_node->next = prev->next;  // add the old tail after the new node
    prev->next = new_node;        // add the new node after the old node
}

insert(head, 20); // insert 20 after the head
// head -> 10 -> NULL   becomes    head -> 20 -> 10 -> NULL

How can I use this information to search and/or remove nodes from the list? 如何使用此信息从列表中搜索和/或删除节点?

To iterate, you maintain your own pointer to the element you're looking at; 要进行迭代,您需要维护自己的指针,指向您正在查看的元素; this begins at head , and then follows the next pointer until it reaches the end (ie next is null): head开始,然后跟随next指针,直到它到达结尾(即next为null):

for (node * n = head; n; n = n->next) {
    if (n->nodeNum == 20) {
        std::cout << "Found node 20!\n";
        break;
    }
}

To remove a node from a singly-linked list, you need a pointer to the node before it in order to update its next pointer: 要从单链表中删除节点,需要一个指向其前面节点的指针,以便更新其next指针:

void remove_next(node * prev) {
    if (prev->next) {
        node * next = prev->next->next;  // Get the tail after the removed node
        delete prev->next;
        prev->next = next;               // Add the tail after the remaining node
    }
}

remove_next(head);
// head -> 20 -> 10 -> NULL    becomes    head -> 10 -> NULL

Your problem is here: 你的问题在这里:

void insert(node *previousNode, int num)
{
previousNode = new node;
previousNode->nodeNum = num;
previousNode->next = NULL;
}

insert(head, 20);

Here is what this bit of code does: previousNode = new node; 这是代码的作用: previousNode = new node; makes a pointer to a node and assignes that pointer to previousNode. 创建一个指向节点的指针,并指定指向previousNode的指针。 PreviousNode started off as a copy head, it now points to something new. PreviousNode以复制头开始,它现在指向一些新东西。 You now assign values to the new node. 您现在可以为新节点分配值。 In other words, this implementation of insert doesn't insert. 换句话说,插入的这种实现不会插入。

What you want to do is something more like: 你想做的更像是:

void better_insert(node *previousNode, int num)
{
    node *post_node = new node;    #create a brand new pointer to a brand new node
    post_node->nodeNum = num;      #give it a number
    post_node->next = previousNode->next; #we want previousNode to be behind new node
    previousNode->next = post_node;     
}

What this does is: after creating a new node and pointing to it with a new pointer we give it a number. 它的作用是:在创建一个新节点并用新指针指向它之后,我们给它一个数字。 Next thing is to sort out where the pointers are pointing... 接下来要弄清楚指针指向的位置......

let's pretend we wave some nodes in a linked list. 让我们假装我们在链表中挥动一些节点。 All lower case letters are pointers, ok? 所有小写​​字母都是指针,好吗?

a->next = b

now say we want node x to come after a , and have the number 10... we call `better_insert(a, 10) 现在说我们希望节点xa之后,并且数字为10 ...我们称之为`better_insert(a,10)

post_node points to a new node (our node x), and is assigned 10. cool... post_node指向一个新节点(我们的节点x),并被赋予10. cool ...

we want: 我们想要:

a->next = x
x->next = b

we have: 我们有:

a->next = b
x->next = null

the last two lines of the function just shuffle stuff til it fits the bill 函数的最后两行只是改变了东西,直到它适合账单

So in more detail... 所以更详细......

we have: 我们有:

a->next = b
x->next = null

so we call: 所以我们打电话:

post_node->next = previousNode->next; #we want previousNode to be behind new node

now we have: a->next = b x->next = b 现在我们有:a-> next = b x-> next = b

now we call: 现在我们打电话:

previousNode->next = post_node;

and we end up with: 我们最终得到:

a->next = x
x->next = b

or in other 'words': 或者在其他'词'中:

a->next = x
a->next->next = b

You need to use more variables in your code. 您需要在代码中使用更多变量。 An insert operation modifies two nodes. insert操作修改两个节点。 The previous node needs to be changed to point to the new node, and the new node needs to be created and made to point to the node after the previous node (which may or may not be NULL). 需要将先前节点更改为指向新节点,并且需要创建新节点并使其指向前一节点之后的节点(可能是也可能不是NULL)。

void insert(node *previousNode, int num)
{
    node *newnode = new node;
    newnode->nodeNum = num;
    newnode->next = previousNode->next;
    previousNode->next = newnode;
}

To traverse the list, you keep track of a "current node", which you can change from one node to the next: 要遍历列表,您可以跟踪“当前节点”,您可以从一个节点更改为下一个节点:

while (currentNode != 0) {
    do_something_with(currentNode);
    currentNode = currentNode->next;
}

Of course, if do_something_with removes the node then you can't step forward from it afterwards. 当然,如果do_something_with删除了节点,那么之后就无法从它向前迈进。 Also, to remove a node from a singly-linked list you need a pointer to the node before it. 此外,要从单链接列表中删除节点,您需要一个指向该节点之前的节点的指针。 So in that case your loop would likely track two nodes, current and previous, rather than just one. 因此,在这种情况下,您的循环可能会跟踪两个节点,当前和之前,而不是一个。

The terminology you're using is confusing you. 您正在使用的术语让您感到困惑。 Hopefully this analogy doesn't further confuse you. 希望这个比喻不会让你更加困惑。 Essentially, imagine the linked list as this horrifying set of doorways where once you enter one doorway, it closes behind you and you can only see what's in that room or go to the next room. 从本质上讲,想象链接列表就像这个可怕的门道一样,一旦你进入一个门口,它就会在你身后关闭,你只能看到那个房间里的东西或者去另一个房间。 From the outside of this hallway, you only know where the entrance is, not what's inside. 从这个走廊的外面,你只知道入口的位置,而不是里面的东西。

So, from outside of your linked list structure, all you know is the entrance, some pointer ll_node *head; 所以,从链表结构的外部,你只知道入口,一些指针ll_node *head; . Inside of head is some data and a pointer to the next node in your linked list. head内部是一些数据和指向链表中下一个节点的指针。 Traversing that linked list is as simple as starting from the entrance to the hallway, head , and entering one hallway at a time until you find the node you're looking for. 遍历该链表非常简单,从入口到走廊, head ,一次进入一个走廊,直到找到您正在寻找的节点。

ll_node *current_location = head;
while (current_location != NULL)
{
   // if we are at the node that you were hoping to reach, exit.
   if (current_location->nodeNum == target_data_im_looking_for)
   {
      break;
   }
   // this isn't the node you're looking for, go to the next one.
   current_location = current_location->next;
}

Similarly, inserting a node should traverse to the end of the linked list (until current_location->next == NULL ) and replace the last element's next pointer with the memory location of a new ll_node you create. 类似地,插入节点应该遍历到链表的末尾(直到current_location->next == NULL )并将最后一个元素的下一个指针替换为您创建的新ll_node的内存位置。 I won't implement that for you so you have a chance to learn, but there's enough here to get to where you want to be. 我不会为你实现这个,所以你有机会学习,但这里有足够的东西到达你想成为的地方。

Typically a linked list's node does not contain its number. 通常,链表的节点不包含其编号。 It, usually, consist of a Data, and a pointer to the next node. 它通常由一个Data和一个指向下一个节点的指针组成。 Also you should get your code free of typos. 你也应该让你的代码免于拼写错误。

typedef Data int;

struct node
{
   Data data; // some Data
   node *next;   // pointer to the next node
}

In general you also don't want a new node after a specified one, but rather to a list. 通常,您也不希望在指定的节点之后使用新节点,而是需要列表。 void insert(node *previousNode, int num) signature suggest you want new node after some specified previous one. void insert(node *previousNode, int num)签名建议您在某个指定的前一个节点之后需要新节点。

Technically you can add a new node to a list in three ways. 从技术上讲,您可以通过三种方式将新节点添加到列表中。 To the beginning or the end of a list or somewhere in the middle. 到列表的开头或结尾或中间的某个地方。 Adding to the beginning is the fastest and most simple. 添加到开头是最快和最简单的。

void insert(Data num)
{
    node* tmp = new node;  //make a new node
    tmp->data = num;       //fill in data
    tmp->next = head;      //set the next element of new one to be the current head
    head = tmp;            //set new element as the head
}

This way you put a new element in front of the list. 这样,您就可以在列表前放置一个新元素。 You always traverse a one-way linked list from head to the end. 您总是从头到尾遍历单向链表。

void print_all()
{
   node* current = head;      // start at head
   while(current != NULL){    // while element exists
     cout << current->data << ' ';   //print its data
     current = current->next; //move to the next one
   }
}

It is very easy to understand when you have pictures with boxes for data, and arrows for node* next . 当你有带数据框的图片和node* next箭头时,很容易理解。

That's for starters. 这是初学者。 You need to modify the insert function to handle special case where the list is initially empty. 您需要修改insert函数以处理列表最初为空的特殊情况。 Ie head == NULL . head == NULL

Also I would strongly encourage you to try to implement it in a Object Oriented way. 另外,我强烈建议您尝试以面向对象的方式实现它。 Write class List that makes use of struct node . class List使用struct node class List

class List {
  private:
    node* head;
  public:
    List();
    void insert(data);
    void print_all();
}

Try implementing these functions. 尝试实现这些功能。 From my experience it helps organize your thinking about data structures and container when you do things this way and it's kinda more c++ way to do things like that. 根据我的经验,当你以这种方式做事时,它有助于组织你对数据结构和容器的思考,而这更像是c ++这样做的事情。

In your above mentioned list you have very little information ie nodeNum. 在上面提到的列表中,您只有很少的信息,即nodeNum。 you should keep more ionformation into it. 你应该保留更多的离子信息。 For example at on Node with value nodeNum = 10 there should be some other information you want to get from that Node.So that when user call 例如,在具有值nodeNum = 10的节点上,您应该从该节点获取一些其他信息。因此,当用户调用时

node* search(node *head,int num){
      node *temp = head;//copy node reference into temp variable
      while (temp != NULL){
            if (temp->nodeNum == num){
               return temp;
            }
            temp = temp->next;
      }

} 

you can get data in that node with node 您可以使用节点获取该节点中的数据

 res = search(head,10);

For delete 删除

bool delete(node *head,int num){
      node *temp = head;//copy node reference into template
      node *prev = NULL;
      bool isDelete = false;
      while (temp != NULL){
            if (temp->nodeNum == num){
               break;
            }else
               prev = temp;
               temp = temp->next;
            }
      }
      if (temp != NULL){
          prev-> next = temp->next;//maintain list
          delete temp;
          isDelete = true;
      }
      return  isDelete;
} 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM