【问题标题】:Trouble merging two linked lists in C在 C 中合并两个链表时遇到问题
【发布时间】:2019-12-29 01:05:44
【问题描述】:

我应该编写一个函数来合并(将一个放在另一个的末尾)两个单链表。用户在控制台中输入一系列数字,例如:1 2 3 4 0(0 表示输入结束,不是列表的元素)。这些数字被放入链表,链表现在看起来像这样:1 2 3 4。这个过程再次重复,直到我们有两个不同的链表。然后合并函数称为“void merge(struct Node head1, struct Node head2)”。打印新列表后程序结束。

我的想法是首先让我的指针指向第一个列表的末尾,然后创建一个 while 循环,该循环将遍历另一个列表,并使第一个列表的下一个元素成为第二个列表的当前元素列表。

typedef struct Element Element;

struct Element
{
    int data;
    Element *next;
};

Element *addNew(int data)
{
    Element *newN = (Element*)malloc(sizeof(Element));

    newN->data = data;
    newN->next = NULL;

    return newN;
}

Element *add_on_beginning(Element *head, Element *newN)
{
    newN->next = head;

    return newN;
}

Element *add_on_end(Element *head, Element *newN)
{
    if(head == NULL)
    {
        return newN;
    }

    Element *temp = head;

    while(temp->next != NULL)
    {
        temp = temp->next;
    }

    temp->next = newN;

    return head;
}

void printElement(Element *element)
{
    printf("%d ", element->data);
}

void printList(Element *head)
{
    Element *temp = head;

    while(temp != NULL)
    {
        printElement(temp);
        temp = temp->next;
    }
}

void merge(Element *head1, Element *head2)
{
    Element *temp1 = head1;
    Element *temp2 = head2;

    while(temp1->next != NULL)
    {
        temp1 = temp1->next;
    }

    while(temp2->next != NULL)
    {
        temp1->next = temp2;
        temp2 = temp2->next;
    }
}

int main()
{
    Element *head1 = NULL;
    Element *head2 = NULL;

    int arr[1000];
    char temp1;
    char temp2;
    int i = 0;
    int j = 0;

    printf("Input the first set of elements: \n");

    while(temp1 != '\n')
    {
        scanf("%d%c", &arr[i], &temp1);

        if(arr[i] == 0)
        {
            break;
        }

        head1 = add_on_end(head1, addNew(arr[i]));

        i++;
    }

    printf("Input the second set of elements: \n");

    while(temp2 != '\n')
    {
        scanf("%d%c", &arr[j], &temp2);

        if(arr[j] == 0)
        {
            break;
        }

        head2 = add_on_end(head2, addNew(arr[j]));

        j++;
    }

    merge(head1, head2);

    printList(head1);

    return 0;
}

所以由于某种原因,该函数只读取第二个列表的最后两个元素。

输入:

1 2 3 4 0
5 6 7 8 0

输出:

1 2 3 4 7 8

我应该得到的结果是

输入:

1 2 3 4 0
5 6 7 8 0

输出:

1 2 3 4 5 6 7 8

【问题讨论】:

  • 不是你的问题,但我建议坚持使用更简单的 scanf 调用。不要打扰%c 并查看是否有换行符。但是请检查scanf 的返回值,然后继续它是 1。
  • 您想让第一个列表末尾的next 元素指向第二个列表的head。在merge(),根本不需要扫描第二个列表。
  • 这确实有效,我不敢相信我这么愚蠢没有看到...所以对于将来查看这篇文章的任何人,在函数合并中,只需摆脱第二个while循环,而不是写temp->next = head2,仅此而已。 @SteveSummit 感谢所有帮助!
  • 如所写,您的 merge 函数无法处理第一个列表为 NULL 的情况。为了处理这个问题(并与编写其他函数的方式保持一致,例如 add_on_end),您希望 merge 返回新列表。
  • 虽然标签 merge 的描述足够通用,但我会避免使用术语 merge 来表示没有 O(1) "random 的数据结构联合"/indexed access 如果它没有保持每个结构单独显示的顺序join, chain, concatenate, (unite?), ...

标签: c algorithm merge linked-list singly-linked-list


【解决方案1】:

这个函数

void merge(Element *head1, Element *head2)
{
    Element *temp1 = head1;
    Element *temp2 = head2;

    while(temp1->next != NULL)
    {
        temp1 = temp1->next;
    }

    while(temp2->next != NULL)
    {
        temp1->next = temp2;
        temp2 = temp2->next;
    }
}

无效。

首先它并没有改变原来的指针head1和head2,因为它们是按值传递给函数的。因此该函数处理原始指针的副本。

其次在函数中没有检查head1head2是否等于NULL

函数可以通过以下方式定义

void merge( Element **head1, Element **head2 )
{
    if ( *head1 == NULL )
    {
        *head1 = *head2;
        *head2 = NULL;
    }
    else if ( *head2 != NULL )
    {
        while ( *head1 != NULL ) head1 = &( *head1 )->next;

        for ( ; *head2 != NULL; head2 = &( *head2 )->next )
        {
            *head1 = *head2;
            head1 = &( *head1 )->next;
        }
    }              
}

注意,列表中输入数据不需要声明数组。

还有这些 while 循环

    char temp1;
    char temp2;
    int i = 0;
    int j = 0;

    printf("Input the first set of elements: \n");

    while(temp1 != '\n')
    //..

   while(temp2 != '\n')
   //...

具有未定义的行为,因为 temp1temp2 均未初始化。

【讨论】:

  • 为什么只指定有条件的*head2 = NULL(如果*head1 == NULL)? merge() 确实可以定义为第 1 修订版中所示,但请参阅 Victor Dominguez Muñoz's answer 的第 2 部分。
  • @Victor Dominguez Muñoz's 因为在其他情况下 head2 由于循环将被设置为 NULL。
  • Nah - 要么使用一个不以 head 作为名称部分的单独指针来迭代列表(无论如何这可能是个好主意),或者在迭代后做一些勤奋的洗牌(*)head1指定的列表。
  • @greybeard 你还没明白你想说什么。正如我所说,由于 for 循环,原始指针 header2 设置为 NULL。所以没有必要,引入单独的指针只是一个坏主意。
【解决方案2】:

你的一个问题是:

while(temp2->next != NULL) {
    temp1->next = temp2;
    temp2 = temp2->next;
}

您没有更新 temp1 的值。

另外,你为什么不这样做而不是这一秒呢:

temp1->next = temp2;

我的意思是链表 2 已正确链接,您只需将第一个列表的末尾与第二个列表的开头链接即可。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多