
最优解法没做出来,插入排序前面做过了。
 参考做法
使用归并排序,有两种方法:一种是自顶向下,另一种是自底向上。
如果要达到常数级空间复杂度,那么就需要使用自底向上的解法。
 自顶向下
 算法思想
递归地从中间拆分链表,然后进行排序,再合并两个有序链表。
找链表中间节点可以使用快慢指针,快指针走两步,慢指针走一步,当快指针到达末尾,慢指针指向链表中间节点。
设中间节点为mid,记下mid的下一个节点,然后将mid->next设为null。
分别排序前后两个链表,然后进行合并。
注意:有一种特殊情况就是:当链表有两个节点时,mid最终会指向第二个结点,会发生错误,因为递归排序前后两个链表时,前面链表还是原链表,后面链表是空。那么它会一直卡在两个节点的链表这里,陷入死循环。
所以本来递归地终止条件是当传入链表为空,或只有一个节点。
为了避免上述特殊情况,再加上一个条件就是当链表只有两个节点时:比较前后两个节点的值,升序,则直接返回;否则,交换两个节点,返回头节点。
 算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
   | class Solution { public:     ListNode* sortList(ListNode* head) {         return sort(head);     }
      ListNode * sort(ListNode * head) {         if(head == nullptr || head->next == nullptr) {             return head;         }                  if(head->next->next == nullptr) {             if(head->val < head->next->val) {                 return head;             } else {                 ListNode * node = head->next;                 head->next = nullptr;                 node->next = head;                 return node;             }         }         ListNode * slow = head;         ListNode * fast = head;         while (fast && fast->next) {             slow = slow->next;             fast = fast->next->next;         }         ListNode * mid = slow;         ListNode * next_mid = mid->next;         mid->next = nullptr;         return merge(sortList(head), sortList(next_mid));
      }
      ListNode * merge(ListNode * l1, ListNode * l2) {         if(l1 == nullptr) return l2;         if(l2 == nullptr) return l1;
          ListNode * dummy = new ListNode();         ListNode * node = dummy, * node1 = l1, * node2 = l2;         while (node1 != nullptr && node2 != nullptr) {             if(node1->val < node2->val) {                 node->next = node1;                 node1 = node1->next;             } else {                 node->next = node2;                 node2 = node2->next;             }             node = node->next;         }         if (node1 != nullptr) {             node->next = node1;         }         if(node2 != nullptr) {             node->next = node2;         }         return dummy->next;     }
 
  };
 
  | 
也可以在找中间节点的时候避免这种情况,就是将上述的while条件fast && fast->next改成fast->next && fast->next->next。然后删掉处理 链表只有两个节点的特殊情况的代码即可。
那么sort函数写成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | ListNode * sort(ListNode * head) {     if(head == nullptr || head->next == nullptr) {         return head;     }     ListNode * slow = head;     ListNode * fast = head;     while (fast->next && fast->next->next) {         slow = slow->next;         fast = fast->next->next;     }     ListNode * mid = slow;     ListNode * next_mid = mid->next;     mid->next = nullptr;     return merge(sortList(head), sortList(next_mid));
  }
 
  | 
 性能分析
时间复杂度:O(NlogN)。
空间复杂度:递归栈存了$$logN$$个节点。时间复杂度为O(logN)。
 自底向上
 算法思想
首先求得链表的长度length。然后将链表拆分成子链表,进行合并。
- 使用subLength代表每次需要排序的子链表的长度,初始subLength = 1
 - 每次将链表拆分成若干个长度为subLength的子链表(最后一个子链表有可能小于subLength,如果除不尽)按照每两个子链表一组进行合并,合并后可以得到若干个长度为 subLength * 2 的有序子链表(最后一个子链表长度有可能小于subLength * 2)。
 - 将subLength值加倍,重复第二步,直到subLength的值大于或等于length。
 
整个链表排序完成。
简单说就是:两个链表一组进行合并。初始每个链表长度为1,合并后是若干个长度为2的有序子链表;然后再两个一组,这时,每个子链表长度为2,合并后是若干个长度为4的有序子链表。一直合并下去,直到合并后的有序子链表长度大于等于链表总长度。
 算法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
   | class Solution { public:     ListNode* sortList(ListNode* head) {         if(head == nullptr || head->next == nullptr) {             return head;         }         int length = 0;         ListNode * node = head;         while (node) {             length++;             node = node->next;         }         ListNode * dummy = new ListNode(0);         dummy->next = head;
          for(int subLength = 1; subLength < length; subLength *= 2) {                          ListNode * pre = dummy, * cur = dummy->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);                                  pre->next = merged;                 while (pre->next != nullptr) {                     pre = pre->next;                 }                                  cur = next;             }         }         return dummy->next;     }
 
      ListNode * merge(ListNode * l1, ListNode * l2) {         if(l1 == nullptr) return l2;         if(l2 == nullptr) return l1;
          ListNode * dummy = new ListNode();         ListNode * node = dummy, * node1 = l1, * node2 = l2;         while (node1 != nullptr && node2 != nullptr) {             if(node1->val < node2->val) {                 node->next = node1;                 node1 = node1->next;             } else {                 node->next = node2;                 node2 = node2->next;             }             node = node->next;         }         if (node1 != nullptr) {             node->next = node1;         }         if(node2 != nullptr) {             node->next = node2;         }         return dummy->next;     }
 
  };
 
  | 
 性能分析
时间复杂度:O(NlogN)。
空间复杂度:O(1)。