【问题标题】:A function implementation to cut a portion of a link list and append it to the end of the list用于剪切链接列表的一部分并将其附加到列表末尾的函数实现
【发布时间】:2014-02-27 19:07:17
【问题描述】:

考虑以下函数:

void CustomAppender(int totalNodesToSkip, int totalNodesToCut);

这里totalNodesToSkiptotalNodesToCut 都是给定的输入。所以需要从 totalNodesToSkip 编号的节点开始切割长度为 totalNodesToCut 的给定链表的一部分。

考虑以下列表:

List_1:a->b->c->d->e->f->g->h->i->j->k

函数的输入是CustomAppender(2,3)

那么在第一遍中,列表应该变成:

List_2: a->b->f->g->h->i->j->k->c->d->e. 所以这里 c->d->e 部分移到了尾部。这个过程需要重复,直到我们到达给定列表的末尾。也就是说,当我们到达列表 1 的节点“k”时,我们需要停止。

解决这个问题的简单方法是根据给定的输入取要切割的部分: 在 *Marker 中保持一个标记到终点“k”。

  1. *startPTR 指向节点 c.

  2. *endPTR 指向节点 e。 (两者都可以使用给定的输入进行迭代。)

  3. 点 b 节点下一个作为 endPTR 的下一个。

  4. 'k' 节点的下一个指向 startPTR。

  5. 最后 endPTR 指向 NULL。

  6. 可以重复上述步骤,直到到达标记。

这工作正常。

但我的问题是无论如何以任何不同的方式实现它,以便我使用更少的指针和更快的方法。由于此过程需要 O(n^2) 运行时间。

请建议任何其他方式。

【问题讨论】:

    标签: c algorithm data-structures linked-list


    【解决方案1】:

    您可以在列表中一次性完成。

    starting at the head of the list, move forward `(skip-1)` nodes. Call this `skip_ptr`.
    start another pointer, call it `glue_ptr`. Move it forward `(cut-1)` nodes.
    

    此时,skip_ptr 引用最后一个要跳过的节点,glue_ptr 引用最后一个要剪切的节点。

    现在,将它们粘在一起并标记切割清单的结尾。

    cut_ptr = skip_ptr.next;  // save c->d->e
    skip_ptr.next = glue_ptr.next; // attach a->b to f->g ...
    glue_ptr.next = null; // mark e as the end of the cut list
    

    所以现在你有a->b->f->g->h->i->j->k,并且cut_ptr 指向c->d->e

    现在,如果您设置glue_ptr = skip_ptr.next 并将glue_ptr 向前移动到glue_ptr.next == null,那么您可以编写:

    glue_ptr.next = cut_ptr;  // attaches the cut part to the end of the list
    

    您只需要跟踪各个部分。但它一次完成操作:O(n)。

    更新

    如果你必须反复跳过并剪切到原来的列表:

    a,b,c,d,e,f,g,h,i,j,k
    

    变成

    a,b,f,g,k,c,d,e,h,i,j
    

    您仍然可以通过单次遍历列表来完成此操作。这是显示它是如何完成的 C# 代码。应该很容易转换为 C。

    private void CustomAppender(LLNode list, int skip, int cut)
    {
        LLNode skip_ptr = list;
        LLNode cut_list = null;
        LLNode cut_ptr = null;
    
        while (skip_ptr.Next != null)
        {
            // skip over 'skip' items
            for (int i = 1; i < skip && skip_ptr.Next != null; ++i)
            {
                skip_ptr = skip_ptr.Next;
            }
    
            // skip_ptr.next is the start of the next group we want to cut.
            // move forward to the last node to be cut.
            for (int i = 0; i < cut && skip_ptr.Next != null; ++i)
            {
                LLNode nodeToCut = skip_ptr.Next;
                skip_ptr.Next = nodeToCut.Next;
                nodeToCut.Next = null;
                if (cut_list == null)
                {
                    cut_list = nodeToCut;
                }
                else
                {
                    cut_ptr.Next = nodeToCut;
                }
                cut_ptr = nodeToCut;
            }
            if (skip_ptr.Next != null)
                skip_ptr = skip_ptr.Next;
        }
        // So now we have two lists.
        // skip_ptr points to the last node in the list.
        // Append cut_list to the end.
        skip_ptr.Next = cut_list;
    }
    

    【讨论】:

    • 感谢@Jim 的回复。但这只会在第一遍中给出列表。我需要重复执行此操作,直到到达最后一个节点。例如,我有一个 1000 个节点的列表,然后我必须在 2 个节点切割长度为 3 的节点的一部分后重复执行此操作,除非我到达列表的末尾。那个结尾是列表的结尾,在对列表进行任何更改之前我应该​​标记它。
    • @Shantanu:你是说你必须重复跳过 2 并删除 3,以便最终得到a-&gt;b-&gt;f-&gt;g-&gt;k-&gt;c-&gt;d-&gt;e-&gt;g-&gt;h-&gt;i?如果是这样,您仍然可以在一次扫描中完成。只需保持指向切割列表末尾的指针并继续将切割部分粘贴到它上面。当您到达原始列表的末尾时,您将添加剪切部分列表。它仍然是列表的单次扫描。
    • 是的,我必须反复这样做。无论您提出的建议与我在问题中描述的相同。最终你必须运行至少两个一嵌套循环。
    • 是的,我必须反复这样做。无论您提出的建议与我在问题中描述的相同。最终你必须运行至少两个一嵌套循环。 for(PTR = Head; Run untill end of the list; PTR = PTR-&gt;Next) // Outer loop to iterate through the list. { for()//Inner loop to cut the nodes and append to end of the list } 因此有两个循环,所以 O(n^2)。
    • @Shantanu:您仍然可以通过单次扫描列表来完成。查看我的更新。
    【解决方案2】:

    i 首先计算元素的总数。因为我们必须将元素分成 3 个链。现在从总数中减去跳过的数字。作为分裂链,即

      a->b c->d->e->f-g->h->i->j->k
    

    现在,我们还剩下号码。现在取剩余数的模数。如果模块为 0,则在计数后从最后一个拆分出来的数字什么也不做。

    如上所述,您已经采取了 3,因此不需要任何操作来反转事物,但在 2 中,拆分为:

    a->b c->d->e->f-g->h->i->j k
    

    现在将最后一个元素链更改为第二个元素链:

     a->b k c->d->e->f-g->h->i->j
    

    现在合并它们

     a->b->k->c->d->e->f-g->h->i->j
    

    【讨论】:

    • 我认为它不会起作用。因为以下是迭代切割到列表末尾后的列表结果。原列表:a-&gt;b-&gt;c-&gt;d-&gt;e-&gt;f-g-&gt;h-&gt;i-&gt;j-&gt;k Pass_1:从c切到e,迭代指针指向b。结果是:a-&gt;b-&gt;f-&gt;g-&gt;h-&gt;i-&gt;j-&gt;k-&gt;c-&gt;d-&gt;e Pass_2:从 h 切割到 j 并迭代指向 g 的指针。结果是:a-&gt;b-&gt;f-&gt;g-&gt;k-&gt;c-&gt;d-&gt;e-&gt;h-&gt;i-&gt;j Pass_3:这里迭代指针递增一次到达节点 k,它是父列表中的最后一个节点。因此达到终止条件并且过程完成。
    • @Shantanu 但是 f->g->k 清除了 3 个变量 + 结束标记的条件。 Number of Steps 进行切割和链接的另一件事需要更多的步骤。但是使用我的公式,您必须只切割经过优化的相同工作。如果模块 == 0,那么您可以针对该条件执行操作。您可以根据自己的需要进行更改,这是做事的基本理念..
    • 按照您的方法,我认为解决方案不正确。对不起,如果我听起来很粗鲁。
    猜你喜欢
    • 2011-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多