【问题标题】:Linked List Flattening Algorithm Pointers链表展平算法指针
【发布时间】:2012-12-27 16:30:05
【问题描述】:

我正在研究以下算法来展平“编程面试暴露”一书中的树状链表系统:

void flattenList( Node *head, Node **tail)
    Node *curNode =  head;
    while( curNode ){
        /* The current node has a child */
        if( curNode->child ){
            append( curNode->child, tail );
        }
        curNode = curNode->next;
    }
}

/* Appends the child list to the end of the tail and updates
 * the tail.
 */
void append( Node *child, Node **tail ){
    Node *curNode;

    /* Append the child child list to the end */
    (*tail)->next = child;
    child->prev = *tail;

    /*Find the new tail, which is the end of the child child
     *list.
     */
    for( curNode = child; curNode->next;
         curNode = curNode->next )
        ; /* Body intentionally empty */

    /* Update the tail pointer now that curNode is the new
     * tail.
     */
    *tail = curNode;
}

我知道我们将 **tail 而不是 *tail 传递给 append 函数以确保对 *tail 的更改持续存在,但我不明白为什么我们不这样做对于孩子(意思是我们为什么不通过**child 而不是*child)?

【问题讨论】:

    标签: c algorithm linked-list


    【解决方案1】:

    C 使用“按值调用”参数传递语义。这意味着,如果您调用像f(x) 这样的函数,则只有x 的值被传递给函数,而不是变量x 本身。当您为函数内部的参数赋值时,用于调用函数的x 不会改变。

    如果您希望能够更改变量本身,则必须像在此调用中一样传递地址f(&x),如果您随后在更改变量x的函数内对*x进行赋值。

    flattenList 是这样声明的,因为 head 是按值传递的(它不会改变),但 tail 是用它的地址传递的,因为函数必须改变它。

    这类似于append 函数。第一个参数不改变,而第二个参数必须在函数内部改变。

    【讨论】:

      【解决方案2】:

      立即回答您的问题。 Nodechild 成员永远不会更新。它们仅用于遍历底层子列表。

      老实说,参数名称 (child) 的选择再糟糕不过了(不叫它parent 或类似名称)。看看以下是否更清楚。它利用了 C 是一个 byval-call 系统这一事实,并且实际上使用其中一个参数(节点指针)来查找从给定节点开始的列表的结尾,而无需任何临时指针(我们使用节点指针本身作为走,因为它是byval):

      void append( Node *node, Node **tail )
      {
          /* Append the child child list to the end */
          (*tail)->next = node;
          node->prev = *tail;
      
          /*Find the new tail, which is the end of the child child
           *list.
           */
          while (node->next)
              node = node->next;
      
          /* Update the tail pointer now that curNode is the new
           * tail.
           */
          *tail = node;
      }
      

      我应该节点,反正这不是很好的代码。它不对任何参数等的 NULL 进行检查。这应该得到显着加强。我选择放弃错误检查和非空验证,只是因为原始代码也忽略了所有这些。如果您需要更强大的版本,请告诉我。

      【讨论】:

        【解决方案3】:

        append里的cmets说的很清楚

        /* Update the tail pointer now that curNode is the new
         * tail.
         */
        *tail = curNode;
        

        因为您正在更新尾指针本身(而不仅仅是它所引用的节点),所以您需要传递 pointer to a pointer 以使更新在函数外部可见。

        对于child 指针,我们只更新它所指的节点,而不是指针本身。因此,没有理由通过添加另一个间接级别来使代码进一步复杂化。

        【讨论】:

          【解决方案4】:
          struct node *flatten_nr(struct node *root)
          {  
          
          
              struct node *temp1, *temp2;
          
              if (NULL == root) return NULL;
              temp2 = root;
              while (root != NULL)
              {
                  if( root->next != NULL)
                  {
                      temp1 = root;
                      /* go to the end of the list and append */
                      while (temp1->down != NULL)
                      {
                          temp1 = temp1->down;
                      }
                      temp1->down = root->next;
                  }
                  root = root->down;
              }
              return temp2;
          }
          

          【讨论】:

            猜你喜欢
            • 2022-11-21
            • 1970-01-01
            • 2015-06-17
            • 1970-01-01
            • 1970-01-01
            • 2014-11-29
            • 2012-08-11
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多