/**
* Definition for singly-linked list.
* 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) {}
* };
*/
插入排序
插入排序的时间复杂度是 O(n^2)
插入排序算法:
- 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
- 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
- 重复直到所有输入数据插入完为止。
ListNode* sortList(ListNode* head) {
//使用插入排序
if(head == NULL || head->next == NULL) return head;
//一个哑结点,方便在head之前插入节点
ListNode * dummyHead = new ListNode(0);
//哑结点的指向head
dummyHead->next = head;
//需要排序插入的节点,从第二个节点开始
ListNode * curr = head->next;
//排好序的最后一个节点,从第一个节点开始
ListNode * lastSorted = head;
ListNode * prev;
while(curr != NULL){
//如果需要排序的节点的值大于已经排好序的最后一个值,就指向下一个
if(curr->val > lastSorted->val){
lastSorted = lastSorted->next;
curr = lastSorted->next;
}else{
//否则就表明当前需要排序的节点需要插入到已经排好序的节点中
//需要从头开始,找地方插入
prev = dummyHead;
//先找到需要插入的地方,需要满足大于等于前一个,小于后一个
while(prev->next->val < curr->val){
prev = prev->next;
}
//经过上面的循环,已经找到了一个prev,它满足prev小于curr,并且prev->next大于等于curr,所以curr要插入在prev之后
//插入之前要先将curr之后的节点保存起来,存在lastSorted->next
lastSorted->next = curr->next;
curr->next = prev->next;
prev->next = curr;
//lastSorted的下一个节点就是待插入的节点
curr = lastSorted->next;
}
}
return dummyHead->next;
}
归并排序
时间复杂度是O(nlogn) 的排序算法包括归并排序、堆排序和快速排序(快速排序的最差时间复杂度是 O(n^2),其中最适合链表的排序算法是归并排序。
归并排序基于分治算法。最容易想到的实现方式是自顶向下的递归实现,考虑到递归调用的栈空间,自顶向下归并排序的空间复杂度是O(logn)。如果要达到O(1) 的空间复杂度,则需要使用自底向上的实现方式。
对链表自顶向下归并排序的过程如下。
- 找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中点可以使用快慢指针的做法,快指针每次移动 22 步,慢指针每次移动 11 步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。
- 对两个子链表分别排序。
- 将两个排序后的子链表合并,得到完整的排序后的链表。
上述过程可以通过递归实现。递归的终止条件是链表的节点个数小于或等于 1,即当链表为空或者链表只包含 1个节点时,不需要对链表进行拆分和排序。
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;
}
使用自底向上的方法实现归并排序,则可以达到 O(1) 的空间复杂度。
首先求得链表的长度}length,然后将链表拆分成子链表进行合并。
具体做法如下。
- 用subLength 表示每次需要排序的子链表的长度,初始时subLength=1。
- 每次将链表拆分成若干个长度为subLength 的子链表(最后一个子链表的长度可以小于
- subLength),按照每两个子链表一组进行合并,合并后即可得到若干个长度为 subLength×2 的有序子链表(最后一个子链表的长度可以小于 subLength×2)。
- 将subLength 的值加倍,重复第 2 步,对更长的有序子链表进行合并操作,直到有序子链表的长度大于或等于length,整个链表排序完毕。
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) {
ListNode* prev = dummyHead, *curr = dummyHead->next;
while (curr != nullptr) {
ListNode* head1 = curr;
for (int i = 1; i < subLength && curr->next != nullptr; i++) {
curr = curr->next;
}
ListNode* head2 = curr->next;
curr->next = nullptr;
curr = head2;
for (int i = 1; i < subLength && curr != nullptr && curr->next != nullptr; i++) {
curr = curr->next;
}
ListNode* next = nullptr;
if (curr != nullptr) {
next = curr->next;
curr->next = nullptr;
}
ListNode* merged = merge(head1, head2);
prev->next = merged;
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;
}