合并两个已排序的单链表的方法

转自 http://www.geeksforgeeks.org/archives/3622

前两种方法时间和空间上最好, 但个人觉得第三种递归的方法比较好理解


Write a SortedMerge() function that takes two lists, each of which is sorted in increasing order, and merges the two together into one list which is in increasing order. SortedMerge() should return the new list. The new list should be made by splicing
together the nodes of the first two lists.

For example if the first linked list a is 5->10->15 and the other linked list b is 2->3->20, then SortedMerge() should return a pointer to the head node of the merged list 2->3->5->10->15->20.

There are many cases to deal with: either ‘a’ or ‘b’ may be empty, during processing either ‘a’ or ‘b’ may run out first, and finally there’s the problem of starting the result list empty, and building it up while going through ‘a’ and ‘b’.

Method 1 (Using Dummy Nodes)
The strategy here uses a temporary dummy node as the start of the result list. The pointer Tail always points to the last node in the result list, so appending new nodes is easy.
The dummy node gives tail something to point to initially when the result list is empty. This dummy node is efficient, since it is only temporary, and it is allocated in the stack. The loop proceeds, removing one node from either ‘a’ or ‘b’, and adding it to tail. When
we are done, the result is in dummy.next.

 

/*Program to alternatively split a linked list into two halves */
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
 
/* Link list node */
structnode
{
    intdata;
    structnode* next;
};
 
 
/* Takes two lists sorted in increasing order, and splices their nodes together to make one big sorted list which is returned.  */
structnode* SortedMerge(structnode* a, structnode* b)
{
   /* a dummy first node to hang the result on */
//dummy 节点作为入口,指向第一个节点
   structnode dummy;     
 
   /* tail points to the last result node  */
   structnode* tail = &dummy; 
 
   /* so tail->next is the place to add new nodes
     to the result. */
   dummy.next = NULL;
   while(1)
   {
      if(a == NULL)
      {
         /* if either list runs out, use the other list */
         tail->next = b;
         break;
      }
      elseif (b == NULL)
      {
         tail->next = a;
         break;
      }
      if(a->data <= b->data)
      {
         MoveNode(&(tail->next), &a);
      }
      else
     {
        MoveNode(&(tail->next), &b);
     }
     tail = tail->next;
  }
  return(dummy.next);
} 
 
//这个函数是关键,把源单链表的头结点切下,加入到新单链表的开头
/* UTILITY FUNCTIONS */
/*MoveNode() function takes the node from the front of the source, and move it to the front of the dest.
   It is an error to call this with the source list empty.
 
   Before calling MoveNode():
   source == {1, 2, 3}
   dest == {1, 2, 3}
 
   Affter calling MoveNode():
   source == {2, 3}
   dest == {1, 1, 2, 3}
*/
voidMoveNode(structnode** destRef, structnode** sourceRef)
{
  /* the front source node  */
//newNode 指向第一个节点,*sourceRef的值即为源单链表的第一个节点的地址
  structnode* newNode = *sourceRef;
  assert(newNode != NULL);
 
  /* Advance the source pointer */
  *sourceRef = newNode->next;
 
  /* Link the old dest off the new node */
  newNode->next = *destRef;
 
  /* Move dest to point to the new node */
  *destRef = newNode;
}
 
/* Function to insert a node at the beginging of the linked list */
voidpush(struct node** head_ref, int new_data)
{
  /* allocate node */
  structnode* new_node =
            (structnode*) malloc(sizeof(structnode));
 
  /* put in the data  */
  new_node->data  = new_data;
 
  /* link the old list off the new node */
  new_node->next = (*head_ref);    
 
  /* move the head to point to the new node */
  (*head_ref)    = new_node;
}
 
/* Function to print nodes in a given linked list */
voidprintList(structnode *node)
{
  while(node!=NULL)
  {
   printf("%d ", node->data);
   node = node->next;
  }
}
 
/* Drier program to test above functions*/
intmain()
{
  /* Start with the empty list */
  structnode* res = NULL;
  structnode* a = NULL;
  structnode* b = NULL; 
 
  /* Let us create two sorted linked lists to test the functions
   Created lists shall be a: 5->10->15,  b: 2->3->20 */
  push(&a, 15);
  push(&a, 10);
  push(&a, 5);
 
  push(&b, 20);
  push(&b, 3);
  push(&b, 2); 
 
  /* Remove duplicates from linked list */
  res = SortedMerge(a, b);
 
  printf("\n Merged Linked List is: \n");
  printList(res);           
 
  getchar();
  return0;
}



Method 2 (Using Local References)
This solution is structurally very similar to the above, but it avoids using a dummy node. Instead, it maintains a struct node** pointer, lastPtrRef, that always points to the last pointer of the result list. This solves the same case that the dummy node did — dealing with the result list when it is empty. If you are trying to build up a list at its tail, either the dummy node or the struct node** “reference” strategy can be used (see Section 1 for details).

structnode* SortedMerge(structnode* a, structnode* b)
{
  structnode* result = NULL;
 
  /* point to the last result pointer */
  structnode** lastPtrRef = &result;
 
  while(1)
  {
    if(a == NULL)
    {
      *lastPtrRef = b;
       break;
    }
    elseif (b==NULL)
    {
       *lastPtrRef = a;
       break;
    }
    if(a->data <= b->data)
    {
      MoveNode(lastPtrRef, &a);
    }
    else
    {
      MoveNode(lastPtrRef, &b);
    }
 
    /* tricky: advance to point to the next ".next" field */
    lastPtrRef = &((*lastPtrRef)->next);
  }
  return(result);
}




Method 3 (Using Recursion)
Merge is one of those nice recursive problems where the recursive solution code is much cleaner than the iterative code. You probably wouldn’t want to use the recursive version for production code however, because it will use stack space which is proportional to the length of the lists.

structnode* SortedMerge(structnode* a, structnode* b)
{
  structnode* result = NULL;
 
  /* Base cases */
  if(a == NULL)
     return(b);
  elseif (b==NULL)
     return(a);
 
  /* Pick either a or b, and recur */
  if(a->data <= b->data)
  {
     result = a;
     result->next = SortedMerge(a->next, b);
  }
  else
  {
     result = b;
     result->next = SortedMerge(a, b->next);
  }
  return(result);
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用合并两个排序链表的方法来将两个递增的单链表合并成一个递增的单链表。首先分别定义两个指针来指向两个链表的头结点,然后比较指针指向的节点值,将值较小的节点添加到新链表中,并将指针指向下一个节点,直到两个指针均到达链表尾部。 ### 回答2: 算法步骤如下: 1. 首先,创建一个新的单链表,用来存放合并后的链表。 2. 定义两个指针p和q分别指向两个递增单链表的头节点。 3. 比较p和q指针所指节点的值,将较小值的节点添加到新的单链表中,并将指针向后移动一位。 4. 当其中一个链表遍历结束时,将另一个链表剩余的节点添加到新的单链表中。 5. 返回新的单链表即为合并后的递增单链表。 具体实现如下所示: ```python # 定义节点类 class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def mergeTwoLists(l1, l2): # 创建一个dummy节点作为新链表的头节点 dummy = ListNode(0) p = l1 q = l2 current = dummy # 循环比较两个链表的节点值,并将较小值的节点添加到新链表中 while p and q: if p.val < q.val: current.next = p p = p.next else: current.next = q q = q.next current = current.next # 将剩余的节点添加到新链表中 if p: current.next = p if q: current.next = q # 返回新链表中去掉dummy节点后的头节点 return dummy.next ``` 时间复杂度:合并两个链表需要遍历所有节点,时间复杂度为O(m + n),其中m和n分别为两个链表的长度。 空间复杂度:只使用了常数级别的额外空间,空间复杂度为O(1)。 ### 回答3: 将两个递增的单链表合并成一个递增的单链表方法是,首先定义一个新的单链表,然后分别定义两个指针,分别指向两个原始链表的头结点。通过比较两个指针所指节点的值,将值较小的节点加入到新的链表中,并将指针指向下一个节点。重复这个过程,直到其中一个链表为空。然后将另一个非空链表剩余的节点直接添加到新的链表的末尾。 具体实现如下: 1. 创建一个新链表,定义一个头指针和一个当前指针,将头指针指向新链表的头节点。 2. 分别定义两个指针p和q,p指向第一个链表的头结点,q指向第二个链表的头结点。 3. 循环比较p和q指向的节点的值,将值较小的节点接入新链表,并将指针后移。 4. 当p或q其中一个指针为空时,停止循环。 5. 若p非空,则将p后面的节点全部接入新链表。 6. 若q非空,则将q后面的节点全部接入新链表。 7. 返回新链表的头节点。 代码示例: ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def merge_two_lists(l1: ListNode, l2: ListNode) -> ListNode: dummy = ListNode(0) # 创建一个哑结点作为新链表的头节点 cur = dummy # 当前结点指针 while l1 and l2: if l1.val <= l2.val: cur.next = l1 l1 = l1.next else: cur.next = l2 l2 = l2.next cur = cur.next if l1: cur.next = l1 if l2: cur.next = l2 return dummy.next ``` 以上代码通过比较两个链表的节点值大小,不断将较小的值的节点添加到新链表中,直到其中一个链表为空。然后直接将另一个链表剩余的部分添加到新链表的末尾。最后返回新链表的头节点,即为合并后的递增单链表

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值