C++編寫(xiě)算法 (四)——排序問(wèn)題進(jìn)階漓帚,快速排序

快速排序母债,是應(yīng)用最廣泛的排序方法〕⒍叮快速排序流行的原因是它實(shí)現(xiàn)簡(jiǎn)單毡们、適用于各種不同的輸入數(shù)據(jù)且在一般應(yīng)用中比其他排序算法要快得多。 -------《算法》

一昧辽、快速排序

快速排序也是一種分治的思想衙熔。它需要將數(shù)組分為兩個(gè)、四個(gè)搅荞、八個(gè)等子數(shù)組红氯,通過(guò)遞歸的方法,對(duì)每個(gè)子數(shù)組進(jìn)行排序咕痛。因此痢甘,快速排序的第一個(gè)步驟就是需要找到一個(gè)能將數(shù)組劃分成子數(shù)組的分割點(diǎn)。分割點(diǎn)的產(chǎn)生與劃分茉贡,需要一個(gè)簡(jiǎn)單的分割算法(Partition)來(lái)實(shí)現(xiàn)塞栅。

分割算法的思想為:

①先在數(shù)組中找到一個(gè)幸運(yùn)數(shù)字(Lucky Number, LN)。
②將比LN小的數(shù)字(后統(tǒng)一稱(chēng)作小值)置于LN前腔丧,將比LN大的數(shù)字(后統(tǒng)一稱(chēng)作大值)置于LN之后放椰。
首先,為代碼簡(jiǎn)便愉粤,幸運(yùn)數(shù)字通忱剑可以選擇數(shù)組首個(gè)元素。確定幸運(yùn)數(shù)字后科汗,需要將小值全部移動(dòng)到LN前藻烤,將大值移動(dòng)到LN后。因此头滔,我們需要兩個(gè)index來(lái)對(duì)數(shù)組的元素進(jìn)行搜索怖亭。

具體步驟

Partition函數(shù)就是用來(lái)實(shí)現(xiàn)子數(shù)組的分割,需要注意的是坤检,它的工作只限于分割兴猩,而分割后的子數(shù)組有可能仍然處于亂序當(dāng)中。分割算法的具體實(shí)現(xiàn):需要將兩個(gè)index(我們?cè)诤竺娣Q(chēng)作哨兵i和j)分別指向數(shù)組的首部和尾部早歇,哨兵i從首部開(kāi)始搜尋比LN大的元素倾芝,哨兵j從尾部開(kāi)始搜尋比LN小的元素讨勤。哨兵i與j分別搜索到相應(yīng)的元素之后,將這兩個(gè)元素的位置進(jìn)行交換晨另。交換后潭千,哨兵i與哨兵j繼續(xù)進(jìn)行相應(yīng)的搜索。直到哨兵i與哨兵j相遇借尿,分割工作的第一步就完成了刨晴。此時(shí),我們選出的LN還位于數(shù)組的首位路翻,需要將LN放置到數(shù)組合適的位置中去狈癞,以完成Partition函數(shù)的分割工作。在哨兵i與哨兵j相遇后茂契,我們將LN與在哨兵j的位置的數(shù)進(jìn)行交換蝶桶,便可得到分割后的數(shù)組了。

Partition Function

 int Partition(int *arr, int start, int end)
{
    int i, j;
    i = start;
    j = end;
    int temp1, temp2;
    int LK_num = arr[i];
    while (true)
    {
        while (arr[i] <= LK_num)
        {
            i++;
            if (i > end)
                break;
        }
        while (arr[j] > LK_num)
        {
            j--;
            if (j < start)
                break;
        }
        if (i >= j)
            break;
        temp1 = arr[i];
        arr[i] = arr[j];
        arr[j] = temp1;
    }
    temp2 = LK_num;
    arr[start] = arr[j];
    arr[j] = LK_num;
    return j;
}

此外掉冶,分割函數(shù)中還存在一個(gè)問(wèn)題真竖。為了保持隨機(jī)性,我們將數(shù)組通過(guò)Shuffle函數(shù)進(jìn)行順序的打亂郭蕉。這樣就可以隨機(jī)的選擇一個(gè)元素進(jìn)行切分了疼邀。

Shuffle Function

void Shuffle(int *arr)
{
    srand(time(NULL));
    int temp;
    for (int i = 0; i < SIZE; i++)
    {
        int value = rand() % (SIZE);
        temp = arr[i];
        arr[i] = arr[value];
        arr[value] = temp;
    }
}

在這里喂江,也碰到了一個(gè)小問(wèn)題召锈。在使用rand()函數(shù)時(shí),每一次運(yùn)行程序后获询,數(shù)組的打亂順序基本一致涨岁。這個(gè)問(wèn)題在第二篇撲克牌排序的文章中也寫(xiě)到了。

rand()函數(shù)產(chǎn)生的隨機(jī)數(shù)時(shí)偽隨機(jī)數(shù)吉嚣,是計(jì)算機(jī)按照某種規(guī)律產(chǎn)生的數(shù)字梢薪。因此rand()產(chǎn)生的數(shù)字并不是真正意義上的隨機(jī)數(shù)。因此我們需要使用srand()函數(shù)來(lái)產(chǎn)生一個(gè)隨機(jī)數(shù)種子尝哆。

因此秉撇,通過(guò)使用Partition函數(shù),數(shù)組可達(dá)到部分有序秋泄,也被劃分為兩個(gè)子數(shù)組琐馆,通過(guò)對(duì)兩個(gè)子數(shù)繼續(xù)進(jìn)行規(guī)律的分割,最后實(shí)現(xiàn)排序恒序,快速排序如是而已瘦麸。

#include <iostream>
#include <vector>
#include<ctime>
const int SIZE = 15;
using namespace std;
void Shuffle(int *arr);
void SORT(int *arr, int lo, int hi);
//void SORT(int *arr, int begin, int end);
int Partition(int *arr, int start, int end);
int main()
{
    int *arr = new int[SIZE];
    for (int i = 0; i < SIZE; i++)
    {
        cout << "第" << i + 1 << "個(gè)數(shù)為:";
        cin >> arr[i];
    }
    cout << "數(shù)組為:";
    for (int j = 0; j < SIZE; j++)
    {
        cout << arr[j] << " ";
    }
    cout << endl;
    Shuffle(arr);
    SORT(arr, 0, SIZE-1);
    cout << "排序后的數(shù)組為: ";
    for (int i = 0; i < SIZE; i++)
        cout << arr[i] << " ";
    cout << endl;
    delete[] arr;
    system("pause");
    return 0;
}

void Shuffle(int *arr)
{
    srand(time(NULL));
    int temp;
    for (int i = 0; i < SIZE; i++)
    {
        int value = rand() % (SIZE);
        temp = arr[i];
        arr[i] = arr[value];
        arr[value] = temp;
    }
}

int Partition(int *arr, int start, int end)
{
    int i, j;
    i = start;
    j = end;
    int temp1, temp2;
    int LK_num = arr[i];
    while (true)
    {
        while (arr[i] <= LK_num)
        {
            i++;
            if (i > end)
                break;
        }
        while (arr[j] > LK_num)
        {
            j--;
            if (j < start)
                break;
        }
        if (i >= j)
            break;
        temp1 = arr[i];
        arr[i] = arr[j];
        arr[j] = temp1;
    }
    temp2 = LK_num;
    arr[start] = arr[j];
    arr[j] = LK_num;
    return j;
}

void SORT(int *arr, int lo, int hi)
{
    if (lo >= hi)
        return;
    int LK_located = Partition(arr, lo, hi);
    cout << endl;
    SORT(arr, LK_located + 1, hi);
    SORT(arr, lo, LK_located - 1);  
}

快速排序切分方法的內(nèi)循環(huán)會(huì)用一個(gè)遞增的索引將數(shù)組元素和一個(gè)定值比較。這種簡(jiǎn)潔性也是快速排序的一個(gè)優(yōu)點(diǎn)歧胁∽趟牵快速排序擁有最短小的內(nèi)循環(huán)厉碟。歸并排序和希爾排序一般都比快速排序要慢,其原因就是他們還在內(nèi)循環(huán)中移動(dòng)數(shù)據(jù)屠缭。

二箍鼓、快速排序算法的改進(jìn)

1、和之前提及的歸并排序相似呵曹,快速排序也是采用了遞歸的思想進(jìn)行排序袄秩,改進(jìn)這種遞歸排序的方法可以基于以下兩點(diǎn):
-對(duì)于分割的小數(shù)組,可以采用插入排序算法逢并。對(duì)于小型數(shù)組之剧,插入排序比快速排序快。
-因?yàn)榭焖倥判虿捎昧诉f歸的思想砍聊,在很小的數(shù)組中背稼,也會(huì)自己調(diào)用自己。
因此玻蝌,改進(jìn)的基本思想與歸并算法的思想是一致的:提前結(jié)束遞歸蟹肘,在小數(shù)組中采用更快的排序算法。

#include <iostream>
#include <vector>
#include<ctime>
const int SIZE = 16;
using namespace std;
void Shuffle(int *arr);
void SORT(int *arr, int lo, int hi);
void InsertSort(int * arr, int lo, int hi);
int Partition(int *arr, int start, int end);
int main()
{
    int *arr = new int[SIZE];
    for (int i = 0; i < SIZE; i++)
    {
        cout << "第" << i + 1 << "個(gè)數(shù)為:";
        cin >> arr[i];
    }
    cout << "數(shù)組為:";
    for (int j = 0; j < SIZE; j++)
    {
        cout << arr[j] << " ";
    }
    cout << endl;
    Shuffle(arr);
    SORT(arr, 0, SIZE-1);
    cout << "排序后的數(shù)組為: ";
    for (int i = 0; i < SIZE; i++)
        cout << arr[i] << " ";
    cout << endl;
    delete[] arr;
    system("pause");
    return 0;
}

void Shuffle(int *arr)
{
    srand(time(NULL));
    int temp;
    for (int i = 0; i < SIZE; i++)
    {
        int value = rand() % (SIZE);
        temp = arr[i];
        arr[i] = arr[value];
        arr[value] = temp;
    }
}

int Partition(int *arr, int start, int end)
{
    int i, j;
    i = start;
    j = end;
    int temp1, temp2;
    int LK_num = arr[i];
    while (true)
    {
        while (arr[i] <= LK_num)
        {
            i++;
            if (i > end)
                break;
        }
        while (arr[j] > LK_num)
        {
            j--;
            if (j < start)
                break;
        }
        if (i >= j)
            break;
        temp1 = arr[i];
        arr[i] = arr[j];
        arr[j] = temp1;
    }
    temp2 = LK_num;
    arr[start] = arr[j];
    arr[j] = LK_num;
    return j;
}

void SORT(int *arr, int lo, int hi)
{
    if (lo + 4 >= hi)   //提前結(jié)束遞歸俯树,采用插入排序
    {
        InsertSort(arr, lo, hi);
        return;
    }
    int LK_located = Partition(arr, lo, hi);
    cout << endl;
    SORT(arr, LK_located + 1, hi);
    SORT(arr, lo, LK_located - 1);  
}
//引入插入排序算法
void InsertSort(int *arr, int lo, int hi)
{
    int temp;
    for(int i = lo; i <= hi; i++)
    {
        for (int j = i; j > lo; j--)
        {
            if (arr[j] < arr[j - 1])
            {
                temp = arr[j];
                arr[j] = arr[j-1];
                arr[j-1] = temp;
            }
        }
    }
}

2帘腹、如果數(shù)組中存在較多相同元素時(shí),可以采用三向切分快速排序许饿,其思想是將比LN小的數(shù)字歸于左部分阳欲,比LN大的數(shù)字歸于右部分,中間部分的數(shù)是未確定部分陋率。三向切分快速排序需要三個(gè)指針來(lái)執(zhí)行操作球化。但總體的代碼量比傳統(tǒng)快速排序要小。

#include <iostream>
#include <vector>
#include<ctime>
const int SIZE = 8;
using namespace std;
void SORT(int *arr, int lo, int hi);
int main()
{
    int *arr = new int[SIZE];
    for (int i = 0; i < SIZE; i++)
    {
        cout << "第" << i + 1 << "個(gè)數(shù)為:";
        cin >> arr[i];
    }
    cout << "數(shù)組為:";
    for (int j = 0; j < SIZE; j++)
    {
        cout << arr[j] << " ";
    }
    cout << endl;
    SORT(arr, 0, SIZE-1);
    cout << "排序后的數(shù)組為: ";
    for (int i = 0; i < SIZE; i++)
        cout << arr[i] << " ";
    cout << endl;
    delete[] arr;
    system("pause");
    return 0;
}

void SORT(int *arr, int lo, int hi)
{
    int lt = lo;
    int gt = hi;
    int i = lo + 1;
    int v = arr[lo];
    int temp;
    if (lo >= hi)
        return;
    while (i <= hi)
    {
        if (arr[i] < v)
        {
            temp = arr[i];
            arr[i] = arr[lt];
            arr[lt] = temp;
            i++;
            lt++;
        }
        if (arr[i] > v)
        {
            temp = arr[i];
            arr[i] = arr[gt];
            arr[gt] = temp;
            gt--;
        }
        if (arr[i] == v)
        {
            i++;
        }
    }
    SORT(arr, lo, lt - 1);
    SORT(arr, gt + 1, hi);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瓦糟,一起剝皮案震驚了整個(gè)濱河市筒愚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌菩浙,老刑警劉巖巢掺,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異劲蜻,居然都是意外死亡陆淀,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)斋竞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)倔约,“玉大人,你說(shuō)我怎么就攤上這事坝初〗#” “怎么了钾军?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)绢要。 經(jīng)常有香客問(wèn)我吏恭,道長(zhǎng),這世上最難降的妖魔是什么重罪? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任樱哼,我火速辦了婚禮,結(jié)果婚禮上剿配,老公的妹妹穿的比我還像新娘搅幅。我一直安慰自己,他們只是感情好呼胚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布茄唐。 她就那樣靜靜地躺著,像睡著了一般蝇更。 火紅的嫁衣襯著肌膚如雪沪编。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,590評(píng)論 1 305
  • 那天年扩,我揣著相機(jī)與錄音蚁廓,去河邊找鬼。 笑死厨幻,一個(gè)胖子當(dāng)著我的面吹牛相嵌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播克胳,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼平绩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了漠另?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤跃赚,失蹤者是張志新(化名)和其女友劉穎笆搓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體纬傲,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡满败,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了叹括。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片算墨。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖汁雷,靈堂內(nèi)的尸體忽然破棺而出净嘀,到底是詐尸還是另有隱情报咳,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布挖藏,位于F島的核電站暑刃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏膜眠。R本人自食惡果不足惜岩臣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宵膨。 院中可真熱鬧架谎,春花似錦、人聲如沸辟躏。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸿脓。三九已至抑钟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間野哭,已是汗流浹背在塔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拨黔,地道東北人蛔溃。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像篱蝇,于是被迫代替她去往敵國(guó)和親贺待。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 概述 排序有內(nèi)部排序和外部排序零截,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序麸塞,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    蟻前閱讀 5,186評(píng)論 0 52
  • 概述:排序有內(nèi)部排序和外部排序涧衙,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進(jìn)行排序哪工,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,732評(píng)論 0 15
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個(gè)記錄插入到已排序好...
    依依玖玥閱讀 1,258評(píng)論 0 2
  • 今天是主題班會(huì)弧哎,班主任馬琳老師帶領(lǐng)我們總結(jié)了這次期中考試的成績(jī)雁比。這次成績(jī)有很多同學(xué)進(jìn)步很大,王雪潔同學(xué)撤嫩,王璽同學(xué)偎捎,...
    衛(wèi)校一五助產(chǎn)閱讀 262評(píng)論 0 0
  • 記得已逝詩(shī)人汪國(guó)真,有這么一首詩(shī),其中有一句是:最美的風(fēng)景茴她,在遠(yuǎn)方寻拂。當(dāng)時(shí),看到這句話時(shí)败京,心里立即就被觸動(dòng)了兜喻,就像琴...
    一泓夜雨閱讀 460評(píng)論 0 0