链表的排序-冒泡,简单选择,插入排序,归并,快排——以leetcode148. Sort List为例子

     对于数组的各种经典排序方法,请参看常用的7种排序算法——以leetcode 75. Sort Colors为例,该博客该出了7种方法分析。

一、题目

Sort a linked list in O(n log n) time using constant space complexity.

Example 1:

Input: 4->2->1->3
Output: 1->2->3->4

Example 2:

Input: -1->5->3->4->0
Output: -1->0->3->4->5

二、解答

本篇主要讲解链表的快速排序方法:冒泡,简单选择,插入排序,归并,快排。各种方法的主要代码如下:详见的注释见代码。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(head == nullptr || head->next == nullptr)
            return head;
        //方法一:冒泡排序:交换节点的值 算法交换链表节点val值,时间复杂度O(n^2),空间复杂度O(1)
        // bubble_sort(head);
        // return head;
        
        //方法二:简单选择排序 交换节点的值 算法中只是交换节点的val值,时间复杂度O(n^2),空间复杂度O(1)
        
        //return select_sort(head);
        
        //方法三:直接插入排序 算法中是直接交换节点,时间复杂度O(n^2),空间复杂度O(1)
        //return insert_sort(head);
        
        //方法四:归并排序  算法交换链表节点,时间复杂度O(nlogn),不考虑递归栈空间的话空间复杂度是O(1)
        return merge_sort(head);


        
        //方法x: 快排:交换节点的值 时间复杂度O(nlogn) 空间复杂度是O(logn)- o(n) 不考虑递归的话是O(1)
        //quick_sort(head, nullptr);
        //return head;


    }
   
    //METHOD4:the subfunction of merge sort1: merge sort  
    ListNode* merge_sort(ListNode* head)
    {
        //当每次拆分后的前半边(或者后半边)仅有一个元素,此时应该返回merge_sort()为其本身,也即是序列的首元素
        if(head->next == nullptr)
            return head;
        //首先找到中间节点的位置。利用fast slow 指针
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast->next != nullptr && fast->next->next != nullptr)
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        //slow指向了中间节点的元素
        fast = slow;//fast指向前半边序列的末尾
        slow = slow -> next;//slow指向后半边序列的开始
        fast -> next = nullptr;
        //divide
        fast  = merge_sort(head);//前半边,fast指的是前半边的序列的首元素,由merge_sort返回。
        slow = merge_sort(slow);//后半边,merge_sort()返回的是后面的半边序列的起始节点指针
        //agency
        return merge(fast, slow);
        
    }
    //METHOD4:the subfunction of merge sort2: merge 
    ListNode* merge(ListNode* head1, ListNode* head2)
    {
        //当head1或者head2为空时候,返回另外一个即可
        if(head1 == nullptr)
            return head2;
        else if(head2 == nullptr)
            return head1;
        ListNode* res, * p;//res表示最终结果的第一个节点指针,p是新的合并后指针,其保存最小的值的节点指针,要随着合并往后移动,head1 和head2都是要随着往后移动
        if(head1->val > head2->val)  
        {
            p = head2;//p保存最小值节点指针
            head2 = head2->next;//head2要往后移动
        }
        else 
        {
            p = head1;
            head1 = head1->next;            
        }
        
        res = p;
        while(head1 != nullptr && head2 != nullptr)
        {
            if(head1->val > head2->val)//若是head2小的话,head2要接上p,同时head2要往后移动
            {
                p->next  = head2;
                head2 = head2->next;//
            }
            else
            {
                p->next = head1;
                head1 = head1->next;
            }
            
            p = p->next;
            
        }
        if(head1 == nullptr)
            p->next = head2;
        else
            p->next = head1;
        
        return res;
            
    } 
  
    
    /*
    //METHOD1: the function of bubble sort
    void bubble_sort(ListNode* head)
    {
        ListNode* p = nullptr;
        bool flag = true;
        while(p != head->next && flag)//这里p会遍历所有的节点。p每次都舍弃一个节点,也就是每次都会向前移动一个节点,截止条件是不等于head->next
        {
            flag = false;
            ListNode* q = head;//q每次都从开始的head节点开始遍历,直到不等于p为止
            for( ; q->next && q->next != p; q = q->next)
            {
                if(q->val > q->next->val)//大的值往后移动
                {
                    swap(q->val, q->next->val);
                    flag = true;
                }
                    
            }
            p = q;//p每次都会舍弃掉最后一个节点
        }
    }
    
      //METHOD2:the function of select sort
    ListNode* select_sort(ListNode* head)
    {
        ListNode* dump = new ListNode(0);
        dump->next = head;
        ListNode* sortedTail = dump;//排好序的末尾
        while(sortedTail ->next != nullptr)
        {
             ListNode* minNode = sortedTail->next;
             ListNode* p = sortedTail->next->next;
             while(p != nullptr)
             {
                 if(p->val < minNode->val)
                     minNode = p;
                 p = p->next;
             }
            if(sortedTail ->next != minNode)
                swap(sortedTail->next->val, minNode->val);
            sortedTail = sortedTail->next;


        }
        return dump->next;
        
    }
    
    //METHOD3:the  function of insert sort
    ListNode* insert_sort(ListNode* head)
    {
        ListNode* dump = new ListNode(0);
        dump->next = head;
        ListNode* sortedTail = dump->next;//每次的末尾元素,首先是赋值为head
        ListNode* p = dump->next->next; //p首先指向head->next
        while(p != nullptr)
        {
            ListNode* tmp = dump->next;
            ListNode* pre = dump;//每一趟插入排序,tmp都从开始dump->next出发,直至到tmp=p或者找到某个tmp->val <= p->val(也就是找到插入位置)
            while(tmp != p && tmp->val <= p->val)
            {
                tmp = tmp->next;
                pre = pre->next;
            }
            if(tmp == p)//若到了tmp==p.说明p之前的元素都是有序的,不用处理,此时sortedTail = p;
                sortedTail = p;
            else//交换节点
            {
                sortedTail ->next = p->next;
                p->next = tmp;
                pre->next = p;
            }
            p = sortedTail->next;//sortedTail
            
            
        }
        return dump->next;
        
        
    }
    
   
    
    //METHOD4:the subfunction of merge sort1: merge sort
    ListNode* merge_sort(ListNode* head)
    {
        //当每次拆分后的前半边(或者后半边)仅有一个元素,此时应该返回merge_sort()为其本身,也即是序列的首元素
        if(head->next == nullptr)
            return head;
        //首先找到中间节点的位置。利用fast slow 指针
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast->next != nullptr && fast->next->next != nullptr)
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        //slow指向了中间节点的元素
        fast = slow;//fast指向前半边序列的末尾
        slow = slow -> next;//slow指向后半边序列的开始
        fast -> next = nullptr;
        //divide
        fast  = merge_sort(head);//前半边,fast指的是前半边的序列的首元素,由merge_sort返回。
        slow = merge_sort(slow);//后半边,merge_sort()返回的是后面的半边序列的起始节点指针
        //agency
        return merge(fast, slow);
        
    }
    //METHOD4:the subfunction of merge sort2: merge 
    ListNode* merge(ListNode* head1, ListNode* head2)
    {
        //当head1或者head2为空时候,返回另外一个即可
        if(head1 == nullptr)
            return head2;
        else if(head2 == nullptr)
            return head1;
        ListNode* res, * p;//res表示最终结果的第一个节点指针,p是新的合并后指针,其保存最小的值的节点指针,要随着合并往后移动,head1 和head2都是要随着往后移动
        if(head1->val > head2->val)  
        {
            p = head2;//p保存最小值节点指针
            head2 = head2->next;//head2要往后移动
        }
        else 
        {
            p = head1;
            head1 = head1->next;            
        }
        
        res = p;
        while(head1 != nullptr && head2 != nullptr)
        {
            if(head1->val > head2->val)//若是head2小的话,head2要接上p,同时head2要往后移动
            {
                p->next  = head2;
                head2 = head2->next;//
            }
            else
            {
                p->next = head1;
                head1 = head1->next;
            }
            
            p = p->next;
            
        }
        if(head1 == nullptr)
            p->next = head2;
        else
            p->next = head1;
        
        return res;
            
    } 
    
     //METHODx: the subfunction of quick sort 1: quick sort
    // 与数组的快排相比,这里是[low, high) 前闭后开,不包括后面的tail
    void quick_sort(ListNode* head, ListNode* tail)
    {
        ListNode * pivot;
        if(head != tail && head->next != tail)
        {
            pivot = findPivot(head, tail);
            quick_sort(head, pivot);
            quick_sort(pivot->next, tail);
            
        }
    }
    
    //METHODx: the subfunction of quick sort 2: quick sort find pivot
    ListNode* findPivot(ListNode* low, ListNode* high)
    {
        int key = low -> val;
        ListNode* loc = low;
        for(ListNode* i = low->next; i != high; i = i->next)//这里是 != , 因为[low, high)前闭后开。 这里是所有小于pivot的元素放到最前面
        {
            
            if(i->val < key)
            {
                loc = loc -> next;
                swap(i->val, loc->val);
            }
                
        }
        swap(low->val, loc->val);//piovt放到所有小于pivot的元素段的末尾 pivot就是这里的key
        return loc;
        
    }
   */
};

以上。

推荐使用归并排序,原来归并排序的数组的空间复杂度O(n),而在链表上并不用开辟空间存储元素,因而空间复杂度是O(1),所以链表排序推荐归并排序,时间复杂度O(nlogn),空间复杂度O(1)。

参考:https://www.cnblogs.com/TenosDoIt/p/3666585.html

猜你喜欢

转载自blog.csdn.net/u012426298/article/details/80448622
今日推荐