148. Sorting Linked List

148. Sorting Linked List

describe

Given the head node of your linked list, please sort it in ascending order and return the sorted linked list.

Advanced: Can you sort a linked list in O(n log n) time complexity and constant space complexity?

example

insert image description here
Input: head = [-1,5,3,4,0]
Output: [-1,0,3,4,5]

answer

1. Merge sort

  • top-down recursion

Merge sort is mainly divided into three steps:

  1. Find the middle node;
  2. Split the linked list and perform left and right recursion;
  3. Merge the left and right parts of the ordered linked list, and return the merged head node.

insert image description here

  • Time complexity: O ( n log ⁡ n ) O(n \log n)O ( nlogn ) , where n is the length of the linked list.
  • Space complexity: O ( log ⁡ n ) O(\log n)O(logn ) , where n is the length of the linked list. The space complexity mainly depends on the stack space of the recursive call.
class Solution {
    
    
public:
    ListNode* mergeSort(ListNode* head){
    
    
        if(head == nullptr || head->next == nullptr) return head;       // 终止条件,空节点或只有一个节点
        /*================== 快慢指针确定重点位置 ==================*/
        ListNode* slow = head;
        ListNode* fast = head;
        while(fast->next != nullptr && fast->next->next != nullptr){
    
     
            slow = slow->next;
            fast = fast->next->next;
        }
        /*================== 链表分割,递归 ==================*/
        ListNode* rightHead = slow->next;               // 右半部分链表头结点slow->next
        slow->next = nullptr;                           // 分割链表,指向空节点截断链表
        ListNode* left = mergeSort(head);               // 递归
        ListNode* right = mergeSort(rightHead);         
        /*================== 合并两个有序链表 ==================*/
        ListNode* dummyHead = new ListNode(-1);         // 迭代,LC21.合并两个有序链表
        ListNode* prev = dummyHead;
        while(left != nullptr && right != nullptr){
    
    
            if(left->val < right->val){
    
    
                prev->next = left;
                left = left->next;
            }else{
    
    
                prev->next = right;
                right = right->next;
            }
            prev = prev->next;
        }
        prev->next = left == nullptr? right : left;
        return dummyHead->next;                         // 返回合并后的链表头结点
    }

    ListNode* sortList(ListNode* head) {
    
    
        return mergeSort(head);
    }
};
  • Bottom-up space complexity O(1)

Using the bottom-up method to realize non-recursive recursive sorting, the space complexity of O(1) can be achieved.

insert image description here
In merge sorting, each division is to obtain the smallest node unit of the linked list through the dichotomy method, and then obtain the sorting result through multiple rounds of merging;

Each round of merging has a fixed unit length, that is, step = 1, 2, 4, 8…;

Therefore, we can simulate this split-merge operation through an iterative method.

The main idea is: 1 - Count the length
of the linked list n; 2 - The length of the sub-linked list step increases exponentially from 1, not greater than n; 3 - Split the left linked list with a length of step, and return the head node of the right linked list, and continue to divide , to get the head node of the next merge; 4- Merge the left and right linked lists, and connect to the last merged linked list, and finally complete the sorting after multiple rounds of merging.


  • Time complexity: O ( n log ⁡ n ) O(n \log n)O ( nlogn ) , where n is the length of the linked list.
  • Space complexity: O ( 1 ) O(1)O(1)
class Solution {
    
    
public:
    ListNode* merge(ListNode* left, ListNode* right){
    
     
        ListNode* dummyHead = new ListNode(-1);     // LC21.合并两个有序链表
        ListNode* prev = dummyHead;
        while(left != nullptr && right != nullptr){
    
    
            if(left->val < right->val){
    
    
                prev->next = left;
                left = left->next;
            }else{
    
    
                prev->next = right;
                right = right->next;
            }
            prev = prev->next;
        }
        prev->next = left == nullptr? right : left;
        ListNode* head = dummyHead->next;
        delete dummyHead;
        return head;                                // 返回合并后的链表头结点
    }

    ListNode* cut(ListNode* head, int len){
    
             // 从 head 开始分割长度为 len 的链表,并返回下一个链表的头结点
        if(head == nullptr) return nullptr;
        for(int i = 1; i < len; ++i){
    
               
            if(head == nullptr) return nullptr;     // 最后一个单元长度可能小于 len,因此返回 null
            head = head->next;
        }
        if(head != nullptr){
    
    
            ListNode* nextHead = head->next;
            head->next = nullptr;
            return nextHead;                        // 返回下一个链表的头结点
        }else{
    
    
            return nullptr;
        }
    }

    ListNode* sortList(ListNode* head) {
    
    
        if(head == nullptr || head->next == nullptr) return head;
        int n = 0;
        ListNode* curr = head;
        while(curr != nullptr){
    
    
            ++n;
            curr = curr->next;                      // 统计链表长度
        }
        ListNode* dummyHead = new ListNode(0, head);// 虚拟头结点
        for(int step = 1; step < n; step *= 2){
    
         // step = step * 2
            ListNode* prev = dummyHead;             // 上一次合并链表的尾节点 prev
            ListNode* curr = dummyHead->next;       // 遍历节点
            while(curr != nullptr){
    
    
                ListNode* head1 = curr;             // 左链表头结点
                ListNode* head2 = cut(head1, step); // 分割左链表,并返回右链表头结点
                curr = cut(head2, step);            // 分割右链表,并返回下一次合并的头结点
                prev->next = merge(head1, head2);   // 合并,连接链表
                while(prev->next != nullptr){
    
    
                    prev = prev->next;              // 更新合并链表的尾节点
                }
            }
        }
        return dummyHead->next;                     // 返回合并后的头结点
    }
};

2. Quick Sort

Quick sorting is achieved by exchanging node values , without node pointer operations ;

Selecting the nodes with subscript n/4 each time as the boundary value can avoid the timeout result in extreme cases.

  • Time complexity : select cut-off value complexity O ( n ) O(n)O ( n ) , partition complexityO ( n ) O(n)O ( n ) , recursion complexity isO ( logn ) O(logn)O ( log n ) The optimal and average time
    complexity of quick sort is O ( nlogn ) O(nlogn)O ( n l o g n ) , the worst time complexity isO ( n 2 ) O(n^2)O ( n2)
  • Space complexity : number of recursive layers O ( logn ) O(logn)O(logn)
class Solution {
    
    
public:
    void Qsort(ListNode* head, int cnt){
    
                        // 对从 head 开始的 cnt 个节点进行快速排序
        if(head == nullptr || cnt == 0) return;             // 排序结束
        ListNode* curr = head;
        for(int i = 0; i < cnt/4; ++i) curr = curr->next;   // (随机)选取分界值
        swap(head->val, curr->val);
        int pivot = head->val;                              // 当前排序的分界值 
        ListNode* curr_changed = head;                      // 交换过的最后一个节点
        curr = head->next;
        int changeCnt = 0;                                  // 节点交换的次数
        for(int i = 1; i < cnt; ++i){
    
                           // 遍历待排序的所有节点
            if(curr->val < pivot){
    
                              
                curr_changed = curr_changed->next;          // 左右划分
                swap(curr_changed->val, curr->val);
                ++changeCnt;
            }
            curr = curr->next;
        }
        swap(head->val, curr_changed->val);                 // 交换分界值到合适位置
        Qsort(head, changeCnt);                             // 递归前半部分
        Qsort(curr_changed->next, cnt-changeCnt-1);         // 递归后半部分
    }

    ListNode* sortList(ListNode* head) {
    
    
        int n = 0;
        ListNode* curr = head;
        while(curr != nullptr){
    
         // 统计链表长度
            ++n;
            curr = curr->next;
        } 
        if(n <= 1) return head;     
        Qsort(head, n);             // 排序
        return head;
    }
};

Quick Sort Operation Linked List Node
Quick Sort Operation Linked List Node

3. Insertion sort

Time complexity: O ( n 2 ) O(n^2)O ( n2 ), time limit exceeded.

Guess you like

Origin blog.csdn.net/qq_19887221/article/details/126368896