【问题标题】:How do you insert into a sorted linked list? [duplicate]你如何插入一个排序的链表? [复制]
【发布时间】:2009-08-24 04:17:58
【问题描述】:

可能重复:
LinkedList “node jump”

我只需要一个按名称排序的链表。我只能到第 1、第 3、第 5、.. 节点。我只是想不到这么远。我想成为一名 C++ 程序员,但如果我不明白这是他们的希望吗? STL 容器std::lists 目前对我来说不是一个选择。你在 list 函数中看到的正是我想要理解的。

list::node::node(const winery &winery) : item( winery.getName(), winery.getLocation(),
    winery.getAcres(), winery.getRating() ), nextByName( NULL ), nextByRating( NULL )
{

}

void list::insert(const winery& winery)
{
    node *current_node  = new node( winery ); // but came here and did this so it has new info!
    node *next_node     = NULL;
    node *tail_node     = current_node;

    if ( headByName == NULL ) // then we are here for the first item
    {       
        headByName   = current_node; // the list ptrs will have the first node's address. 
        headByRating = current_node; 
    }
    while ( headByName->nextByName != NULL )
    {
        headByName->nextByName = tail_node;
        tail_node = next_node;
        //next_node = current_node;
    }
    tail_node = new node( winery ); 
    headByName->nextByName = tail_node;
}

以及可供我使用的指针:

struct node
{
    winery  item;
    node *  nextByName;
    node *  nextByRating;
};

class list
{
    ...
private:
    node * headByName;
    node * headByRating;
};

【问题讨论】:

  • 在我看来像是单链表,而不是双链表...
  • 它不是传统意义上的双向链表,而是一个表示同一组节点的两种不同排序的列表。通常的排序是按插入顺序和反向插入顺序,但在这种情况下(根据变量名称),它们是按名称按评级。
  • 灯罩,不要以这种方式编辑您的帖子。
  • @lampshade:如果您不希望看到您的问题,可以将其删除。

标签: c++ linked-list


【解决方案1】:

当然有希望,我们都必须从某个地方开始! :)

首先,我必须指出我自己的学生在您的实现中使用的一种危险做法,它通常会导致很多人挠头来发现问题:

while ( headByName->nextByName != NULL )
{
     headByName->nextByName = tail_node;
     tail_node = next_node;
     //next_node = current_node;
}

除非绝对必要,否则不要在循环中使用像 headByName 这样的列表成员作为迭代变量。您应该首先使用局部变量找到合适的节点,然后修改该节点。

也就是说,Rob Kennedy 是对的,您应该分别处理名称和评级(换句话说,“纯”实现将有两个不知道什么是通用列表类的实例name' 和 'rating' 的意思),但是我假设上面的列表、节点和函数的接口已在作业中提供给您(如果没有,请忽略我的其余答案:))。

鉴于以上接口,我的做法如下:

void list::insert(const winery& winery)
{
    node* wineryNode = new node(winery);
    assert (wineryNode);

    // Find  a node in the list with a name greater than wineryNode's
    node* prevNode = NULL;
    node* searchNode = headByName; // Note: not using the list member itself to iterate but a local variable
    while (NULL != searchNode && 
        searchNode.winery.getName() < wineryNode.winery.getName())
    {
        // Keep iterating through the list until there are no more items, or the right node is found
        prevNode = searchNode;
        searchNode = searchNode->nextByName;
    }   

    /* At this point searchNode is either NULL 
       or it's name is greater than or equal to wineryNode's */

    // Check for the case where the list was empty, or the first item was what we wanted
    if (NULL == prevNode)
    {
        headByName = wineryNode;
    }
    else
    {
        // prevNode is not NULL, and it's Name is less wineryNode's
        prevNode-> nextByName = wineryNode;
    }
    wineryNode->nextByName = searchNode;    

    /*  Now you just need to insert sorted by rating using the same approach as above, except initialize searchNode to headByRating, 
    and compare and iterate by ratings in the while loop. Don't forget to reset prevNode to NULL */
}

【讨论】:

  • 代码中的第三行 3..HeadByName 为空?
  • 我确实设法弄清楚如何获取所有节点,而无需将尾部和前一个指针声明为列表的私有成员 class=)
  • 很高兴你得到了排序!第3行没有headByName?如果你是这个意思: node* searchNode = headByName;那么这是正确的,最初没有头,所以循环会立即退出,prevNode==NULL 和 searchNode==NULL,所以新节点将成为新的 headByName(在 if 检查中),并且永远不会引用 prevNode . nextByName 也将正确地分配给 NULL。
【解决方案2】:

您将其称为双向链表,尽管您的节点确实有两个链接,但这并不是大多数人在听到双向链表时真正想到的。由于您的两个链接显然彼此无关 - 没有人按字母顺序对酒厂进行评级 - 如果您不认为这是一个双向链接列表,将会更容易一些。

当不需要更具体的顺序时,插入链表的通常位置是在链表的末尾。具体步骤如下:

  1. 创建一个新节点。
  2. 找到插入新节点的位置(即最后一个节点,在列表的末尾)
  3. 更新最后一个节点的“next”指针以指向新节点。

当您插入可能是列表的中间的内容时,就像您的情况一样,还有另一个步骤:

2.5。更新新节点的“next”指针。

step 通常不存在的原因是,在列表末尾插入时,新节点的“next”指针在您构造新节点对象时已经为空。

您已经找到了第一步。事实上,您做得很好,因为您的代码实际上创建了两个新节点。一个存储在current_node,另一个存储在tail_node。摆脱第二个。

第 2 步是关于确定哪个节点应该是紧接在新节点之前的那个节点。当按名称排序时,这将是您找到的第一个节点之前的节点,该节点的名称​​在当前名称之后。您将不得不沿着列表走,可能会查看每个节点,直到找到一个属于新节点名称之后的节点。当您沿着列表移动时,您还必须跟踪哪个节点出现在该节点之前,因为一旦找到要查找的节点,您将不得不回溯。

分别担心名称和评级。不要试图同时解决这两个部分。一旦您按名称正确插入酒厂,然后复制该代码并将“名称”替换为“评级”。但不要创建另一个节点对象。继续使用您之前创建的那个。

当您完成这项作业时,您是否绘制了任何带有指向其他节点的箭头的节点图片?尝试一下。您肯定已经在教科书或老师的黑板上看到过它。如果一个问题太大而你无法完全在脑海中推理,那么使用一些东西来帮助你跟踪你头脑之外的事情。专业人士也这样做。由于每个节点都有多个指针,因此您应该标记指针或使用不同的颜色来命名和评级。

【讨论】:

    【解决方案3】:

    要成为双向链表,每个节点都必须有一个指向其前后节点的指针。如果您愿意,也可以存储头尾节点,但这更多是为了个人方便,而不是满足双向链表的要求。

    【讨论】:

      【解决方案4】:

      我不确定我是否完全理解您的问题。仅从主题“如何插入已排序的链表?”开始。我会做这样的事情(伪代码):

      list::insert(Winery winery) {
          Winery node = getHeadNode();
      
          // winery comes before the head node
          if (winery.name < node.name) {
              // the old head node now goes after this new node
              // and this new node becomes the new head node
              winery.next = node;
              setHeadNode(winery);
              return;
          }
      
          // let's iterate over the rest of the nodes in the list
          Winery previous_node = node;
          node = node.next;
          while (node != NULL) {
              // we found our insertion point
              if (winery.name < node.name) {
                  // insert our new node
                  previous_node.next = winery;
                  winery.next = node;
                  return;
              }
      
              // insertion point not found, move to the next node and repeat
              previous_node = node;
              node = node.next;
          }
      
          // all elements visited, append our new element to the end of the list
          previous_node.next = winery;
          winery.next = NULL;
      }
      

      假设列表按名称排序,上述算法会将您的新节点插入列表中的适当位置。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-04-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多