【问题标题】:Understanding an algorithm for merging two sorted linked-list了解合并两个排序链表的算法
【发布时间】:2015-08-09 13:38:57
【问题描述】:

我试图理解以下将两个排序的链接 lsits 合并为一个排序的链接列表的算法(取自 here)。

我不完全理解这段代码是如何工作的,很高兴得到解释。

代码:

#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;
}

【问题讨论】:

  • 你为什么不用调试器单步调试代码呢?这应该有助于理解。
  • 创建两个小列表,在调试器中逐行遍历合并代码,观察指针和它们指向的节点,将它们全部写在纸上。
  • 我试过了(list1=1,5 and list2 = 3,7,9)但是很​​困惑:/
  • 你需要改变路线。您不愿意为成为一名软件开发人员付出足够的努力。
  • 我投票结束这个问题,因为“请解释我所有的代码”。

标签: c algorithm sorting


【解决方案1】:

一开始有针对 NULL 参数的标准测试,这对应于一个列表为空白,而另一个列表默认排序的基本情况。

作者在这里做了一个技巧,每次下一项都会从指向名为 list1 的节点的指针中添加,这发生在以下行中:*pnext = list1;,剩下的就是了解如何总是从一个添加列表,这是使用 SWAP_PTRS 宏来完成的,该宏在 list1 处植入具有较低值的列表开始。

更新完成后,使用pnext = &list1->next; 找到下一个应该更新的节点。它工作的原因也很有趣,因为当前更新的节点已经指向 list1 的当前项,其下一个字段中的值是指向下一个应该更新的位置的指针,通过传递这个指针的地址(使用 & ) 我们可以更改下一项的内容。

这个过程一直持续到添加的项目是列表中的最后一个。这里有趣的一点是没有边缘条件,因为删除的项目总是来自 list1,这意味着 list2 在 list1 之前不能为空。当 list1 的最后一项被添加时,while 循环终止,list2 的其余部分被添加到结果列表的末尾。

【讨论】:

  • 谢谢你..它真的是令人困惑和辉煌的同时!
  • @eggbaz np。是的,它有一些巧妙的技巧。我建议您仔细查看并确保您了解所有这些以及它们的确切原因和方式
【解决方案2】:

算法如下:

如果list2为空,表示只有一个列表,那么算法会返回list1,也就是合并后的列表。如果list1 为空,则应进行相同的测试...

接下来,我们一起循环遍历这两个列表,直到到达list1list2 的末尾。从每个列表的第一个项目开始,我们比较项目并将较小的项目添加到合并列表中,并在相应的列表中前进。在下一次迭代中,将比较一个列表中的第一项与另一个列表中的第二项,依此类推,直到我们到达其中一个列表的末尾。

之后,我们只需将列表中未到达其末尾的项目添加到合并列表中

【讨论】:

  • “并将较小的一个添加到合并列表”发生在哪里?
  • 你为什么说“应该做同样的测试”——已经完成了:看list1 != NULL@eggbaz *pnext = list1;“加小一个到合并列表"
  • 因为如果list1 == NULL,它没有到达那一行
【解决方案3】:

这个没有交换的例子可能更容易理解。

Node * MergeLists(Node *pSrc1, Node *pSrc2)
{
Node *pDst = NULL;                      /* destination head ptr */
Node **ppDst = &pDst;                   /* ptr to head or prev->next */
    if(pSrc1 == NULL)
        return pSrc2;
    if(pSrc2 == NULL)
        return pSrc1;
    while(1){
        if(pSrc2->data < pSrc1->data){  /* if src2 < src1 */
            *ppDst = pSrc2;
            pSrc2 = *(ppDst = &(pSrc2->next));
            if(pSrc2 == NULL){
                *ppDst = pSrc1;
                break;
            }
        } else {                        /* src1 <= src2 */
            *ppDst = pSrc1;
            pSrc1 = *(ppDst = &(pSrc1->next));
            if(pSrc1 == NULL){
                *ppDst = pSrc2;
                break;
            }
        }
    }
    return pDst;
}

此线程中显示的任何一个 MergeList 函数都可以用于此合并排序函数,该函数使用指向节点的数组对列表进行排序:

#define NUMLISTS 32                     /* size of array */
Node * SortList(Node *pList)
{
Node * aList[NUMLISTS];                 /* array of lists */
Node * pNode;
Node * pNext;
int i;
    if(pList == NULL)                   /* check for empty list */
        return NULL;
    for(i = 0; i < NUMLISTS; i++)       /* zero array */
        aList[i] = NULL;
    pNode = pList;                      /* merge Nodes into aList[] */
    while(pNode != NULL){
        pNext = pNode->next;
        pNode->next = NULL;
        for(i = 0; (i < NUMLISTS) && (aList[i] != NULL); i++){
            pNode = MergeLists(aList[i], pNode);
            aList[i] = NULL;
        }
        if(i == NUMLISTS)
            i--;
        aList[i] = pNode;
        pNode = pNext;
    }
    pNode = NULL;                       /* merge array into one list */
    for(i = 0; i < NUMLISTS; i++)
        pNode = MergeLists(aList[i], pNode);
    return pNode;
}

【讨论】:

    猜你喜欢
    • 2011-01-21
    • 2017-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多