创建一个有效的递归函数来对List <template>类</template>进行排序

时间:2014-04-15 17:09:52

标签: c++ sorting recursion merge linked-list

这是关于Stack Overflow的第一个问题所以请保持温和。提前感谢您的任何回复,您似乎真的知道您在机器峰值时所做的事情。

我一直在构建一个Linked List工具包模板,我需要做的最后一个补充是sort()函数和merge()函数。

这是我的节点结构的私有成员变量:

  struct node
        {
            Item data;              // the variable containing the data in the node.
            node* next;             // the pointer to the next node in the list.
            node( Item _data, node* _next = NULL )  // constructor to build a node when data or the link is already known
            {
                data = _data;       
                next = _next;
            }
        };
 node* head;

这是我的简单排序函数,用于测试列表是否按升序排序:

template <class Item>
bool List<Item>::sorted( )
{
    node* cursor;       // allocate a node pointer for traversal

    for ( cursor = head; cursor != NULL; cursor = cursor->next)     // loop from head until NULL
        if ( cursor->data > cursor->next->data )
            return false;       // if we ever find that the data in a node is greater than the data in the next node, return false

    return true;        // if we reach this point, the list is in ascending order
}

如果我能够递归地解决sort()函数,我写了这个函数作为简单基本情况的一种方法,唉我已经能够实现这个目标了。注意:我对迭代解决方案同样满意,就像我使用递归解决方案一样。

现在; merge()函数(请注意,如果我可以进行排序工作,断言将被替换为对sort()的两次调用!)

template <class Item>
    List<Item> List<Item>::merge(List<Item>& list1, List<Item>& list2)
    {          // libraries used: <algorithm> for sort() and <cassert> for assert();

        if ( list1.head == NULL )   // if the first list is empty, just return the second list
            return list2;
        if ( list2.head == NULL )   // if the second list is empty, just return the first list
            return list1;

        assert ( sorted() && otherList.sorted() );    // if this assertion is false, the function will not work.

        List<Item> newList;

        node* cursor1 = head;
        node* cursor2 = otherList.head;

        while ( cursor1 != NULL && cursor2 != NULL )
        {
            if (cursor1 == NULL)
            {
                while (cursor2 != NULL)
                {
                    newList.headInsert(cursor2->data);
                    cursor2 = cursor2->next;
                }
            }
            if (cursor2 == NULL)
            {
                while (cursor1 != NULL)
                {
                    newList.headInsert(cursor1->data);
                    cursor1 = cursor1->next;
                }
            }
            if (cursor1->data < cursor2->data)
            {
                newList.headInsert(cursor1->data);
                cursor1 = cursor1->next;
            }
            if (cursor2->data < cursor1->data)
            {
                newList.headInsert(cursor2->data);
                cursor2 = cursor2->next;
            }
            else
                if (cursor1->data == cursor2->data)
                {
                    newList.headInsert(cursor1->data);
                    newList.headInsert(cursor2->data);
                    cursor1 = cursor1->next;
                    cursor2 = cursor2->next;
                }
        }

        clear();
        head = newList.head;
    }

排序功能是我真正迷失的地方。我在这个网站上看了很多回答的问题,但没有发现任何我真正想要的东西。如果可能的话,我希望有一个声明为这样的函数

void List<Item>::sort( )

如果可能的话,如果有人能向我解释如何实现我的merge()函数,我会更加感激

void List<Item>::merge(List<Item>& L2)

我认为如果我可以将merge作为一个函数实现,而不是将合并的节点分配给一个新的List,它会很棒。它可以很容易地使用另一个List并基本上将它们合并到我的主列表中,就像这样

myList.merge(otherList);

我确实知道有很多类似的问题已经在这个网站上单独回答了,所以请不要只是把我链接到另一个问题,因为我很有机会看到它并空手而归(因为至少这个问题)。问题是,即使在仔细研究了问题和答案后,我仍然无法找到问题的答案。除非我有充分的理由,否则我不想模仿别人的代码。这对我来说更像是一次学习练习,我只是在编写最佳方法时阻止了这一点。我很乐意上传任何其他代码块,或者在我的思考过程中添加更多评论,而且批评非常受欢迎。任何格式提示也将受到赞赏。

此致

一位充满激情的新手编码器

表示rclgdr

 template <class Item>
    void List<Item>::sort( )
    {
        if ( head == NULL || head->next == NULL )   // the list is empty or contains only one node, so it doesn't require sorting
            return;

        node* cursor = head;        // the cursor will be used for traversal
        node* smallest = head;      // this will always point to the node with the lowest data value
        node* was_smallest = head;  // tthis will point to the smallest data if a smaller value is found by the cursor
        node* back = head;          // this is a previous pointer, pointing at the node behind the cursor
        node* temp;                 // I always declare a temporary cursor just in case, it will be returned to heap anyway

        while ( cursor != NULL )        //cursor has originated at the head, and will go until it reaches NULL
        {
            if ( cursor->data < smallest->data )        // compare the data to the current smallest data
            {
                was_smallest = back;        // if the smallest was trumped by the cursor's data, set was_smallest to smallest
                smallest = cursor;          // smallest is where the cursor is pointing
            }
        back = cursor;              // always point back at where the cursor is
        cursor = cursor->next;      // THEN move the cursor, back is pointing at the previous node
        }

        if ( head != smallest )     // if the head is not the smallest (it SHOULD be!)
        {
            was_smallest->next = head;      // link was_smallest to the new head, essentially making it the new head
            temp = head->next;              // first use of temp, point it after the head
            head->next = smallest->next;    // after the head ( not TEMP! ) is linked from the new head (smallest)
            smallest->next = temp;          // link smallest to the head's link
        }

        //  I believe I need a recursive call here I think (head != smallest) is the base case?

    }


       template <class Item>
        void List<Item>::merge(List<Item>& otherList)
        {          // libraries used: <algorithm> for sort() and <cassert> for assert();

            if ( list1.head == NULL )   // if the first list is empty, just return the second list
                return list2;
            if ( list2.head == NULL )   // if the second list is empty, just return the first list
                return list1;

            assert ( sorted() && otherList.sorted() );    // if this assertion is false, the function will not work.

            List<Item> newList;

            node* cursor1 = head;
            node* cursor2 = otherList.head;

            while ( cursor1 != NULL && cursor2 != NULL )
            {
                if (cursor1 == NULL)
                {
                    while (cursor2 != NULL)
                    {
                        newList.headInsert(cursor2->data);
                        cursor2 = cursor2->next;
                    }
                }
                if (cursor2 == NULL)
                {
                    while (cursor1 != NULL)
                    {
                        newList.headInsert(cursor1->data);
                        cursor1 = cursor1->next;
                    }
                }
                if (cursor1->data < cursor2->data)
                {
                    newList.headInsert(cursor1->data);
                    cursor1 = cursor1->next;
                }
                if (cursor2->data < cursor1->data)
                {
                    newList.headInsert(cursor2->data);
                    cursor2 = cursor2->next;
                }
                else
                    if (cursor1->data == cursor2->data)
                    {
                        newList.headInsert(cursor1->data);
                        newList.headInsert(cursor2->data);
                        cursor1 = cursor1->next;
                        cursor2 = cursor2->next;
                    }
            }
            clear()
            head = newList.head;
        }

2 个答案:

答案 0 :(得分:0)

您需要关注的第一件事是您的解决方案的高级描述。看起来你想要进行合并排序,为此步骤是:

  • 将输入分成两个(大致相当于大小的)列表
  • 独立排序
  • 将结果合并在一起

你提到你希望sort成为一个成员函数,所以就这样做:

void List::sort(); // ignoring template arguments here

对于实现,首先需要将列表拆分为两个,而不假设您知道正确的大小,您可以通过创建新列表(本地)并将每个其他节点从此节点移动到另一个节点来分成两半名单。此时,您有两个可以递归排序的列表,然后您可以调用merge

以同样的方式,您可以将merge实现为获取其他列表的成员函数:

void List::merge(List & other);

请注意界面中的一些更改。第一个是如果你想进行就地操作,那么返回列表的副本是没有意义的,你什么也不返回(调用者已经有了对象)或者你可以返回对此列表的引用。

答案 1 :(得分:0)

递归地将列表拆分为子列表以进行排序是低效的。 HP / Microsoft STL std :: list :: sort使用一个指向节点的n个指针数组,其中array [i]是一个大小为2 ^ i的列表(除了数组[n-1]可以更大),加上一个临时指针到一个节点。节点被&#34;合并&#34;一次一个地进入数组。合并一个所有节点,然后合并该数组以创建排序列表。

您还可以使用指向节点的4个指针(2&#34;源&#34;,2&#34;目标&#34; poitners)和子列表大小计数(1)实现更传统的自下而上排序,2,4,8,...,直到&gt; =列表的大小)

古代示例C代码。该数组是静态的,但如果将此代码转换为类,则该数组将是类成员。请注意有关NODE.next和LIST.first的注释,该注释用于简化附加到空列表的操作。一些STL代码使用类似的方法,但您可以添加代码来检查空列表处理,而不需要关于next的假设,并且首先是结构的第一个成员(这意味着它们具有与结构相同的地址)。

#define NUMLISTS 32                     /* number of lists */

typedef unsigned long long UI64;

/*      MergeLists() casts an instance of LIST to NODE      */
/*      and uses NODE.next as the equivalent of LIST.first  */

typedef struct _NODE{
struct _NODE * next;        /* must be first member of NODE */
UI64 data;
}NODE, *PNODE;

typedef struct _LIST{
NODE *first;                /* must be first member of LIST */
}LIST, *PLIST;

/*----------------------------------------------------------------------*/
/*      data                                                            */
/*----------------------------------------------------------------------*/
static LIST aList[NUMLISTS];    /* array of lists */

/*----------------------------------------------------------------------*/
/*      SortList                                                        */
/*----------------------------------------------------------------------*/
void SortList(LIST *pList)
{
NODE * pNode;
LIST mList;                             /* merged list */
int i;

    if(pList == NULL)                   /* check for null ptr */
        return;

    /* merge nodes into aList[] */
    while(NULL != (pNode = GetFirstNode(pList))){
        MergeNode(pNode);
    }

    /* find 1st non empty aList[] */
    for(i = 0; (i < NUMLISTS) && (aList[i].first == NULL); i++);
    if(i >= NUMLISTS){                  /* if all empty */
        pList->first = NULL;            /*   return null list */
        return;
    }

    pList->first = aList[i].first;      /* plist = aList[i] */
    aList[i].first = NULL;              /* clear aList[i] (optional) */

    for(i++; i < NUMLISTS; i++){        /* merge remaining aList[] */
        if(aList[i].first != NULL){
            MergeLists(&mList, &aList[i], pList);
            pList->first = mList.first;
        }
    }
}

/*----------------------------------------------------------------------*/
/*      MergeNode   merge a node into the array of lists                */
/*----------------------------------------------------------------------*/
void MergeNode(NODE *pNode)
{
LIST sList;                             /* source list */
LIST mList;                             /* merged list */
int i;

    if(pNode == NULL)                   /* return if null ptr */
        return;

    sList.first = pNode;                /* src list = node */

    /* merge into array */
    for(i = 0; (i < NUMLISTS) && (aList[i].first != NULL); i++){
        MergeLists(&mList, &aList[i], &sList);
        sList.first = mList.first;
    }
    if(i == NUMLISTS)                   /* update array */
        i--;
    aList[i].first = sList.first;
}

/*----------------------------------------------------------------------*/
/*      MergeLists  dst = merge(src1, src2)                             */
/*----------------------------------------------------------------------*/
void MergeLists(LIST *plDst, LIST *plSrc1, LIST *plSrc2)
{
NODE *pDst, *pSrc1, *pSrc2;             /* node ptrs */

    pDst = (NODE *)plDst;               /* treat list same as node */
    pSrc1 = GetFirstNode(plSrc1);       /* init ptrs to nodes */
    pSrc2 = GetFirstNode(plSrc2);

    while(1){
        if(pSrc2->data < pSrc1->data){  /* if src2 < src1 */
            pDst->next = pSrc2;
            pDst = pDst->next;
            /* if end of src2, dst = rest of src1 */
            if(NULL == (pSrc2 = GetFirstNode(plSrc2))){
                pSrc1->next = plSrc1->first;
                pDst->next = pSrc1;
                plSrc1->first = NULL;
                break;
            }
        } else {                        /* src1 <= src2 */
            pDst->next = pSrc1;
            pDst = pDst->next;
            /* if end of src1, dst = rest of src2 */
            if(NULL == (pSrc1 = GetFirstNode(plSrc1))){
                pSrc2->next = plSrc2->first;
                pDst->next = pSrc2;
                plSrc2->first = NULL;
                break;
            }
        }
    }
}

/*----------------------------------------------------------------------*/
/*      GetFirstNode                                                    */
/*----------------------------------------------------------------------*/
NODE * GetFirstNode(LIST * pSrc)
{
NODE * pNode;

    if(pSrc == NULL)                        /* return if null ptr */
        return NULL;
    pNode = pSrc->first;                    /* get node */
    if(pNode != NULL){
        pSrc->first = pNode->next;          /* advance list ptr */
        pNode->next = NULL;                 /* reset node ptr */
    }
    return pNode;
}