1. 程式人生 > 其它 >leetcode 148. 排序連結串列(歸併排序 快慢指標 遞迴 迭代)

leetcode 148. 排序連結串列(歸併排序 快慢指標 遞迴 迭代)

連結:https://leetcode-cn.com/problems/sort-list/

題目

給你連結串列的頭結點head,請將其按 升序 排列並返回 排序後的連結串列 。

進階:

你可以在O(nlogn) 時間複雜度和常數級空間複雜度下,對連結串列進行排序嗎?

示例

示例 1:

輸入:head = [4,2,1,3]
輸出:[1,2,3,4]
示例 2:

輸入:head = [-1,5,3,4,0]
輸出:[-1,0,3,4,5]
示例 3:

輸入:head = []
輸出:[]

提示:

連結串列中節點的數目在範圍[0, 5 * 104]內
-105<= Node.val <= 105

思路

方法1
遞迴 實現歸併排序
使用快慢指標分割連結串列

由於使用遞迴O(logn)棧記憶體空間

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(!head||head->next==nullptr)
            return head;
        ListNode *front=head->next,*back=head;
        int index=0;
        while(front->next)
        {
            front=front->next;
            index++;
            if(index%2==0)
            {
                back=back->next;
                index=0;
            }
        }
        front=back->next;
        back->next=nullptr;
        return sort(sortList(head),sortList(front));

    }

    ListNode*sort(ListNode*a,ListNode*b)
    {
        ListNode*ha=a,*hb=b;
        ListNode*ptr=nullptr;
        if(ha->val<=hb->val)
        {
            ptr=ha;
            ha=ha->next;
        }else
        {
            ptr=hb;
            hb=hb->next;
        }
        ListNode *head=ptr;
        while(ha!=nullptr&&hb!=nullptr)
        {
            if(ha->val<=hb->val)
            {
                ptr->next=ha;
                ha=ha->next;
            }else{
                ptr->next=hb;
                hb=hb->next;
            }
            ptr=ptr->next;
        }
        if(ha!=nullptr)
        {
            ptr->next=ha;
        }
        if(hb!=nullptr)
        {
            ptr->next=hb;
        }
        return head;
    }  
};

方法2
由於進階要求常數級空間複雜度 所以不能用遞迴
可以手動分割等長連結串列 迭代

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(!head||head->next==nullptr)
            return head;
        int num=0;
        ListNode*ptr=head;
        while(ptr)
        {
            num++;
            ptr=ptr->next;
        }
        ListNode *dhead=new ListNode(0,head);//定義頭節點
        for(int lenth=1;lenth<num;lenth<<=1)
        {
            ListNode *pre=dhead,*cur=dhead->next;//定義分支的頭節點和 流動的節點
            while(cur!=nullptr)//當前節點不為空
            {
                ListNode *head1=cur;
                for(int i=1;i<lenth&&cur->next!=nullptr;++i)
                {
                    cur=cur->next;
                }
                ListNode *head2=cur->next;
                cur->next=nullptr;
                cur=head2;
                for(int i=1;i<lenth&&cur!=nullptr&&cur->next!=nullptr;++i)
                {
                    cur=cur->next;
                }
                ListNode *n =nullptr;
                if(cur!=nullptr)
                {
                    n=cur->next;
                    cur->next=nullptr;
                }
                pre->next=merge(head1,head2);
                while(pre->next!=nullptr){
                    pre=pre->next;
                }
                cur=n;
            }
        }
        return dhead->next;

    }

    ListNode* merge(ListNode* head1, ListNode* head2) {
        ListNode* dummyHead = new ListNode(0);
        ListNode* temp = dummyHead, *temp1 = head1, *temp2 = head2;
        while (temp1 != nullptr && temp2 != nullptr) {
            if (temp1->val <= temp2->val) {
                temp->next = temp1;
                temp1 = temp1->next;
            } else {
                temp->next = temp2;
                temp2 = temp2->next;
            }
            temp = temp->next;
        }
        if (temp1 != nullptr) {
            temp->next = temp1;
        } else if (temp2 != nullptr) {
            temp->next = temp2;
        }
        return dummyHead->next;
    }
};

注意

連結串列問題可以定義額外頭節點來 規範迴圈條件ListNode *dhead=new ListNode(0,head);