题目描述:
因为要求时间复杂度为O(nlogn),而时间复杂度为O(nlogn)的排序算法有快排,归并排序和堆排。
那么首先,链表的相关操作如下:
struct LinkNode {
int data;
struct LinkNode* next;
LinkNode() : next(nullptr) {} //带有表头结点
LinkNode(int d) : data(d), next(nullptr) {}
};
void addNode(LinkNode* head, int data) {
LinkNode* p = head;
while (p->next) p = p->next;
LinkNode* q = new LinkNode(data);
p->next = q;
}
void deleteNode(LinkNode* head) {
while (head) {
LinkNode* p = head;
head = head->next;
delete(p);
}
}
快排
快排的思想是每次选取一个元素作为基准元素,由于链表的特性,因此我们直接选择第一个结点的值即可,同时我们只能依次顺序遍历,而不是像数组那样前后两个指针同时遍历,示意图如下:
代码实现如下:
LinkNode* partition(LinkNode* low, LinkNode* high) {
if (low == high || low->next == high) return low;
int key = low->data; //选择low作为基准元素
LinkNode* p = low, * q = low;
while (q != high) { //从low开始向后进行一次遍历
if (q->data < key) {
p = p->next;
swap(p->data, q->data);
}
q = q->next;
}
swap(low->data, p->data);
return p;
}
void quick_sort(LinkNode* low, LinkNode* high) {
if (low == high || low->next == high) return;
LinkNode* mid = partition(low, high);
quick_sort(low, mid);
quick_sort(mid->next, high);
}
LinkNode* quickSort(LinkNode* head) {
if (head == nullptr || head->next == nullptr) return head;
quick_sort(head->next, nullptr);
return head;
}
归并排序
归并排序需要先从中间断开,然后再合并,因此断开的过程可以使用快慢两指针:
LinkNode* getMid(LinkNode* head) { //获取中间节点并分段
LinkNode* fast = head;
LinkNode* slow = head;
LinkNode* prev = head;
while (true) {
if (fast == nullptr) break;
fast = fast->next;
if (fast == nullptr) break;
fast = fast->next;
prev = slow;
slow = slow->next;
}
prev->next = nullptr; //将链表分为两段
return slow;
}
LinkNode* merge(LinkNode* head1, LinkNode* head2) {
LinkNode* head = new LinkNode();
LinkNode* tail = head;
while (head1 && head2) {
LinkNode* p;
if (head1->data < head2->data) {
p = head1;
head1 = head1->next;
}
else {
p = head2;
head2 = head2->next;
}
p->next = tail->next;
tail->next = p;
tail = tail->next;
}
if (head1) tail->next = head1;
if (head2) tail->next = head2;
return head->next;
}
LinkNode* mergeSort(LinkNode* head) {
if (head == nullptr || head->next == nullptr) return head;
LinkNode* head1 = head;
LinkNode* head2 = getMid(head); //获取中间节点,将链表分为两段
head1 = mergeSort(head1); //分别对两段链表进行排序
head2 = mergeSort(head2);
return merge(head1, head2);
}