【问题标题】:Merging two Sorted Circular Singly Linked Lists合并两个有序循环单链表
【发布时间】:2015-04-28 01:17:54
【问题描述】:

我在 C 语言课程考试前练习了一些算法问题,但我在这个不知道如何回答的问题上卡住了(至少 3 甚至 4 个小时):

你有两个已经排序的 circular 单链表,你必须合并它们并返回新 circular 链表的头部而不创建任何新的额外节点.返回的列表也应该排序。

节点结构为:

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

我尝试了很多方法(递归和非递归),但都没有解决问题。

感谢您的帮助。

【问题讨论】:

  • 你熟悉归并排序吗?这基本上是合并步骤,可以在链表中就地完成。
  • 您可以在最后一个节点断开两个列表(您知道它是最后一个,因为下一个节点的值较小)现在问题简化为合并到链表,这很容易做到。在它们再次合并后,通过指向第一个元素旁边的最后一个使其循环
  • 你应该展示你认为最好的尝试。没有真正需要递归算法,但如果您愿意,它当然可以递归地编写。是什么让你跌倒?是循环链表吗?
  • @sasha 对于所有元素都具有相同值的链表(1->1->1->1->...->1 是有效的排序链表),此逻辑将失败。您需要标记头部并在到达时结束合并。
  • @Saita as amit 说如果所有元素都相同,请注意这种情况。您可以通过选择任何元素作为 head 来做到这一点,记下它的值并遍历列表,直到遇到不同的值或再次到达头部。如果你再次在头部结束,所有的值都是一样的。

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


【解决方案1】:

这合并了两个循环链表。该代码假定两个列表都已排序并开始一个正确的点。

#include <stdio.h>

struct llist {
        struct llist *next;
        int value;
        };

struct llist wheel[] =
{{ wheel+1, 0 }
,{ wheel+2, 2 }
,{ wheel+3, 4 }
,{ wheel+0, 6 }
};
struct llist cycle[] =
{{ cycle+1, 0 }
,{ cycle+2, 3 }
,{ cycle+3, 6 }
,{ cycle+0, 9 }
};

struct llist *mergelists(struct llist *one, struct llist *two)
{
struct llist *einz, *zwei;
struct llist *result ,**pp;

if (!one) return two;
if (!two) return one;

result = NULL; pp= &result;

for (einz=one, zwei=two; einz || zwei; pp = &(*pp)->next ) {
        if ( !zwei || einz && einz->value <= zwei->value) {
                *pp = einz; einz = einz->next == one ? NULL : einz->next;
                }
        else    {
                *pp = zwei; zwei = zwei->next == two ? NULL : zwei->next;
                }
        }
*pp = result; /* close the loop */
return result;
}

int main(void)
{
struct llist *p;

        /* This loops.  forever ... */
for(p = mergelists( wheel, cycle); p ; p = p->next) {
        printf("%p = %d\n", (void*) p, p->value );
        }
return 0;
}

【讨论】:

    【解决方案2】:

    合并两个已排序列表的示例代码。要检查循环列表的结束节点,请检查指向列表头节点的指针而不是 NULL,并且在到达一个列表的末尾后,您需要从另一个列表中一次链接一个剩余节点,因为终止符不为 NULL。合并后,您需要将最后一个节点的 next 指针设置为头节点,使其成为循环列表。代码检查 Src2

    NODE * MergeLists(NODE *pSrc1, NODE *pSrc2)
    {
    NODE *pDst = NULL;                      /* destination head ptr */
    NODE **ppDst = &pDst;                   /* ptr to head or prev->next */
        while(1){
            if(pSrc1 == NULL){              /* if end of Src1 */
                *ppDst = pSrc2;             /*   append remainder of Src2 */
                break;                      /*   and break out of loop */
            }
            if(pSrc2 == NULL){              /* if end of Src2 */
                *ppDst = pSrc1;             /*   append remainder of Src1 */
                break;                      /*   and break out of loop */
            }
            if(pSrc2->data < pSrc1->data){  /* if Src2 < Src1 */
                *ppDst = pSrc2;             /*   append node from Src2 */
                pSrc2 = *(ppDst = &(pSrc2->next));
                continue;
            } else {                        /* else Src1 <= Src2 */
                *ppDst = pSrc1;             /*   append node from Src1 */
                pSrc1 = *(ppDst = &(pSrc1->next));
                continue;
            }
        }
        return pDst;
    }
    

    【讨论】:

      【解决方案3】:

      首先维护一个有两个链表的队列。下面是它的伪代码:-

       queue* merge_queues (queue* first, queue* second)
             {
      
                 queue* merged_queue = create_queue(first->capacity + second->capacity);
                 if (first != NULL && second != NULL){
                    while ( !is_empty (first) && !is_empty (second)){
                 int max;
                 if ( peekqueue (first) > peekqueue (second)){
                     max = peekqueue (first);
                     dequeue (first);
                 }
                 else{
                      max = peekqueue (second);
                      dequeue (second);
                 }
                 enqueue( merged_queue, max);
              }
               while ( !is_empty (first)){
                    enqueue( merged_queue, peekqueue(first));
                    dequeue (first);
               }
               while (!is_empty (second)){
                   enqueue (merged_queue, peekqueue(second));
                   dequeue (second);
              }
          }
          return merged_queue;
      }
      

      【讨论】:

      • OP希望它就地合并,而不是创建一个新列表(即使你从相应的列表中删除,释放内存并添加新列表,我认为即使这样也没有,虽然简而言之没有使用额外的内存)
      【解决方案4】:

      这基本上是来自merge sort 的合并步骤。

      在链表中,可以就地完成。

      思路是对每个list都有一个迭代器,直到合并list中的数据用完,比较list1中的node和list2中的node,如果list2_iterator

      通过维护一个额外的prev 迭代器在当前节点之前插入一个节点。

      请注意,在此算法的整个过程中 - 没有创建一个新节点,您所做的只是将节点从list2“移动”到list1

      如果此过程为O(n),则复杂性。

      【讨论】:

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