1. 程式人生 > ><泛> 多路快排

<泛> 多路快排

數字 打折 color main begin ont == 排序 lag

今天寫一個多路快排函數模板,與STL容器兼容的。

我們默認為升序排序

因為,STL容器均為逾尾容器,所以我們這裏采用的參數也是逾尾的參數

一、二路快排

基本思路

給你一個序列,先選擇一個數作為基數,我們要做的是把小於該基數的數字放於左側,大於該基數的數字放於右側,最後將此基數放於中間,形成新的序列,我們把左側序列和右側序列分別像之前那樣做,最後得到的序列即為順序序列。

過程演示

比如給你一個序列 4 3 1 5 4 7 9 1 8 0

我們采用雙指針掃描法,紅色代表左指針所指位置,綠色代表右指針所指位置

我們選取一個數作為基數,假設選第一個數,即 flag 為 4

那麽,我們把這個位置空出來,用null表示

則,一開始的序列圖為

null 3 1 5 4 7 9 1 8 0 flag is 4

我們從右指針開始遍歷,找到第一個小於flag的數字,填入坑中,然後左指針++,指向下一位然後開始遍歷

0 3 1 5 4 7 9 1 8 null

0 3 1 5 4 7 9 1 8 null

0 3 1 5 4 7 9 1 8 null

0 3 1 null 4 7 9 1 8 5

0 3 1 null 4 7 9 1

8 5

0 3 1 1 4 7 9 null 8 5

0 3 1 1 4 7 9 null 8 5

0 3 1 1 4 null 9 7 8 5

右指針指向9,9>flag,繼續左移,然後紅綠指針相遇,結束

我們把flag放入null,即形成了新的序列

0 3 1 1 4 4 9 7 8 5

我們把左側序列0 3 1 1 4

和右側序列9 7 8 5

分別如上做,即可得到有序序列

C++模板代碼:

template<typename value_type, typename value_Ptr>
void
quick_sort(value_Ptr begin, value_Ptr end) //逾尾 { if (begin == end)return; value_type flag = *begin; value_Ptr l = begin, r = end; r--; while (l < r) { while (l < r && *r > flag)r--; if (l < r)*(l++) = *r; while (l < r && *l < flag)l++; if (l < r)*(r--) = *l; } *l = flag; quick_sort<value_type>(begin, l); quick_sort<value_type>(l + 1, end); }

測試以及使用

#include <iostream>
#include <vector>
using namespace std;int main()
{
    ios::sync_with_stdio(false);

    int list[10]{ 22,3,1,5,4,7,9,1,8,0 };
    vector<int> v{ list,list + 10 };

    quick_sort<int>(list + 0, list + 10);
    quick_sort<int>(v.begin(), v.end());
    for (auto it : v)cout << it << " ";
    cout << endl;
    for (auto it : list)cout << it << " ";
    cout << endl;

    char list_[10]{ r,c,2,A,z,b,8,0,r, 3 };
    vector<char>v_{ list_,list_ + 10 };

    quick_sort<char>(list_ + 0, list_ + 10);
    quick_sort<char>(v_.begin(), v_.end());
    for (auto it : v_)cout << it << " ";
    cout << endl;
    for (auto it : list_)cout << it << " ";
    cout << endl;
}

測試結果

技術分享圖片

效率為O(N * logN)

如果序列中有很多重復的元素,那麽,那一長串的重復值就相當於一個非遞減序列,按理說是不需要排序的,但是上述無法識別此情況,它依舊會把這一長串不必處理的序列進行處理,無疑將函數執行效率大打折扣

這時,我們就需要引入三路快排

二、三路快排

基本思路

給你一個序列,我們選擇一個基數作為標準,小於基數的元素放於左側區間,等於基數的元素放於中間區間,大於基數的元素放於右側區間

整理好之後,我們將左側區間和右側區間如上做。

過程描述

我們用四指針掃描

l r 用於掃描待處理的序列

equ_l equ_r 用於記錄兩側相等的區間

equ_l = l = begin

requ_r指向右側

l r向中間遍歷,遇到不等於的情況和快排一樣,留一個空位,依次填補即可,遇到相等的情況,左指針指向的值等於基數,那麽就放於equ_l處,且equ_l++,右邊同理

l r 相遇時,我們讓四指針分別向兩端移動,equ_l 所指的元素依次和 l 所指的元素交換,右側同理,即可得到我們想要的序列,即 < = >

之後,我們將<區間和>區間分別如上做,即可得到有序序列

技術分享圖片

泛型模板代碼:

template<typename value_type, typename value_Ptr>
void Tri_quick(const value_Ptr& begin, const value_Ptr& end)
{
    static auto swap = [](value_type& l, value_type& r)
    {
        if (l == r)return;
        value_type t{ l };
        l = r;
        r = t;
    };
    value_Ptr equ_l = begin, equ_r = end, l = begin, r = end;
    if (begin >= end || begin >= end - 1)return;       

//上式:如果第二個條件成立的話,表達式1中的 ++l 就會解析 end 叠代器
value_type flag = *l; while (l < r) { while (l < r && *(++l) < flag)if (l == end - 1)break; // 表達式1 while (l < r && flag < *(--r)); if (*l != *r) swap(*l, *r); if (*l == flag) swap(*(++equ_l), *l); if (*r == flag) swap(*(--equ_r), *r); } if (*l > flag)l--;
//此時l==r,可能l的值大於flag,我們不換,但是前面的一定小於等於flag,所以我們--
while (equ_l > begin)swap(*(equ_l--), *(l--));
//選擇大於是因為equ_l如果為容器的begin叠代器,那麽不允許--操作
swap(*equ_l, *l);
//所以begin叠代器另做處理
while (equ_r < end)swap(*(equ_r++), *(r++)); Tri_quick<value_type>(begin, l); Tri_quick<value_type>(r, end); }

測試及使用

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    int list[10]{ 1,2,1,1,3,5,4,2,1,0 };
    vector<int> v{ list,list + 10 };
    Tri_quick<int>(list + 0, list + 10);
    for (auto it : list)cout << it << " ";
    cout << endl;
    Tri_quick<int>(v.begin(), v.end());
    for (auto it : v)cout << it << " ";
    cout << endl;
    
    char list_[10]{ r,c,2,A,z,b,8,0,r, 3 };
    vector<char>v_{ list_,list_ + 10 };

    Tri_quick<char>(list_ + 0, list_ + 10);
    Tri_quick<char>(v_.begin(), v_.end());
    for (auto it : v_)cout << it << " ";
    cout << endl;
    for (auto it : list_)cout << it << " ";
    cout << endl;
}

技術分享圖片

這就是今天的多路快排,關於快排有很多不適應的情況,也有很多優化的算法,關於其他的算法,大家可以去其他地方學習,這裏不做贅述

如果有什麽問題,請於下方評論區留言。

<泛> 多路快排