zoukankan      html  css  js  c++  java
  • 单链表快速排序

     快排不适合同于链表,但是可以实现,时间复杂度为o(nlgn)

    平均时间复杂度O(nlogn),不考虑递归栈空间的话空间复杂度是O(1))

    分析:由于单链表是没有prev指针的,所以跟数组一样的low,high指针就不适合单链表

    方法一:不移动元素节点本身,只移动元素的值                                  //注意必须以第一个元素为基准中枢值,

    1)定义两个指针pslow,pfast,其中pslow指向单链表的头结点,pfast指向单链表头结点的下一个结点;

    2)pslow代表的是比基准节点值小的链表的尾节点,此链表的所有的节点,都比基准的值小,pslow是尾节点

    3)pslow之后的节点都是比基准节点值大的节点

    4)用pfast用于遍历单链表,如果发现此时pfast节点的值pfast->value   < pivot,则要把pslow=pslow->next,这就此时的pslow就是大于基准节点的节点了,swap(pslow,pfast),这样小于基准节点的节点,就在pslow中,由于原理pslow的值是大于基准节点值的,所以被swap之后,还是大于基准节点

    5)如此遍历,知道pfast到达pend

    6)此时,除了基准节点在第一个位置之外,后面的就是按小于基准节点,大于基准节点的两大部分,可以通过swap[pviot,pslow],让基准节点回到中间位置

    ListNode* GetPartition(ListNode* head,ListNode* Begin,ListNode*End);
    
    ListNode* qucik_sort(ListNode*head,ListNode* Begin,ListNode*End){
        if(Begin!=End){                //是[)的区间,最后一个元素相当于end()
            ListNode* temp=GetPartition(head,Begin,End);   
            qucik_sort(head,Begin,temp);
            qucik_sort(head,temp->m_pNext,End);
        }
        return head;
    }
    ListNode* GetPartition(ListNode* head,ListNode* Begin,ListNode*End){
        int pivot=Begin->m_nValue;
        ListNode*pslow=Begin;
        ListNode*pfast=pslow->m_pNext;
        while(pfast!=End){
            if(pfast->m_nValue<pivot){
                pslow=pslow->m_pNext;
                std::swap(pslow->m_nValue,pfast->m_nValue);
            }
            pfast=pfast->m_pNext;
        }
        std::swap(pslow->m_nValue,Begin->m_nValue);
        return pslow;
    }

    qucik_sort(head,head,NULL);

     你可能回诧异,怎么会是快排?快排不是需要一个指针指向头,一个指针指向尾,然后两个指针相向运动并按一定规律交换值,最后找到一个支点使得支点左边小于支点,支点右边大于支点吗(这句话很长,累死俺了)?是滴,木有错,不过问题出来了。如果是这样的话,对于单链表我们没有前驱指针,怎么能使得后面的那个指针往前移动呢?所以这种快排思路行不通滴,如果我们能使两个指针都往next方向移动并且能找到支点那就好了。怎么做呢?

    接下来我们使用快排的另一种思路来解答。我们只需要两个指针pslow和pfast,这两个指针均往next方向移动,移动的过程中保持(pivot,pslow]之前的key都小于选定的key,(pslow,pfast]之间的key都大于选定的key,那么当pfast走到末尾的时候便完成了一次支点的寻找。当所指元素比枢轴小时,将Slow往前游一格,交换Slow和Fast所指元素的值,这样仍能保证Slow指向的元素是小于基准中的最后一个元素。

    快排最核心的思想就是划分,确定一个枢轴元素(pivot),每一趟划分的目的就是把待排序列分为两部分,前一部分比枢轴小(序列A),后一部分比枢轴大(序列B)。经过一趟划分之后序列变为:{A} pivot {B}。以下是具体步骤:
    1、确定每一次划分的枢轴元素为当前待排序列的头节点。
    2、设置Slow和Fast两个游标,Slow指向序列A中的最后一个元素,初始化为枢轴本身(待排序列头节点)。让Fast遍历一遍待排序列,当所指元素比枢轴小时,将Slow往前游一格,交换Slow和Fast所指元素的值,这样仍能保证Slow指向的元素是序列A中的最后一个元素。
    3、交换Slow所指元素和枢轴元素的值。
    4、对序列A和B重复步骤1~4。

    end为大区最后一个标识位;
    slow应该为大小区分界值,并且属于小区的;
    fast应该更名为遍历游标;
    Listhead为快排比较的中枢值;
    最后交换slow和ListHead,标识第一次遍历比较排序结束,
    开始对剩下的小区和大区值分别递归遍历比较排序;
    直到最后两个比较排序移动节点key值为最终排序,最后栈式输出为最终排序结果。

    struct Node 
    {
        int key;
        Node* next;
        Node(int nKey, Node* pNext)
            : key(nKey)
            , next(pNext)
        {}
    };
    
    
    Node* GetPartion(Node* pBegin, Node* pEnd)
    {
        int key = pBegin->key;
        Node* p = pBegin;
        Node* q = p->next;
    
        while(q != pEnd)
        {
            if(q->key < key)
            {
                p = p->next;
                swap(p->key,q->key);
            }
    
            q = q->next;
        }
        swap(p->key,pBegin->key);
        return p;
    }
    
    void QuickSort(Node* pBeign, Node* pEnd)
    {
        if(pBeign != pEnd)
        {
            Node* partion = GetPartion(pBeign,pEnd);
            QuickSort(pBeign,partion);
            QuickSort(partion->next,pEnd);
        }
    }
  • 相关阅读:
    【转载】Oracle实例和Oracle数据库(Oracle体系结构)
    【转载】Oracle 11g R2 for Win7旗舰版(64位)- 安装
    eclipse 中卸载插件的方法
    eclipse编辑jsp文件和javascript代码很卡解决办法
    ExtJS登陆页面涉及到的几个问题
    一个关于ExtJS4具体控件的详细教程
    ES6与React中this完全解惑
    for, for..in, in, for...of的区别
    Sublime Text3的react代码校验插件
    Sublime Text3中JSX支持Emmet快捷键
  • 原文地址:https://www.cnblogs.com/kkshaq/p/4518187.html
Copyright © 2011-2022 走看看