【问题标题】:My function to merge two linked lists is not working properly我合并两个链表的功能无法正常工作
【发布时间】:2019-12-29 14:07:24
【问题描述】:

我应该创建一个函数来合并两个排序的单链表。

用户在控制台中输入两组数字,例如:5 3 1 0,组成一个链表,0表示该输入结束,不属于该链表。然后对列表进行排序,从最小值到最大值。排序后,列表的元素应该被一个一个地添加到一个新的列表中。最后,应该打印新创建的列表。

我尝试了很多不同的方法,但我最喜欢的一个是有两个不同的指向头的指针,所以 p 将是 head1 的指针,q 将是 head2 的指针。我从较小的 head 值开始,并将该 head 的指针移动到下一个元素。然后我记得我要在最后返回的节点的头部,然后进入 while 循环,从一个列表到另一个列表。

好的,我使用 S 将元素添加到我的新列表中,首先我从两个列表之间的较小数字开始,在本例中为 1 和第一个列表,只要我给 S 其值,我还将指针 p0 移动到下一个元素,所以现在它指向 3 (p1)。然后我把那个 S 作为我的新列表的头,然后进入 while 循环。在 while 循环中,我检查添加到新列表中的最后一个元素是来自第一个列表还是第二个列表,具体取决于结果,假设最后一个元素来自第一个列表(编号 1),我们移动到第二个清单。我让 S 指向 q1,然后 q 指向第二个列表 (q2) 的下一个元素,while 循环再次开始。该过程不断重复,直到两个指针之一为 NULL。在 while 循环之后,我有两个 if 语句(每个指针一个,以防它们指向 NULL),我返回另一个指针。希望现在它更清楚了。

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 swap(Element *a, Element *b) {
    int tempV;

    tempV = a->data;
    a->data = b->data;
    b->data = tempV;
}

void sortList(Element *head) {
    Element *temp;
    int swapped;

    do {
        swapped = 0;

        temp = head;

        while (temp->next != NULL) {
            if (temp->data > temp->next->data) {
                swap(temp, temp->next);
                swapped = 1;
            }
            temp = temp->next;
        }
    } while(swapped);
}

Element *merge(Element *head1, Element *head2, Element *newHead) {
    Element *p = head1;
    Element *q = head2;
    Element *sort = NULL;

    if (p == NULL) {
        return q;
    }
    if (q == NULL) {
        return p;
    }

    if (p != NULL && q != NULL) {
        if (p->data <= q->data) {
            sort = p;
            p = sort->next;
        } else {
            sort = q;
            q = sort->next;
        }
    }

    newHead = sort;

    while (p != NULL && q != NULL) {
        if (sort->next == p->next) {
            sort->next = q;
            sort = q;
            q = sort->next;
        } else {
            sort->next = p;
            sort = p;
            p = sort->next;
        }
    }

    if (p == NULL) {
        sort->next = q;
    }
    if (q == NULL) {
        sort->next = p;
    }

    return newHead;
}

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

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

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

int main() {
    Element *head = NULL;
    Element *head2 = NULL;
    Element *head3 = NULL;

    char t;
    int i;
    char p;
    int j;

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

    while (t != '\n') {
        scanf("%d%c", &i, &t);

        if (i == 0) {
            break;
        }

        head = add_on_end(head, addNew(i));
    }

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

    while (p != '\n') {
        scanf("%d%c", &j, &p);

        if (j == 0) {
            break;
        }

        head2 = add_on_end(head2, addNew(j));
    }

    sortList(head);
    sortList(head2);

    head3 = merge(head, head2, head3);

    printList(head3);

    return 0;
}

我遇到的问题是合并,它打印出这两个排序但未与一个和另一个合并的列表,而不是它的第一个列表和第二个列表之后。

例如:

输入:

2 6 4 8 1 
7 9 2 3 5

输出:

1 2 4 6 8 2 3 5 7 9

应该是:

输入

2 6 4 8 1
7 9 2 3 5

输出

1 2 2 3 4 5 6 7 8 9

【问题讨论】:

  • 指针结构的大多数问题的答案是“拿起笔和纸,非常小心地玩自己的代码。”请报告该活动,即证明您进行了此类分析。
  • 老实说,我做过很多次。一切看起来都不错。
  • 然后做一些你认为列表指针在执行程序时如何发展的方框和箭头 ascii 艺术。也许使用较小的输入。
  • 我刚刚编辑了我的帖子,希望它会更容易理解。
  • 使用指向指针的指针通常是一种解决方案。 [而merge() 函数不需要第三个参数]

标签: c merge linked-list


【解决方案1】:

merge 函数太复杂了:

  • 测试if (p != NULL &amp;&amp; q != NULL) 是多余的
  • while 循环中,您不测试节点值,这解释了merge 失败的原因。
  • 第三个参数newHead 没用。

这是修改后的版本:

Element *merge(Element *p, Element *q) {
    Element *newHead = NULL;
    Element *sort = NULL;

    if (p == NULL) {
        return q;
    }
    if (q == NULL) {
        return p;
    }

    if (p->data <= q->data) {
        newHead = sort = p;
        p = p->next;
    } else {
        newHead = sort = q;
        q = q->next;
    }

    while (p != NULL && q != NULL) {
        if (p->data <= q->data) {
            sort = sort->next = p;
            p = p->next;
        } else {
            sort = sort->next = q;
            q = q->next;
        }
    }
    if (p == NULL) {
        sort->next = q;
    }
    if (q == NULL) {
        sort->next = p;
    }
    return newHead;
}

【讨论】:

    【解决方案2】:

    OP 代码中的大部分添加复杂性是由于需要处理边缘情况(查找新头的地址)有时可以避免边缘情况。

    使用指针的简单解决方案:


    struct thing{
            struct thing *next;
            int value;
            };
    
    struct thing *merge(struct thing *one, struct thing *two)
    {
    struct thing *result;
    struct thing **pp; // will always point to the _pointer_ that will be assigned the next node.
    
    result=NULL;
    for(pp = &result; one && two; pp = &(*pp)->next) {
            if(one->value <= two->value) {
                    *pp = one; one = one->next;
                    }
            else    {
                    *pp = two; two = two->next;
                    }
            }
    
            // When we get here,  one and/or two will be NULL
    *pp = (one) ? one : two;
    
    return result;
    }
    

    相同的逻辑,但有一个额外的解引用而不是指针:


    struct thing *merge2(struct thing *one, struct thing *two)
    {
    struct thing dummy;
    struct thing *last;
    
    dummy.next=NULL;
    for(last = &dummy; one && two; last = last->next) {
            if(one->value <= two->value) {
                    last->next = one; one = one->next;
                    }
            else    {
                    last->next = two; two = two->next;
                    }
            }
    
    last->next = (one) ? one : two;
    
    return dummy.next;
    }
    

    现在,有趣的事实是,cc -Wall -omit-frame-pointer -O3 -S llist23.c 为这两个函数生成完全相同的代码。

    【讨论】:

    • 感谢您的回答,我唯一的问题是我们的课程还没有涵盖双指针,所以我的老师不会接受这个答案。这就是我一直试图在没有他们的情况下这样做的原因。
    • 这里只是为了展示**pp可以得到的相对简单性:程序不需要记住where赋值。 (但是:您可以将其枚举到一个单独的变量中)
    猜你喜欢
    • 1970-01-01
    • 2021-01-01
    • 2021-02-27
    • 1970-01-01
    • 1970-01-01
    • 2017-09-23
    • 1970-01-01
    • 2021-12-03
    相关资源
    最近更新 更多