sort-list

Sort a linked list in O(n log n) time using constant space complexity.

实现一个对链表排序的算法,要求时间复杂度为O(nlogn),空间复杂度为O(1)

思路一:

首先想到的是快排,快排代码在下面,可是提交之后总是运行超时,这里做一下分析:
首先分析空间复杂度,一般来说,快排的空间复杂度是O(logn),因为快排是递归调用,每次调用都要用常数的空间,由于对链表进行排序,所以实际上排完序后指向链表节点的指针可以复用,从而实现O(1)的空间复杂度。
其次分析时间复杂度,快排的平均时间复杂度为O(nlogn), 可是最坏的情况为O(n^2),也就是说快排的时间是不稳定的,如果给的测试用例刚好是逆序的,就会到最坏情况。所以会超时,那么需要一种时间比较稳定的算法,选择归并。快排代码如下

#include <iostream>
#include "../coding interviews/Utilities/List.h"
using namespace std;
         ListNode* pNext = nullptr;//内循环指针
         ListNode* pPNext = nullptr;//永远跟在pNext的前一个
        // ListNode* pHead  = nullptr;//标示头指针
         ListNode* temp = nullptr;
         bool ishead    = true;
    ListNode* sortListCore(ListNode *pbegin, ListNode* pend, ListNode* pPreBegin, ListNode* pHead)
    {
        if(pbegin == pend)
            return pbegin;

         ishead = true;//用来确定头指针的位置,头指针是第一个移到pNode前的节点
         pNext = pbegin->m_pNext;//内循环指针
         pPNext = pbegin;//永远跟在pNext的前一个
         pHead  = pbegin;//标示头指针
         temp = nullptr;
         while(pNext != pend)
            {
                if(pbegin->m_nValue > pNext->m_nValue)
                {
                    if(pPreBegin == nullptr)
                    {
                        temp = pNext->m_pNext;
                        pNext->m_pNext = pbegin;
                        pPNext->m_pNext = temp;
                        pPreBegin    = pNext;
                        pNext       = pPNext->m_pNext;
                        if(ishead)
                        {
                            pHead = pPreBegin;
                            ishead = false;
                        }
                    }
                    else
                    {
                        temp = pNext->m_pNext;
                        pNext->m_pNext    = pbegin;
                        pPNext->m_pNext    = temp;
                        pPreBegin->m_pNext = pNext;
                        pPreBegin       = pNext;
                        pNext          = pPNext->m_pNext;
                    }
                }
                else
                {
                    pPNext = pNext;
                    pNext = pNext->m_pNext;
                }
            }

        sortListCore(pbegin->m_pNext, nullptr, pbegin, pHead);
        pHead = sortListCore(pHead, pbegin, nullptr, pHead);
        return pHead;
    }

    ListNode *sortList(ListNode *head)
    {
//        if(head == nullptr)
//            return nullptr;
//        ListNode* pNode = head;//外循环指针
        //ListNode* pPreNode = nullptr;//永远跟在pNode的前一个

       return sortListCore(head, nullptr, nullptr, head);

    }

int main()
{
    ListNode* pNode1 = CreateListNode(5);
    ListNode* pNode2 = CreateListNode(4);
    ListNode* pNode3 = CreateListNode(6);
    ListNode* pNode4 = CreateListNode(2);
    ListNode* pNode5 = CreateListNode(1);
    ListNode* pNode6 = CreateListNode(0);

    ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
    ConnectListNodes(pNode3, pNode4);
    ConnectListNodes(pNode4, pNode5);
    ConnectListNodes(pNode5, pNode6);

    PrintList(pNode1);

    ListNode* newNode = sortList(pNode1);

    PrintList(newNode);
}

思路二

既然快排行不通,那就用归并吧,直接上代码


//  struct ListNode {
//      int val;
//      ListNode *next;
//      ListNode(int x) : val(x), next(nullptr) {}
//  };
#include <iostream>
#include "../coding interviews/Utilities/List.h"
using namespace std;
         ListNode* pRNext = nullptr;//要归并的后一个数组的头指针
         ListNode* pLNext  = nullptr;//要归并的前一个数组的头指针

         ListNode* temp = nullptr;
         ListNode* pmid = nullptr;//标示中间节点的位置
    ListNode* Getmid(ListNode *pbegin, ListNode* pend)
    {
        temp = pbegin;
        pmid  = pbegin;
        if(temp == pend)
        {
            return temp;
        }
        else
        {
            temp = temp->next;
            if(temp == pend)
                return pmid;
            else
                temp = temp->next;
        }

        while(temp != pend && temp->next != pend)
        {
            pmid = pmid->next;
            temp = temp->next->next;
        }
        return pmid;
    }

    ListNode* sortListCore(ListNode *pbegin, ListNode* pend, ListNode* pmiddle)
    {
        if(pbegin != pmiddle->next && pmiddle->next->next != pend && pmiddle->next != pend)
        {
            pmid = Getmid(pmiddle->next,  pend);
            pmiddle->next = sortListCore(pmiddle->next, pend, pmid);
            //PrintList(pmiddle->next);
            pmid = Getmid(pbegin,  pmiddle->next);
            pbegin = sortListCore(pbegin, pmiddle->next, pmid);
        }

        pLNext = pbegin;
        pRNext = pmiddle->next;
        pmiddle->next = nullptr;//将链表拆开
        ListNode *dumy = CreateListNode(0);
        temp = dumy;

        while(pLNext && pRNext)
        {
            if(pLNext->val > pRNext->val)
            {
                temp->next = pRNext;
                pRNext     = pRNext->next;
                temp = temp->next;
            }
            else
            {
                temp->next = pLNext;
                pLNext     = pLNext->next;
                temp = temp->next;
            }
        }
        if(pLNext)   temp->next = pLNext;
        if(pRNext)   temp->next = pRNext;
        temp = dumy->next;
        delete dumy;
        dumy = nullptr;
        return temp;
    }

    ListNode *sortList(ListNode *head)
    {
        if(head == nullptr)
            return nullptr;
        if(head->next == nullptr)
            return head;
        pmid = Getmid(head, nullptr);
       // PrintListNode(pmid);
        return sortListCore(head, nullptr, pmid);
    }

int main()
{
    ListNode* pNode1 = CreateListNode(3);
    ListNode* pNode2 = CreateListNode(4);
    ListNode* pNode3 = CreateListNode(1);
//    ListNode* pNode4 = CreateListNode(0);
//    ListNode* pNode5 = CreateListNode(1);
//    ListNode* pNode6 = CreateListNode(4);

    ConnectListNodes(pNode1, pNode2);
    ConnectListNodes(pNode2, pNode3);
//    ConnectListNodes(pNode3, pNode4);
//    ConnectListNodes(pNode4, pNode5);
//    ConnectListNodes(pNode5, pNode6);

    PrintList(pNode1);

    ListNode* newNode = sortList(pNode1);

    PrintList(newNode);

    DestroyList(newNode);
}

这个代码需要注意俩点,第一点是结束条件要小心,需要考虑各种不同的情况,因为我返回的是后一个需要归并的链表的前一个节点的指针,所以要特别小心,后一个链表不存在,或存在只有一个节点的情况,同样在获得中间节点函数中也要考虑到这几种情况;第二个需要注意的点是,归并可以直接用一个指针来表示新建立的链表,因此避免了O(n)的辅助空间,但是在做归并是必须先把原链表断开,断开之后不能丢节点才可以。我原先的想法是移动节点的位置,只要对要移动和要插入的节点做好记录(保留前一个节点),就可以实现O(1)时间复杂度的节点移动,但是实际运行发现超内存,我的思路还是不够“归并”,所以改成如上所示的代码。

猜你喜欢

转载自blog.csdn.net/wzc2608/article/details/79957142