链表排序——归并排序、快速排序

版权声明:转载请注明出处!谢谢! https://blog.csdn.net/qq_28114615/article/details/86556277

目录

1 归并排序

1.1 实现思路

1.2 代码实现

2 快速排序

2.1 实现思路

2.2 代码实现


1 归并排序

归并排序基本原理https://blog.csdn.net/qq_28114615/article/details/86345717

1.1 实现思路

       与数组归并排序相同,主要分为以下三个步骤:

      ①找到链表中点。最常用的方法是双指针法;

      ②递归左半链表及右半链表。数组有很好的随机访问特点,因此传递索引来实现递归左子区间和右子区间;而在链表中,是顺 序访问的,但是链表的插入删除是很方便的,因此考虑直接将链表从中间“打断”,然后递归“打断”后的两个链表。

扫描二维码关注公众号,回复: 5090492 查看本文章

      ③合并链表。经②后的两个链表递归结束后必定都是有序的,因此这里就是对两个有序链表进行合并,可参考此处。另外一  种方法是递归合并,升序递归中,先找到两个链表中较小结点所在链表,将该链表剩下部分与另一个链表进行递归合并即可。

1.2 代码实现

ListNode* sortList(ListNode* head) {
        
        if(!head||head->next==NULL)return head;
        
        ListNode* slow=head;    //慢指针
        ListNode* fast=head;    //快指针
        
        while(fast->next&&fast->next->next)
        {
            slow=slow->next;
            fast=fast->next->next;
        }
        
        ListNode* breakNode=slow->next;  //断点 
        slow->next=NULL;  //打断结点
        
        ListNode* l=sortList(head); //递归左链表
        ListNode* r=sortList(breakNode); //递归右链表
        
        return mergeList(l,r);  //合并链表
        
    }
    
    ListNode* mergeList(ListNode* l1,ListNode* l2)
    {
        if(!l1)return l2;   
        if(!l2)return l1;
        

        if(l1->val<l2->val)
        {
            l1->next=mergeList(l1->next,l2);  //l1当前结点值较小,则递归合并剩下的部分与l2
            return l1;
        }
        else 
        {
            l2->next=mergeList(l2->next,l1); //l2当前结点值较小,则递归合并剩下的部分与l1
            return l2;
        }
        
        return NULL;    
        
    }

2 快速排序

2.1 实现思路

        链表快速排序思路依然分为以下几个步骤:

        ①选定基准结点。这个依然和数组快排相类似,只不过数组快排是找到一个数,而链表中是找到一个结点;

        ②将比基准结点值小的结点放在一边,比基准结点值大的放另一边,找到最终基准结点所在位置。在数组快排中,比较简单的方法是一个从前往后,一个从后往前走,而单链表没有前驱结点,因此可以考虑换一种思路:用双指针的方法,慢指针初始化为基准结点,快指针往前走,当快指针所处结点值小于基准结点值,那么就先将慢指针往前走一步,然后将慢指针所在结点值与快指针所在结点值交换,然后快指针继续往前走,碰到下一个比基准结点值小的,依然先让慢指针往前走一步,然后慢指针所处结点值与快指针所处结点值交换......这样一直到快指针遍历到了链表终点,基准结点与慢指针所处结点之间所有结点值(包括慢指针所在结点值)都是比基准结点值小的,因此此时将慢指针结点值与基准结点值交换,这样基准结点左边的所有结点值都比其小了;

        ③两边进行递归。数组快排时是以区间的范围进行递归的,而在链表中是无法索引的,因此可以直接以②中的基准结点为界限,划分出两个链表范围:基准结点作为左链表的终点结点,其下一个结点作为右链表的起点。

2.2 代码实现

ListNode* sortList(ListNode* head) {
        
        if(!head||head->next==NULL)return head;

        qSortList(head,NULL);  //起点结点为head,终点结点为null
        return head;
}
ListNode* GetPartion(ListNode* left,ListNode* right)
    {  
        if(left==right||left->next==right)return left;
        
        int key=left->val;     //基准结点值
        ListNode* slow=left;   //慢指针
        ListNode* fast=slow->next; //快指针
        
        while(fast!=right)  
        {
            if(fast->val<key)  //快指针所在结点值小于基准结点值
            {
                slow=slow->next; 
                swap(slow->val,fast->val);  //相当于把比基准结点值小的全部放在基准结点与慢指针所在结点之间
            }
            fast=fast->next;
        }
        swap(slow->val,left->val);  //最后将慢指针所在结点值与基准结点值交换,这样基准结点左边所有结点值都小于基准结点值
        return slow;
    }
    
    void qSortList(ListNode* sNode,ListNode* eNode)
    {
        if(sNode==eNode)return;
        ListNode* pNode=GetPartion(sNode,eNode);
        qSortList(sNode,pNode);  //以基准结点作为左子链表终点
        qSortList(pNode->next,eNode); //以基准结点作为右子链表起点
        
        return;
    }

猜你喜欢

转载自blog.csdn.net/qq_28114615/article/details/86556277