如何交替分割双向链表

时间:2014-01-27 14:37:26

标签: c doubly-linked-list

行。我在c编程课程中有一个作业。 我需要实现一个函数原型:

void split(node* head, node **first, node **second)   

此函数将head指向的双向链接列表拆分为两个列表firstsecond

假设head包含元素F0,S0,F1,S1,F2,S2,......

然后:

  • first应按此顺序包含元素:F0,F1,F2,...
  • second应按此顺序包含元素:S0,S1,S2,...

不要进行任何分配或解除分配(malloc,calloc,realloc,free)。只更新指针。不要更改节点数据。

限制:不要使用malloc(),calloc(),realloc(),free()。​​

我被卡住了,我无法生成任何算法。请帮忙!

typedef struct node
{
    int data;
    struct node *prev;
    struct node *next;
} node;

编辑解决方案:

    #define DATA(p) ((p)->data)
    #define NEXT(p) ((p)->next)
    #define PREV(p) ((p)->prev)



    void split ( node* head, node **first, node **second )
    {
        node* firstCurrent = head;
        node* secondCurrent = NULL;
        node* dummyforbprev = NULL;

        if ( firstCurrent )
        {
            secondCurrent = NEXT(firstCurrent);
            if(secondCurrent)
                PREV(secondCurrent)=NULL;
        }

        *first = firstCurrent;
        *second = secondCurrent;

        while ( firstCurrent && secondCurrent )
        {
            NEXT(firstCurrent) = NEXT(secondCurrent);
            dummyforbprev = PREV(firstCurrent);
            firstCurrent = NEXT(firstCurrent);
            if(firstCurrent)
                PREV(firstCurrent) = PREV(secondCurrent);

            if ( firstCurrent )
                NEXT(secondCurrent) = NEXT(firstCurrent);
            PREV(secondCurrent) = dummyforbprev;
            secondCurrent = NEXT(secondCurrent);
        }

        if ( firstCurrent )
            NEXT(firstCurrent) = NULL;

        if ( secondCurrent )
            NEXT(secondCurrent) = NULL;
    }

2 个答案:

答案 0 :(得分:1)

如果不为新列表分配任何新节点,只需调整指针以形成新列表,请记住,这必须修改您传递的列表英寸

从列表开始:

head -> 1 -> 2 -> 3 -> 4 -> 6 -> null

它可以相对容易地在两个节点的组中工作并将它们分配给新列表。您基本上首先确保列表中有两个或更多节点,否则返回的第一个列表是原始anfd,第二个列表为空。

检查完成后,设置要返回的列表的头指针,以及跟踪每个列表中最后一项的流动指针:

second ------|
first --|    |
head -> 1 -> 2 -> 3 -> 4 -> 6 -> null
fEnd ---|    |
sEnd --------|

然后,简单地说,您执行以下步骤:

  • fEnd下一个值设置为sEnd下一个值(因此1现在指向3)然后将fEnd指针前进到{{ 1}}。
  • 3下一个值设置为sEnd下一个值(因此fEnd现在指向2)然后将4指针前进到{{ 1}}。

现在你遇到这种情况:

sEnd

您可以看到列表的前部已经拆分,指针已经前进到列表的其余部分。

您只需继续执行此操作,直至到达列表末尾。

现在你会注意到"简单地说"上文提到的。虽然这里解释的概念很简单,但还有一些复杂的因素。

首先,您可能无法能够处理两个节点的组,这仅仅是因为列表中可能存在奇数个节点。其次,处理链接列表末尾的常见问题是你必须小心这样的事情:

4

sEnd --------------------| fEnd ---------------| | ________ | | / \ | first/ -> 1 2 3 -> 4 -> 6 -> null head |\ / | \______/ second --------| 可能在最后,当您取消引用node->next->prev = node; 时,可能会导致崩溃。

尽管如此,这些功能还有一些额外的安全性。您可以在下面看到一个完整的程序,它说明了如何执行该操作,首先是标题,节点结构和帮助函数,用于以可读形式转储列表(并确保它没有被破坏):

node

其次,"肉"解决方案,根据您的要求node->next函数,它采用单个列表并返回两个列表,每个列表都包含原始值的交替值:

#include <stdio.h>
#include <stdlib.h>

typedef struct sNode {
    int value;
    struct sNode *next;
    struct sNode *prev;
} tNode;

static void dump (char *desc, tNode *head) {
    printf ("Dumping %s: ", desc);
    tNode *p = head;
    while (p != NULL) {
        if ((p != head) && (p->prev->next != p)) {
            printf ("double-link error\n");
            exit (1);
        }
        printf ("%d -> ", p->value);
        p = p->next;
    }
    puts ("NULL");
}

如前所述,该函数也会影响原始列表,因为它必须使用原始节点,并且更改这些节点中的split()值也会使原始列表变形。

代码的最后一位是测试工具,它简单地为您提供每个节点中增加的整数列表(大小基于您给出的任何参数,如果您不提供任何参数,则为10)。

它构造列表并输出它,然后调用void split (tNode* head, tNode **pFirst, tNode **pSecond) { // Must have two elements or more to split. if ((head == NULL) || (head->next == NULL)) { *pFirst = head; *pSecond = NULL; return; } // Set up list heads and roving pointers. tNode *first = head, *second = head->next; tNode *fEnd = first, *sEnd = second; // Do whole list in groups of two, but check to ensure // no crashes due to invalid pointer dereferences. while (fEnd != NULL) { // First in group of two. if (sEnd != NULL) { fEnd->next = sEnd->next; if (fEnd->next != NULL) fEnd->next->prev = fEnd; } if (fEnd != NULL) fEnd = fEnd->next; // Second in group of two. if (fEnd != NULL) { sEnd->next = fEnd->next; if (sEnd->next != NULL) sEnd->next->prev = sEnd; } if (sEnd != NULL) sEnd = sEnd->next; } // Return lists to caller. *pFirst = first; *pSecond = second; } 函数并显示结果:

next/prev

该程序的输出可以在以下记录中看到:

split()

答案 1 :(得分:0)

我们还可以使用队列操作和bool或int变量来
在目标列表之间切换

altsplit(L:List;var L1,L2:List)
   L1.head = NIL;
   L1.tail = NIL;
   L2.head = NIL;
   L2.tail = NIL;
   s = false
   while not isEmpty(L) do
        x = dequeue(L)
        if not s then
             enqueue(L1,x)
        else
             enqueue(L2,x);
        s := not s;

请注意,我们不需要为新节点分配内存
我们只需像在队列中一样设置指针