LeetCode 148. Sort linked list

Posted by F.Danials on Mon, 14 Feb 2022 09:01:46 +0100

subject

Give you the head node of the linked list. Please arrange it in ascending order and return the sorted linked list.

Example 1:

Input: head = [4,2,1,3]
Output: [1,2,3,4]

Example 2:

Input: head = [-1,5,3,4,0]
Output: [- 1,0,3,4,5]

Example 3:

Input: head = []
Output: []

Tips:

  • The number of nodes in the linked list is in the range [0, 5 * 104]
  • -105 <= Node.val <= 105

Advanced: can you sort the linked list under O(n log n) time complexity and constant space complexity?

It is still difficult to sort the linked list. Here is an idea: first traverse the linked list to get the number of elements, then apply for the space of the corresponding number with malloc, store all the linked list elements in the applied array, and sort the array with some sorting methods (fast sorting, Hill sorting, heap sorting, etc.), Then write the ordered data back to the linked list.

Here I do not use the above ideas, but directly operate the linked list, so as to achieve the purpose of ranking the linked list. Both recursive and non recursive code ideas are merge sorting.

recursion

thinking

1. Find the midpoint of the linked list, take the midpoint as the boundary, and split the linked list into two sub linked lists.

2. Sort the two sub linked lists separately.

3. Merge the two sorted sub linked lists to obtain a complete sorted linked list.

The above process can be implemented recursively. The termination condition of recursion is that the number of nodes in the linked list is less than or equal to 1, that is, when the linked list is empty or the linked list contains only 1 node, there is no need to split and sort the linked list.

The fast and slow pointer can be used to find the midpoint of the linked list. The fast pointer moves 2 steps at a time and the slow pointer moves 1} step at a time. When the fast pointer reaches the end of the linked list, the linked list node pointed to by the slow pointer is the midpoint of the linked list.

Merging two linked lists can use the tail insertion method to continuously tail the nodes after the given header.

code

struct ListNode* Mergedata(struct ListNode* head1,struct ListNode* head2){//Merge two ordered linked lists
    struct ListNode s;//Given chain header
    struct ListNode* cur=&s;//Head pointer, pointing to the chain header
    while(head1&&head2){//Insert the tail of two ordered linked lists after s
        if(head1->val<=head2->val){
            cur->next=head1;
            head1=head1->next;
        }else{
            cur->next=head2;
            head2=head2->next;
        }
        cur=cur->next;
    }
    if(head1){//Tail insertion remainder
        cur->next=head1;
    }else{
        cur->next=head2;
    }
    return s.next;
}
struct ListNode* Middle(struct ListNode* head){
    //Find the middle node of the linked list and return the address. Note that this function will divide a linked list into two.
    struct ListNode* prev=NULL;
    struct ListNode* slow=head;
    struct ListNode* fast=head;
    while(fast&&fast->next){
        prev=slow;
        slow=slow->next;
        fast=fast->next->next;
    }
    prev->next=NULL;
    return slow;
}
struct ListNode* sortList(struct ListNode* head){
    if(head==NULL||head->next==NULL){
        return head;
    }
    struct ListNode* cur=Middle(head);//Get the intermediate node address
    head=sortList(head);//Put the first half in order
    cur=sortList(cur);//Put the second half in order
    return Mergedata(head,cur);//Merge the front and rear parts
}

 

After reading the above code and using recursion, you will find that this problem is actually the integration of merging two ordered linked lists, finding the middle node of the linked list and merging and sorting.

Complexity analysis

Time complexity: merge and sort. The time complexity is O(NlogN).

Spatial complexity: the recursion depth is logN, so the spatial complexity is O(logN).

non-recursive

Because the space complexity of the advanced problem is O(1), it is obvious that the above recursive code does not meet the requirements. Then we need to find a way to turn recursion into a loop.

thinking

First get the length len of the linked list, and then split the linked list into sub linked lists for merging.

The specific methods are as follows:

1. Use step to represent the length of the sub linked list to be sorted each time. Initially, step=1;

2. Each time, the linked list is divided into several sub linked lists with the length of step (the length of the last sub linked list can be less than step), and each two sub linked lists are combined in a group. After the combination, several ordered sub linked lists with the length of 2*step can be obtained (the length of the last sub linked list can be less than 2*step).

3. Double the value of step, repeat step 2, and merge the longer ordered sub linked list until the length of the ordered sub linked list is greater than or equal to len, and the whole linked list is sorted.

code

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* Mergedata(struct ListNode* head1,struct ListNode* head2){//Merge two ordered linked lists
    struct ListNode s;
    struct ListNode* cur=&s;
    while(head1&&head2){
        if(head1->val<=head2->val){
            cur->next=head1;
            head1=head1->next;
        }else{
            cur->next=head2;
            head2=head2->next;
        }
        cur=cur->next;
    }
    if(head1){
        cur->next=head1;
    }else{
        cur->next=head2;
    }
    return s.next;
}
struct ListNode* Disconnect(struct ListNode* head,int step){
    //Split the linked list according to the length of step and return the address of the remaining part after splitting
    if(head==NULL){
        return NULL;
    }
    struct ListNode* cur=head;
    for(int i=0;head&&i<step;i++){
        cur=head;
        head=head->next;
    }
    cur->next=NULL;
    return head;
}
int Length(struct ListNode* head){//Find the length of the linked list
    int count=0;
    while(head){
        head=head->next;
        count++;
    }
    return count;
}
struct ListNode* sortList(struct ListNode* head){
    struct ListNode s;//Given node, convenient tail insertion
    struct ListNode* cur=&s;//Point to the given header node
    cur->next=head;//First connect the given linked list, which must be done
    int len=Length(head);
    for(int step=1;step<len;step*=2){//Step < len, merge and sort continue
        head=s.next;//Update head
        cur=&s;//Update cur
        while(head){//Each step is grouped and merged until the head is empty
            struct ListNode* h1=head;//Get the first part after splitting
            struct ListNode* h2=Disconnect(h1,step);//Get the second part after splitting
            head=Disconnect(h2,step);//Get the rest after splitting
            cur->next=Mergedata(h1,h2);//Merge the first and second parts and insert them after s
            while(cur->next){//cur go ahead for the next tail insertion
                cur=cur->next;
            }
        }
    }
    return s.next;
}

Complexity analysis

Time complexity: merge and sort. The time complexity is O(NlogN).

Space complexity: no additional space is applied, and the space complexity is O(1).

Topics: data structure linked list