对一个单向的无序链表进行排序,通常可以采用以下几种排序算法:插入排序、归并排序、快速排序等。对于链表来说,归并排序是一个特别适合的选择,因为它是一个稳定的排序算法,不需要额外的空间,并且具有较好的时间复杂度。链表的节点在排序时可以直接调整指针,因此比数组更适合使用归并排序。
归并排序的思路:
1. 分割:将链表从中间分成两个部分。
2. 递归排序:递归地对每个部分进行排序。
3. 合并:将两个已排序的子链表合并成一个有序链表。
步骤:
1. 使用快慢指针找到链表的中点,并将链表分成两部分。
2. 递归排序这两部分链表。
3. 合并两个已排序的链表。
#include
using namespace std;
// 定义单链表节点
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
// 合并两个有序链表
ListNode* merge(ListNode* l1, ListNode* l2) {
// 创建一个虚拟头结点
ListNode* dummy = new ListNode(0);
ListNode* current = dummy;
// 合并两个链表
while (l1 != nullptr && l2 != nullptr) {
if (l1->val < l2->val) {
current->next = l1;
l1 = l1->next;
} else {
current->next = l2;
l2 = l2->next;
}
current = current->next;
}
// 如果有剩余的节点,直接连接
if (l1 != nullptr) {
current->next = l1;
} else if (l2 != nullptr) {
current->next = l2;
}
return dummy->next;
}
// 寻找链表的中点
ListNode* findMiddle(ListNode* head) {
if (head == nullptr) return nullptr;
ListNode* slow = head;
ListNode* fast = head;
while (fast != nullptr && fast->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
// 归并排序
ListNode* mergeSort(ListNode* head) {
if (head == nullptr || head->next == nullptr) {
return head; // 如果链表为空或只有一个元素,返回本身
}
// 寻找中点并分割链表
ListNode* middle = findMiddle(head);
ListNode* secondHalf = middle->next;
middle->next = nullptr; // 断开链表
// 递归排序两个子链表
ListNode* left = mergeSort(head);
ListNode* right = mergeSort(secondHalf);
// 合并两个已排序的链表
return merge(left, right);
}
// 用于打印链表
void printList(ListNode* head) {
ListNode* current = head;
while (current != nullptr) {
cout << current->val << " -> ";
current = current->next;
}
cout << "null" << endl;
}
// 主函数,测试代码
int main() {
// 构造链表
ListNode* head = new ListNode(4);
head->next = new ListNode(2);
head->next->next = new ListNode(1);
head->next->next->next = new ListNode(3);
cout << "Original list: ";
printList(head);
// 排序链表
head = mergeSort(head);
cout << "Sorted list: ";
printList(head);
return 0;
}
详细解释:
1. ListNode 结构体:定义了链表节点,包含一个整数值 `val` 和一个指向下一个节点的指针 `next`。
2. merge 函数:负责合并两个已排序的链表。通过比较两个链表的节点值,逐个将较小的节点加入到新的链表中,直到其中一个链表为空,最后将另一个链表剩余的部分接到合并链表的末尾。
3. findMiddle 函数:使用快慢指针找到链表的中点。快指针每次走两步,慢指针每次走一步。当快指针到达末尾时,慢指针刚好到达链表的中点。
4. mergeSort 函数:递归实现归并排序。首先找到链表的中点,然后递归地对左右两个子链表进行排序,最后将两个已排序的子链表合并。
5. printList 函数:用于打印链表,方便查看排序前后的结果。
复杂度分析:
时间复杂度:每次递归都将链表分为两半,递归深度为 `O(log n)`,在每一层递归中合并链表的时间是 `O(n)`,因此总的时间复杂度为 `O(n log n)`。
空间复杂度:归并排序需要递归栈的空间,递归的最大深度为 `O(log n)`,因此空间复杂度为 `O(log n)`。