在链表上使用归并排序法(自顶向下的递归和自底向上的迭代两种方法)

转载:leetcode 148题目 官方题解

链表的一些辅助函数
struct ListNode {
     int val;
     ListNode *next;
     ListNode() : val(0), next(nullptr) {}
     ListNode(int x) : val(x), next(nullptr) {}
     ListNode(int x, ListNode *next) : val(x), next(next) {}
};

ListNode* createLinkedList(int arr[],int n){

    if( n == 0 )
        return NULL;

    ListNode* head = new ListNode(arr[0]);

    ListNode* curNode = head;
    for(int i = 1; i < n; i++){
        curNode->next = new ListNode(arr[i]);
        curNode = curNode->next;
    }

    return head;
}

void deleteLinkedList(ListNode* head){
    ListNode* curNode = head;
    while( curNode != NULL ){
        ListNode* delNode = curNode;
        curNode = curNode->next;
        delete delNode;
    }
}

void printLinkedList(ListNode* head){

    ListNode* curNode = head;
    while( curNode != NULL ){
        cout << curNode->val << " -> ";
        curNode = curNode->next;
    }

    cout << "NULL" << endl;

}

自顶向下的递归

对链表自顶向下归并排序的过程如下:

  • 1 找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中点可以使用快慢指针的做法,快指针每次移动 2 步,慢指针每次移动 1 步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。

  • 2 对两个子链表分别排序。将两个排序后的子链表合并,得到完整的排序后的链表(另开辟一个新的节点来保存排序后的链表)

  • 3 上述过程可以通过递归实现。递归的终止条件是链表的节点个数小于或等于 1,即当链表为空或者链表只包含 1个节点时,不需要对链表进行拆分和排序。

class Solution {

private:
    ListNode* sortList(ListNode* head, ListNode* tail){

        if( !head )
            return head;
// 这里用head->next == tail 比 head == tail 好在不用遍历链表去找tail了,直接设置为nullptr就可以了
        if( head->next == tail ){ //只有一个节点的时候
            head->next = nullptr;
            return head;
        }

        ListNode* slow = head, *fast = head;
        //使用快慢指针找中间位置
        while( fast != tail ){
            slow = slow->next;
            fast = fast->next;
            if( fast != tail )
                fast = fast -> next;
        }
        ListNode* mid = slow;
        return merge( sortList(head, mid), sortList(mid, tail) );

    }

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

public:
    ListNode* sortList(ListNode* head) {

        return sortList(head, nullptr);

    }
};

自底向上的迭代方法

可以类比于数组的写法

template  <typename T>
void mergeSortBU( T arr[], int n ){

    for( int sz = 1; sz <= n; sz += sz ){ //步劲从1->2->4->8等等
        for( int i = 0; i + sz < n; i += sz + sz ){
            //对arr[i...i+sz-1] 和 arr[i+sz....i+2*sz-1]进行归并排序
            //这里 i + sz - 1 和 i + sz + sz - 1可能已经超过了n的大小 所以要在循环中限制🚫
            __merge( arr, i , i + sz - 1, min(i + sz + sz - 1, n-1) );
        }
    }
}

链表的迭代方法更多的是需要将指针每次移动相应的步伐(每次移动就是一个for循环遍历)

class Solution {

private:
    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;
    }

public:
    ListNode* sortList(ListNode* head) {

        if( head == nullptr )
            return head;

        int length = 0;
        ListNode* node = head;
        while(node != nullptr){
            length ++;
            node = node->next;
        }

        ListNode* dummyHead = new ListNode(0 , head);

        for( int subLength = 1; subLength < length; subLength <<= 1 ){  //步长从1 -> 2 -> 4 这样逐渐累加
            ListNode* prev = dummyHead, *cur = dummyHead -> next;
            while( cur != nullptr ){
                ListNode* head1 = cur;
                for(int i = 1; i < subLength && cur->next != nullptr; i ++ )
                    cur = cur -> next;

                ListNode* head2 = cur -> next;
                cur -> next = nullptr;
                cur = head2;
                for (int i = 1; i < subLength && cur != nullptr && cur->next != nullptr; i++)
                    cur = cur->next;

                ListNode* next = nullptr;
                if( cur != nullptr ){
                    next = cur -> next;
                    cur -> next = nullptr;
                }

                ListNode* merged = merge(head1, head2);
                prev -> next = merged;
                while( prev -> next != nullptr )
                    prev = prev -> next;

                cur = next;

            }

        }
        return dummyHead->next;
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值