数据结构与算法之排序链表

                                              排序链表

                                                                  参考Leetcode-148

一、归并排序链表

1、方法一:自顶向下归并排序

    步骤如下:

  • 找到链表的中点,将链表拆分成两个子链表;

  • 对两个子链表进行合并排序;

  • 两个子链表重复前两个步骤;

    代码参考如下:

#include <iostream>
using namespace std;

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

class Solution {
public:
    ListNode* sortList(ListNode* head) {
        return sortList(head, nullptr);
    }

    ListNode* sortList(ListNode* head, ListNode* tail) {
        if (head == nullptr) { // 为空结点返回
            return head;
        }
        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;
    }
};

int main() {
    Solution sol;
    ListNode *l1 = new ListNode(-1);
    ListNode *l2 = new ListNode(5);
    ListNode *l3 = new ListNode(3);
    ListNode *l4 = new ListNode(4);
    ListNode *l5 = new ListNode(0);

    l1->next = l2;
    l2->next = l3;
    l3->next = l4;
    l4->next = l5;

    sol.sortList(l1);

    return 0;
}

    上述算法的时间复杂度是O(n logn),空间复杂度为O(logn),其中n为链表的长度。

2、方法二:自底向上归并排序

    具体做法如下:

  • 将链表拆分成长度为subLength的子链表,每两个子链表进行合并;

  • subLength的长度变化为[1, 2, 4, ...],每一轮是前一轮的两倍,重复上述步骤;

    代码示例如下:

#include <iostream>
using namespace std;

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

class Solution {
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) { // subLength左移1位,相当于subLength*2
            ListNode* prev = dummyHead, *curr = dummyHead->next;
            while (curr != nullptr) {
                ListNode* head1 = curr;
                for (int i = 1; i < subLength && curr->next != nullptr; i++) { // 根据subLength来更新curr,以此获得head2的位置
                    curr = curr->next;
                }
                ListNode* head2 = curr->next;
                curr->next = nullptr; // 断开链表,形成head1子链表
                curr = head2;
                for (int i = 1; i < subLength && curr != nullptr && curr->next != nullptr; i++) { // 同上
                    curr = curr->next;
                }
                ListNode* next = nullptr;
                if (curr != nullptr) { // 获取next指针,并且断开链表,形成head2子链表
                    next = curr->next;
                    curr->next = nullptr;
                }
                ListNode* merged = merge(head1, head2); // 合并两个子链表
                prev->next = merged;
                /* 更新prev、next指针 */
                while (prev->next != nullptr) {

                    prev = prev->next;
                }
                curr = next;
            }
        }
        return dummyHead->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;
    }
};

int main() {
    Solution sol;
    ListNode *l1 = new ListNode(-1);
    ListNode *l2 = new ListNode(5);
    ListNode *l3 = new ListNode(3);
    ListNode *l4 = new ListNode(4);
    ListNode *l5 = new ListNode(0);

    l1->next = l2;
    l2->next = l3;
    l3->next = l4;
    l4->next = l5;

    return 0;
}

    排序的过程如下图所示:

第一轮:

第二轮:

第三轮:

    它的时间复杂度为O(n logn),空间复杂度为O(1),其中n为链表的长度。

猜你喜欢

转载自blog.csdn.net/hu853712064/article/details/130543423