【问题标题】:Merging two sorted linked lists合并两个排序的链表
【发布时间】:2011-01-21 20:18:35
【问题描述】:

这是 Microsoft 在笔试期间提出的编程问题之一。我正在给出我想出的问题和答案。事情是我的回答虽然看起来很全面(至少对我来说),但我觉得可以减少行数。它是用 C 语言问的,我是 Java 人,但我设法编写了它(我的答案可能包含太多类似 Java 的语法)

好的,问题来了。

您已经有两个列表 排序后,您必须合并它们并 返回一个没有任何新额外内容的新列表 节点。返回的列表应该是 排序为好。

方法签名是,

Node* MergeLists(Node* list1, Node* list2);

struct Node{
    int data;
    Node *next;
}

以下是我想出的解决方案,

Node* MergeLists(Node* list1, Node* list2){
    Node* mergedList;
    if(list1 == null && list2 ==null){//if both are null, return null
        return null;
    }
    if(list1 == null){//if list1 is null, simply return list2
        return list2;
    }
    if(list2 == null){//if list2 is null, simply return list1
        return list1;
    }
    if(list1.data < list2.data){//initialize mergedList pointer to list1 if list1's data is lesser
        mergedList = list1;
    }else{//initialize mergedList pointer to list2 if list2's data is lesser or equal
        mergedList = list2;
    }
    while(list1!=null && list2!=null){
        if(list1.data < list2.data){
            mergedList->next = list1;
            list1 = list1->next;
        }else{
            mergedList->next = list2;
            list2 = list2->next;
        }
    }
    if(list1 == null){//remaining nodes of list2 appended to mergedList when list1 has reached its end.
        mergedList->next = list2;
    }else{//remaining nodes of list1 appended to mergedList when list2 has reached its end
        mergedList->next = list1;
    }
    return mergedList;
}

我非常有信心这可以得到加强。请帮我找出我添加的多余行。请随时批评我的语法错误和逻辑。

谢谢!

【问题讨论】:

  • 您的一些代码可以简单地通过在开始时使用三元运算符来缩短。 I.E 使用 to mergeList = (list1 == null ? list2 : null) 重写参数测试并保存代码行,尽管“不是”操作。
  • Bragboy,您介意将您的标题更改为更具描述性的形式,例如“合并两个排序列表”吗?您当前的标题(需要您对此编码问题的建议/提示)是我们在这里的全部目的。 :) 它不会识别或宣传您的问题。

标签: c algorithm data-structures linked-list


【解决方案1】:

最明显的错误是在你的循环中,你不断覆盖mergedList->next,丢失了之前添加的节点。也就是说,无论输入如何,您返回的列表都不会包含两个以上的节点...

自从我做 C 以来已经有一段时间了,但我会这样做:

Node* merge(Node* list1, Node* list2) {
    Node* merged = null;
    Node** tail = &merged;

    while (list1 && list2) {
        if (list1->data < list2->data) {
            *tail = list1;
            list1 = list1->next;
        } else {
            *tail = list2;
            list2 = list2->next;
        }
        tail = &((*tail)->next);
    }
    *tail = list1 ? list1 : list2;
    return merged;
}

【讨论】:

  • 男人!那是一个大洞!我真的没有注意到。
【解决方案2】:

为了处理“特殊”情况而插入的if-s 使您的代码超载,这使代码变得非常臃肿并且难以阅读。这通常发生在您决定“通过代码”处理特殊情况而不是找到“通过数据”处理它们的方法时。 David Wheeler 的一份声明说:“计算机科学中的所有问题都可以通过另一个层次的间接性来解决”。这种“额外的间接性”通常与列表配合得很好,有助于显着减少ifs 所造成的混乱。

为了说明上述情况,这是我的代码的样子

#define SWAP_PTRS(a, b) do { void *t = (a); (a) = (b); (b) = t; } while (0)

Node* MergeLists(Node* list1, Node* list2) 
{
  Node *list = NULL, **pnext = &list;

  if (list2 == NULL)
    return list1;

  while (list1 != NULL)
  {
    if (list1->data > list2->data)
      SWAP_PTRS(list1, list2);

    *pnext = list1;
    pnext = &list1->next;
    list1 = *pnext;
  }

  *pnext = list2;
  return list;
}

有些人可能会争辩说,在pnext 指针中使用额外的间接级别实际上会使代码更难阅读。我同意这种方法对于新手来说可能会带来一些困难,但对于有经验的程序员来说,这应该很容易理解为一个习惯用法。

【讨论】:

  • 谢谢。将来在攻击这种性质的问题时会考虑这种方法。这真的很有帮助!
  • null 传递给list2 时,这不会尝试取消对null 的引用吗?
  • @meriton:是的,会的。我加了一张额外的支票 :) 谢谢。
  • +1 用于解释间接和简化 while 条件以仅检查修改后的指针。
  • @polygenelubricants:一个错字。目的是在list2 为空时返回list1(而不是list)。固定。
【解决方案3】:

我的看法,有一个测试用例

到目前为止,所有答案都很有趣并且做得很好。这可能更像是面试官希望看到的,具有 DRY/DIE 和 TDD。 :-)

#include <stdio.h>

typedef struct ns {
    int data;
    struct ns *next;
} Node;

Node l1[] = {
  { 1, &l1[1] },
  { 3, &l1[2] },
  { 5, &l1[3] },
  { 7, &l1[4] },
  { 9, &l1[5] },
  {11, 0 },
};

Node l2[] = {
  { 2, &l2[1] },
  { 4, &l2[2] },
  { 6, 0 },
};

Node* MergeLists(Node* list1, Node* list2) {
  Node *t, **xt;
  for(xt = &t; list1 || list2;) {
    Node **z = list1 == NULL ? &list2 :
               list2 == NULL ? &list1 :
               list1->data < list2->data ? &list1 : &list2;
    *xt = *z;
     xt = &(*z)->next;
    *z  = *xt;
  }
  *xt = NULL;
  return t;
}

int main(void) {
  for(Node *t = MergeLists(l1, l2); t; t = t->next) 
    printf("%d\n", t->data);
  return 0;
}

【讨论】:

  • 这是一种巧妙的链表构造方法。喜欢它。
【解决方案4】:

没有比这更优雅的了:

Node* merge2(Node* n1, Node* n2) {
    n1->next = merge(n1->next, n2);
    return n1;
}

Node* merge(Node* n1, Node* n2) {
    return (n1 == null) ? n2 :
           (n2 == null) ? n1 :
           (n1->data < n2->data) ?
               merge2(n1, n2) :
               merge2(n2, n1);
}

假设您了解递归,这已经很清楚了。


我应该指出,这仅适用于面试答案(据推测,展示思路清晰比简单地展示你知道如何编写程序更有效)。实际上,您不希望以这种方式合并,因为它使用O(n) 堆栈深度,这可能会导致堆栈溢出。此外,它不是尾递归,因此不是编译器可优化的。

【讨论】:

  • 嗯,它很简洁,但我不会称嵌套三元组为“清晰”。
  • 嗯,这需要习惯,但除非你滥用它,否则嵌套三元组实际上至少对我来说是一个非常清晰的代码。它需要良好的逻辑和良好的格式、一些括号和一些缩进,但仅此而已。
  • 它在某种意义上是“优雅的”,它使用递归。但从功能的角度来看,在直接循环的情况下使用递归并不是功能优雅的一个例子。任何循环都可以用递归代替。但它应该是吗?
  • 如果我是你,我会拒绝创建一个额外的函数 :) 你可以使用, 运算符轻松地用一个函数完成它。最后一个分支看起来就像n1-&gt;data &lt; n2-&gt;data ? n1-&gt;next = merge(n1-&gt;next, n2), n1 : n2-&gt;next = merge(n2-&gt;next, n1), n2。但这开始有点混淆 C 代码竞赛的味道 :)
  • 是的,您的解决方案和我的解决方案之间的对比是您的解决方案(在修复了所有错误之后)表明作者知道如何编写正确的代码。我的表明作者知道如何清晰地思考。
【解决方案5】:

分而治之

(即MergeSort

【讨论】:

  • 这正是正确的答案,为什么不赞成? @Luca:+1 来自我。
  • @tzaman:这个问题的相关答案到底如何?
  • @Bragaadeesh:你需要排序吗?我想知道为什么不使用众所周知的算法。当然,您可以使用 Divide & Impera 模式获得最佳性能,而不是简单地遍历两个列表。这只是对算法模式的批评,在我看来确实很中肯。
  • 我也投了你一票。这正是 mergesort 的合并部分。
  • 这是关于合并两个排序列表。归并排序的第二部分是合并两个排序列表。这很简单。
【解决方案6】:

所以将 polygen 与 AndreyT 合并,我们得到:

Node* merge(Node* n1, Node* n2) {
    return (n1 == null) ? n2 :
           (n2 == null) ? n1 :
             (n1->data < n2->data) ? 
               (n1->next = merge(n1->next, n2), n1) : 
               (n2->next = merge(n2->next, n1), n2)}

我不能说这个功劳,但它是最简洁的,并且显示了两个参数之间的对称性,没有引入任何晦涩的辅助函数。我不确定优化编译器会在这里看到尾递归,但我会。缩进是最后的润色。

【讨论】:

    【解决方案7】:

    使用递归。代码如下:

    ListNode* mergeTwoSortedLists(ListNode* pHead1, ListNode* pHead2)
    {
        if(pHead1 == NULL)
            return pHead2;
        else if(pHead2 == NULL)
            return pHead1;
    
        ListNode* pMergedHead = NULL;
    
        if(pHead1->m_nValue < pHead2->m_nValue)
        {
            pMergedHead = pHead1;
            pMergedHead->m_pNext = mergeTwoSortedLists(pHead1->m_pNext, pHead2);
        }
        else
        {
            pMergedHead = pHead2;
            pMergedHead->m_pNext = mergeTwoSortedLists(pHead1, pHead2->m_pNext);
        }
    
        return pMergedHead;
    }
    

    【讨论】:

      【解决方案8】:
      public void Merge(LinkList list1, LinkList list2) {
              if (list1.head == null && list2.head == null) {
                  System.out.println("Empty list"); //Check if lists are empty
              }
              if (list1.head == null) { //If list1 is empty print list2
                  list2.printList();
              }
              if (list2.head == null) { //If list2 is empty print list1
                  list1.printList(); 
              }
              LinkList list3 = new LinkList(); //create a new LinkList object for merging
              Node a = list1.head; //Beginning of list1
              Node b = list2.head; //Beginning of list2
              while (a != null && b != null) { //Until list ends
                  if (a.value <= b.value) { //Compare values of list1 against list2
                      list3.insert(a.value); //Insert values to new list
                      a = a.next;
                  } else if (a.value >= b.value) {
                      list3.insert(b.value);
                      b = b.next;
                  }  else if (a.value == b.value){ //Insert only unique values and discard repeated values
                  list3.insert(a.value);
                  a = a.next;
                  b = b.next;
              }
              }
              if (a == null) {
                  while (b != null) {
                      list3.insert(b.value); //If list1 becomes empty, attach rest of the list2 to list3
                      b = b.next;
                  }
              }
              if (b == null) {
                  while (a != null) {
                      list3.insert(a.value);
                      a = a.next;
                  }
              }
              list3.printList(); //print merged list
          }
      }
      

      大家好!这个月我正在准备面试,当我在解决这个问题时,这就是我想出的解决方案。我将我的解决方案与您在此处发布的许多解决方案进行了比较,发现我的程序非常冗长。尽管我发现这更容易理解和实现,但在 Java 中是否有针对现有代码的更好解决方案。我正在寻找更好的时间复杂度解决方案。任何帮助/方向/提示表示赞赏。

      PS:我无法为上面列出的 C 中使用指针的程序提供 Java 解决方案。

      【讨论】:

        【解决方案9】:

        这是我的看法。与其他解决方案不同,它识别并跳过一个列表上小于或等于另一个列表的头节点的连续节点。另一个列表的头部附加在该序列的末尾,并且在切换角色后重复该过程。 这种方法最大限度地减少了对 Node.next 的分配数量,同时将 NULL 测试限制为每次迭代中的单次检查。

        Node * merge(Node *list1, Node *list2)
        {
            if (!list1) return list2;
            if (!list2) return list1;
        
            Node *tmp;
        
            // compare head nodes and swap lists to guarantee list1 has the smallest node
            if (list1->val > list2->val) {
                tmp = list2;
                list2 = list1;
                list1 = tmp;
            }
        
            Node *tail = list1;
        
            do {
                // Advance the tail pointer skipping over all the elements in the result
                // which have smaller or equal value than the first node on list2
                while (tail->next && (tail->next->val <= list2->val)) {
                    tail = tail->next;
                }
                // concat list2 at tail of result and make the rest after tail list2 
                tmp = tail->next;
                tail->next = list2;
                tail = list2;
                list2 = tmp;
            } while (list2);
        
            return list1;
        }
        

        【讨论】:

          【解决方案10】:
          #include<stdio.h>
          
          typedef struct NODE
          {
              int data;
              struct NODE * next;
          }NODE;
          
          NODE * func(NODE*,NODE*);
          int main()
          {
              int i;
              int size;
              int value;
              NODE * head1,*head2,*newNode,*ptr,*final;
              printf("\nPlease enter the number of elements\n");
              scanf("%d",&size);
          
              for(i=0;i<size;i++)
              {
                      printf("List 1\n");
                      printf("Please enter the value number %d \n",i+1);
                      scanf("%d",&value);
                      newNode=(NODE*)malloc(sizeof(NODE));
                      newNode->data=value;
                      newNode->next=NULL;
                      if(i!=0)
                      {
                          ptr->next=newNode;  
                          ptr=ptr->next;
                      }
          
                      if(i==0)
                      {
                          head1=newNode;
                          ptr=newNode;
          
                      }
              }
              for(i=0;i<size;i++)
              {
                      printf("\n\nList 2\n");
                      printf("Please enter the value number %d \n",i+1);
                      scanf("%d",&value);
                      newNode=(NODE*)malloc(sizeof(NODE));
                      newNode->data=value;
                      newNode->next=NULL;
                      if(i!=0)
                      {
                          ptr->next=newNode;  
                          ptr=ptr->next;
                      }
          
                      if(i==0)
                      {
                          head2=newNode;
                          ptr=newNode;
          
                      }
              }
          
              final=func(head1,head2);
              printf("\n\n");
              while (final!=NULL)
              {
                  printf("%d -->",final->data);
                  final=final->next;
              }
              printf("NULL
              ");
              return 0;
          }
          
          NODE* func(NODE* list1, NODE* list2)
          {
          
              NODE* mergedList,*mergeHead=NULL;
              if(list1 == NULL && list2 ==NULL){//if both are NULL, return NULL
                  return NULL;
              }
              if(list1 == NULL){//if list1 is NULL, simply return list2
                  return list2;
              }
              if(list2 == NULL){//if list2 is NULL, simply return list1
                  return list1;
              }
              mergedList = (NODE*)malloc(sizeof(NODE));
              if(list1->data < list2->data){//initialize mergedList pointer to list1 if list1's data is lesser
          
                  mergedList->data=list1->data;
                  mergedList->next=NULL;
                  list1 = list1->next;
          
              }else{//initialize mergedList pointer to list2 if list2's data is lesser or equal
                  mergedList->data=list2->data;
                  mergedList->next=NULL;
                  list2 = list2->next;
          
              }
              mergeHead=mergedList;
          
              while(list1!=NULL && list2!=NULL){
                  if(list1->data < list2->data){
                      mergedList->next = (NODE*)malloc(sizeof(NODE));
                      mergedList=mergedList->next;
                      mergedList->data=list1->data;
                      mergedList->next=NULL;
                      list1 = list1->next;
                  }else{
                      mergedList->next = (NODE*)malloc(sizeof(NODE));
                      mergedList=mergedList->next;
                      mergedList->data=list2->data;
                      mergedList->next=NULL;
                      list2 = list2->next;
                  }
              }
              if(list1 == NULL){//remaining nodes of list2 appended to mergedList when list1 has reached its end.
                 while(list2!=NULL)
                 {
                      mergedList->next = (NODE*)malloc(sizeof(NODE));
                      mergedList=mergedList->next;
                      mergedList->data=list2->data;
                      mergedList->next=NULL;
                      list2 = list2->next;
                 }
          
              }else{//remaining nodes of list1 appended to mergedList when list2 has reached its end
                      mergedList->next = (NODE*)malloc(sizeof(NODE));
                      mergedList=mergedList->next;
                      mergedList->data=list1->data;
                      mergedList->next=NULL;
                      list1 = list1->next;
              }
              return mergeHead;
          }
          

          【讨论】:

            【解决方案11】:

            我已经为它创建了递归函数。 这是我的解决方案:

            Node* merge_recursion(Node* l1, Node* l2)
            {
                    if (!l1)
                            return l2;
                    if (!l2)
                            return l1;
            
                    if (l1->data < l2->data) {
                            l1->next = merge_recursion(l1->next, l2);
                            return l1;
                    } else {
                            l2->next = merge_recursion(l1, l2->next);
                            return l2;
                    }
            }
            

            将返回指针存储到新指针变量中(在main()/调用函数中),并在新指针上遍历链表打印数据,得到排序后的合并链表。

            【讨论】:

              【解决方案12】:

              你可以使用递归:

              Node* MergeLists(Node *headA, Node* headB)
              {
              
              if(headA==NULL){
                  return headB;
              }else if(headB ==NULL){
                  return headA;
              }
              Node* head = NULL;
              if(headA->data <= headB->data){
                  head= headA;
                  head->next = MergeLists(headA->next,headB);
              }else{
                  head= headB;
                  head->next = MergeLists(headA,headB->next);
              }
               return head;
              }
              

              【讨论】:

              • … 和 O(n) 内存。
              【解决方案13】:

              您可以使用 Java 8、Stream API 来合并、获取 Distinct 和排序。 下面是使用整数元素对两个列表进行排序和合并的示例代码

              List<Integer> list1= Arrays.asList(2,3,5,8);
              List<Integer> list2 = Arrays.asList(3,6,8);
              
              List<Integer> finalList = new ArrayList<>();
              finalList.addAll(list1);
              finalList.addAll(list2);
              
              List<Integer>  mergeSortedList = 
                finalList.stream()
                  .distinct()
                  .sorted()
                  .collect(Collectors.toList());
              System.out.println(mergeSortedList);
              

              【讨论】:

              • 请评估这种方法的渐近运行时间复杂度。
              • 问题应该在 C 中,但你在 Java 中给出了一个 - 不是那么聪明 - 答案。
              • question was [tagged] C, yet you give [an] answer in Java not a first
              【解决方案14】:

              合并两个有序链表的迭代解决方案:

              class ListNode:
                  def __init__(self, val=0, next=None):
              
                       self.val = val
              
                       self.next = next
              
              
                  class Solution:
                          def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
                              head = ListNode(-200)
                              prev = head
                              while l1 and l2:
                                  if l1.val <=l2.val:
                                      prev.next = l1
                                      l1 = l1.next
                                  else:
                                      prev.next = l2
                                      l2 = l2.next
                                  prev = prev.next
                                  
                              prev.next = l1 if l1 is not None else l2
                              
                              return head.next
                              
              

              【讨论】:

                【解决方案15】:
                //I have used recursions .
                //Sorry for such a long code.
                //:) it works,hope it helps.
                #include<stdio.h>
                #include<malloc.h>
                #include<stdlib.h>
                struct node{
                    int data;
                    struct node *next ;
                };
                struct node *start1=NULL,*start2=NULL;
                struct node*start=NULL;
                struct node *create_ll1(struct node *start);
                struct node *create_ll2(struct node *start);
                void sorted_ll(struct node* node1,struct node* node2);
                
                struct node *display(struct node *start);
                void p(struct node*);
                main(){
                    start1=create_ll1(start1);
                    start2=create_ll2(start2);
                    //start1=display(start1);
                    printf("\n");
                    //start2=display(start2);
                    sorted_ll(start1,start2);
                    //start=display(start);
                
                
                }
                struct node *create_ll1(struct node *start1){
                    struct node *ptr,*new_node;
                    int num;
                    printf("Enter -1 for ending \n");
                    printf("Enter data for list 1: \n");
                    scanf("%d",&num);
                    while(num!=-1){
                        new_node=(struct node *)malloc(sizeof(struct node));
                        new_node->data=num;
                        if(start1==NULL){
                            new_node->next=NULL ;
                            start1=new_node;
                        }
                        else{
                            ptr=start1 ;
                            while(ptr->next!=NULL)
                            ptr=ptr->next;
                            ptr->next=new_node;
                            new_node->next=NULL ;
                        }
                        printf("Enter data: \n");
                        scanf("%d",&num);
                    }
                    return start1;
                
                }
                struct node *create_ll2(struct node *start2){
                    struct node *ptr,*new_node;
                    int num;
                    printf("Enter -1 for ending \n");
                    printf("Enter data for list 2: \n");
                    scanf("%d",&num);
                    while(num!=-1){
                        new_node=(struct node *)malloc(sizeof(struct node));
                        new_node->data=num;
                        if(start2==NULL){
                            new_node->next=NULL ;
                            start2=new_node;
                        }
                        else{
                            ptr=start2 ;
                            while(ptr->next!=NULL)
                            ptr=ptr->next;
                            ptr->next=new_node;
                            new_node->next=NULL ;
                        }
                        printf("Enter data: \n");
                        scanf("%d",&num);
                    }
                    return start2;
                
                }
                
                struct node *display(struct node *start){
                    struct node *ptr;
                    ptr=start;
                    while(ptr->next!=NULL){
                        printf("\t %d",ptr->data);
                        ptr=ptr->next;
                    }
                        printf("\t %d",ptr->data);
                        printf("\n");
                
                
                    return start ;
                }
                void sorted_ll(struct node* node1,struct node* node2)
                {
                    if(!node1){
                        p(node2);
                        exit(0);
                    }
                    else if(!node2){
                        p(node1);
                        exit(0);
                    }
                    if(node1->data<node2->data){
                        printf("%d\t",node1->data);
                        sorted_ll(node1->next,node2);
                
                
                    }
                    else{
                        printf("%d\t",node2->data);
                        sorted_ll(node1,node2->next);
                    }
                }
                void p(struct node* pme){
                    while(pme->next!=NULL){
                        printf("%d \t",pme->data);
                        pme=pme->next;
                    }
                    printf("%d",pme->data);
                
                }
                

                【讨论】:

                • 虽然(在某种程度上)包含所有代码的注释是一个好的开始,但该注释不会脱离这​​个问题的上下文。您的代码的其余部分未注释。一旦你有了一个测试框架并且确信你的代码可以按规定工作,考虑在Code Review上发帖以不辜负你的用户名。
                猜你喜欢
                • 2017-08-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多