單鏈表快速排序


 快排不適合同於鏈表,但是可以實現,時間復雜度為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);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM