編程實(shí)現(xiàn)希爾太示、快速、堆排序、歸并排序算法。要求首先隨機(jī)產(chǎn)生10000個(gè)數(shù)據(jù)存入磁盤文件,然后讀入數(shù)據(jù)文件,分別采用不同的排序算法進(jìn)行排序并將結(jié)果存入文件中。

算法分析

希爾排序

對(duì)于大規(guī)模亂序數(shù)組插入排序很慢,效率較低皂林,例如最小數(shù)在數(shù)組最右胎挎,要將其挪到正確位置就要N-1次移動(dòng)美浦。希爾排序?yàn)榱思涌焖俣群?jiǎn)單的改進(jìn)了插入排序沼沈,交換不相鄰的元素以對(duì)數(shù)組的局部進(jìn)行排序,并最終用插入排序?qū)⒕植坑行虻臄?shù)組排序。

步驟
image.png

1.使用元素總數(shù)量的一半5作為增量眨八,將元素劃分為五個(gè)序列,對(duì)這五個(gè)序列分別進(jìn)行排序;


image.png

2.使用5/2=2作為增量互订,將元素劃分成兩個(gè)序列分別排序参滴;


image.png

3.最后使用插入排序妓盲,將元素排序,結(jié)果為:
image.png
優(yōu)劣:
  • 不需要大量的輔助空間,和歸并排序一樣容易實(shí)現(xiàn);
  • 希爾排序的時(shí)間復(fù)雜度與增量序列的選取有關(guān)瘸味,例如希爾增量時(shí)間復(fù)雜度為O(n2),而Hibbard增量的希爾排序的時(shí)間復(fù)雜度為O(n3/2),希爾排序時(shí)間復(fù)雜度的下界是n*log2n;
  • 希爾排序沒有快速排序算法快 O(n*logn)够挂,因此中等大小規(guī)模表現(xiàn)良好旁仿,對(duì)規(guī)模非常大的數(shù)據(jù)排序不是最優(yōu)選擇;
  • 希爾算法在最壞的情況下和平均情況下執(zhí)行效率相差不是很多,而快速排序在最壞的情況下執(zhí)行的效率會(huì)非常差孽糖。

快速排序(挖坑填數(shù)+分治法)

快速排序(Quick Sort)是對(duì)冒泡排序的一種改進(jìn)枯冈,基本思想是選取一個(gè)記錄作為樞軸,經(jīng)過一趟排序办悟,將整段序列分為兩個(gè)部分尘奏,其中一部分的值都小于樞軸,另一部分都大于樞軸病蛉。然后繼續(xù)對(duì)這兩部分繼續(xù)進(jìn)行排序炫加,從而使整個(gè)序列達(dá)到有序。
快速排序之所比較快铺然,因?yàn)橄啾让芭菖判蛩仔ⅲ看谓粨Q是跳躍式的。每次排序的時(shí)候設(shè)置一個(gè)基準(zhǔn)點(diǎn)魄健,將小于等于基準(zhǔn)點(diǎn)的數(shù)全部放到基準(zhǔn)點(diǎn)的左邊赋铝,將大于等于基準(zhǔn)點(diǎn)的數(shù)全部放到基準(zhǔn)點(diǎn)的右邊。這樣在每次交換的時(shí)候就不會(huì)像冒泡排序一樣每次只能在相鄰的數(shù)之間進(jìn)行交換沽瘦,交換的距離就大的多了革骨。因此總的比較和交換次數(shù)就少了农尖,速度自然就提高了。當(dāng)然在最壞的情況下良哲,仍可能是相鄰的兩個(gè)數(shù)進(jìn)行了交換盛卡。因此快速排序的最差時(shí)間復(fù)雜度和冒泡排序是一樣的都是O(n2),它的平均時(shí)間復(fù)雜度為O(n*logn)筑凫。

步驟
image.png

image.png

堆排序

作為選擇排序的改進(jìn)版窟扑,堆排序可以把每一趟元素的比較結(jié)果保存下來(lái),以便我們?cè)谶x擇最小/大元素時(shí)對(duì)已經(jīng)比較過的元素做出相應(yīng)的調(diào)整漏健。
堆排序是一種樹形選擇排序,在排序過程中可以把元素看成是一顆完全二叉樹橘霎,每個(gè)節(jié)點(diǎn)都大(心杞)于它的兩個(gè)子節(jié)點(diǎn),當(dāng)每個(gè)節(jié)點(diǎn)都大于等于它的兩個(gè)子節(jié)點(diǎn)時(shí)姐叁,就稱為大頂堆瓦盛,也叫堆有序; 當(dāng)每個(gè)節(jié)點(diǎn)都小于等于它的兩個(gè)子節(jié)點(diǎn)時(shí)外潜,就稱為小頂堆原环。

算法思想(以大頂堆為例):

1.將長(zhǎng)度為n的待排序的數(shù)組進(jìn)行堆有序化構(gòu)造成一個(gè)大頂堆;
2.將根節(jié)點(diǎn)與尾節(jié)點(diǎn)交換并輸出此時(shí)的尾節(jié)點(diǎn)处窥;
4.重復(fù)第二嘱吗、三步直至構(gòu)造成一個(gè)有序序列。


image.png

image.png

image.png

歸并排序

歸并排序算法有兩個(gè)基本的操作滔驾,一個(gè)是分谒麦,也就是把原數(shù)組劃分成兩個(gè)子數(shù)組的過程。另一個(gè)是治哆致,它將兩個(gè)有序數(shù)組合并成一個(gè)更大的有序數(shù)組绕德。
它將數(shù)組平均分成兩部分: center=(left+right)/2,當(dāng)數(shù)組分得足夠小時(shí)即數(shù)組中只有一個(gè)元素時(shí)摊阀,只有一個(gè)元素的數(shù)組自然而然地就可以視為是有序的耻蛇,此時(shí)就可以進(jìn)行合并操作了。因此胞此,上面講的合并兩個(gè)有序的子數(shù)組臣咖,是從只有一個(gè)元素的兩個(gè)子數(shù)組開始合并的,合并后的元素個(gè)數(shù):從 1->2->4->8……
由歸并排序的遞歸公式:T(n)=2T(n/2)+O(n)可知時(shí)間復(fù)雜度為O(n*logn)豌鹤。
此外亡哄,歸并排序中的比較次數(shù)是所有排序中最少的。原因是布疙,它一開始是不斷地劃分蚊惯,比較只發(fā)生在合并各個(gè)有序的子數(shù)組時(shí)愿卸。

步驟
image.png

程序代碼

#pragma once

#include <time.h>
#include <iostream>
#include <fstream>
using namespace std;

#ifndef _SORT_H
#define _SORT_H

struct Record
{
    int key;
};
#endif


//產(chǎn)生隨機(jī)數(shù)
Record* GenerateData(Record r[], int n)
{
    srand((unsigned)time(NULL));
    for(int i = 0; i < n; i++)
    {
        r[i].key = rand() % n;    //生成n個(gè)取值為[0,n-1]的整數(shù)
    }
    return r;
}

//寫入文件
Record* CreateFile(Record r[], int n, const char* filename)
{
    fstream fout(filename, ios::out);
    if (!fout)
    {
        cout << "無(wú)法創(chuàng)建文件!" << endl;
        exit(1);
    }
    else
    {
        for (int i = 0; i < n; i++)
        {
            fout << r[i].key << " ";
            if ((i + 1) % 15 == 0)
                fout << endl;// '\n';
        }
    }
    fout.close();
    return r;
}

//讀取文件
Record* ReadFile(int n, const char* filename)
{
    fstream fin(filename, ios::in);
    Record* r = new Record[n];

    if (!fin)
    {
        cout << "無(wú)法打開文件截型!" << endl;
        exit(1);
    }
    else
    {
        for (int i = 0; i < n; i++)
            fin >> r[i].key;
    }
    fin.close();
    return r;
}
#include <iostream>
#include <fstream>
#include "Sort.h"
#include "File.h"
#define N 10000
using namespace std;

int Menu();
void Procedure();

int Menu()
{
    cout << "__________*******************************************************__________\n\n";
    cout << "            1.產(chǎn)生隨機(jī)數(shù)作為待排序的數(shù)據(jù)趴荸,并將其存入磁盤文件中\(zhòng)n";
    cout << "            2.執(zhí)行【希爾排序】算法,將排序結(jié)果存入文件\n";
    cout << "            3.執(zhí)行【快速排序】算法宦焦,將排序結(jié)果存入文件\n";
    cout << "            4.執(zhí)行【堆排序】算法发钝,  將排序結(jié)果存入文件\n";
    cout << "            5.執(zhí)行【歸并排序】算法,將排序結(jié)果存入文件\n";
    cout << "            0.退出操作\n\n";
    cout << "__________*******************************************************__________\n\n";
    cout << "請(qǐng)依次輸入以上5個(gè)步驟的代號(hào): ";
    Sleep(2000);
    int choice;
    cin >> choice;
    cout << endl;
    return choice;
}

void Procedure()
{
    while (1)
    {
        int choice = Menu();
        if (choice == 0) 
            break;
        switch (choice)
        {
        case 1:
        {
            cout << "提示:本程序中待排序數(shù)據(jù)的個(gè)數(shù)為" << N << endl;
            Record* r = new Record[N];
            GenerateData(r, N);
            Sleep(1000);
            CreateFile(r, N, "隨機(jī)數(shù).txt");
            Sleep(1000);
            cout << "數(shù)據(jù)已存入【隨機(jī)數(shù).txt】文件\n\n";
            Sleep(1500);
            break;
        }
        case 2:
        {
            Record* rec = ReadFile(N, "隨機(jī)數(shù).txt");
            Sleep(1000);
            ShellSort(rec, N);
            cout << "對(duì)待排序數(shù)據(jù)進(jìn)行希爾排序\n\n";
            Sleep(1000);
            CreateFile(rec, N, "希爾排序.txt");
            cout << "排序的結(jié)果已存入【希爾排序.txt】文檔\n\n";
            Sleep(1500);
            break;
        }
        case 3:
        {
            Record *rec = ReadFile(N, "隨機(jī)數(shù).txt");
            Sleep(1000);
            QuickSort(rec, 0, N - 1); 
            cout << "對(duì)待排序數(shù)據(jù)用快速排序方法進(jìn)行排序\n\n";
            Sleep(1000);
            CreateFile(rec, N, "快速排序.txt");
            cout << "排序的結(jié)果已存入【快速排序.txt】文檔\n\n";
            Sleep(1500);
            break;
        }
        case 4:
        {
            Record* rec = ReadFile(N, "隨機(jī)數(shù).txt");
            Sleep(1000);
            HeapSort(rec, N);
            cout << "對(duì)待排序數(shù)據(jù)用堆排序方法進(jìn)行排序\n\n";
            Sleep(1000);
            CreateFile(rec, N, "堆排序.txt");
            cout << "排序的結(jié)果已存入【堆排序.txt】文檔\n\n";
            Sleep(1500);
            break;
        }
        case 5:
        {
            Record* rec = ReadFile(N, "隨機(jī)數(shù).txt");
            Sleep(1000);
            MergeSort(rec, N);
            cout << "對(duì)待排序數(shù)據(jù)用歸并排序方法進(jìn)行排序\n\n";
            Sleep(1000);
            CreateFile(rec, N, "歸并排序.txt");
            cout << "排序的結(jié)果已存入【歸并排序】.txt文檔\n\n";
            Sleep(1500);
            break;
        }
        default:
            cout << "輸入錯(cuò)誤波闹!\n\n";
        }
    }
}

int main()
{
    Procedure();
    return 0;
}
#pragma once
#include <iostream>
#include <time.h>
#include <windows.h>
using namespace std;

#ifndef _SORT_H
#define _SORT_H

struct Record
{
    int key;
};
#endif

//希爾排序算法
void ShellSort(Record r[], int n)
{
    for (int d = n / 2; d >= 1; d = d / 2)
    {
        for (int i = d; i < n; i++)
        {
            Record temp = r[i];
            int j;

            for (j = i - d; j >= 0 && temp.key < r[j].key; j = j - d)
                r[j + d] = r[j];
            r[j + d] = temp;
        }
    }
}

//快速排序的一次劃分算法           
int Partition(Record r[], int i, int j)
{
    Record temp = r[i];
    while (i < j)
    {
        while (i < j && r[j].key >= temp.key)
            j--;
        if (i < j)
            r[i++] = r[j];
        while (i < j && r[i].key <= temp.key)
            i++;
        if (i < j)
            r[j--] = r[i];
    }
    r[i] = temp;
    return i;
}

//快速排序算法 
void QuickSort(Record r[], int i, int j)
{
    if (i < j)
    {
        int pivot = Partition(r, i, j);
        QuickSort(r, i, pivot - 1);
        QuickSort(r, pivot + 1, j);
    }
}

//堆排序中的篩選算法
void Sift(Record r[], int k, int m)
{
    int i = k;
    int j = 2 * i + 1;                      //置i為要篩的結(jié)點(diǎn)酝豪,j為i的左孩子

    while (j <= m)                          //篩選還沒進(jìn)行到葉子
    {
        if (j < m && r[j].key < r[j + 1].key)
            j++;                           //比較i的左右孩子,j為較大者
        if (r[i].key > r[j].key)
            break;                         //根結(jié)點(diǎn)已經(jīng)大于左右孩子中的較大者
        else
        {
            Record temp = r[i];
            r[i] = r[j];
            r[j] = temp;
            i = j;
            j = i * 2 + 1;                 //被篩選點(diǎn)位于原來(lái)結(jié)點(diǎn)j的位置
        }
    }
}

//堆排序算法
void HeapSort(Record r[], int n)
{
    for (int i = n / 2 - 1; i >= 0; i--)    //初始建堆精堕,從最后一個(gè)非終端結(jié)點(diǎn)至根結(jié)點(diǎn)進(jìn)行篩選
        Sift(r, i, n - 1);
    for (int i = 1; i < n; i++)            //重復(fù)執(zhí)行移走堆頂及重建堆的操作
    {
        Record temp = r[0];
        r[0] = r[n - i];
        r[n - i] = temp;
        Sift(r, 0, n - i - 1);
    }
}

//一次歸并算法
void Merge(Record r[], Record r1[], int s, int m, int t)
{
    int i = s;
    int j = m + 1;
    int k = s;

    while (i <= m && j <= t)
        if (r[i].key <= r[j].key)
            r1[k++] = r[i++];              //取r[i]和r[j]中較小者放入r1[k]
        else
            r1[k++] = r[j++];
    if (i <= m)
        while (i <= m)
            r1[k++] = r[i++];                 //若第一個(gè)子序列沒處理完孵淘,則進(jìn)行收尾處理
    else
        while (j <= t)
            r1[k++] = r[j++];             //若第二個(gè)子序列沒處理完,則進(jìn)行收尾處理
}

//一趟歸并排序算法
void MergePass(Record r[], Record r1[], int n, int h)
{
    int i = 0;

    while (i <= n - 2 * h + 1)                     //待歸并記錄至少有兩個(gè)長(zhǎng)度為h的子序列
    {
        Merge(r, r1, i, i + h - 1, i + 2 * h - 1);
        i += 2 * h;
    }
    if (i < n - h + 1)
        Merge(r, r1, i, i + h - 1, n);        //待歸并序列中有一個(gè)長(zhǎng)度小于h
    else
        for (int k = i; k <= n; k++)
            r1[k] = r[k];                //待歸并序列中只剩下一個(gè)子序列
}

//歸并排序的非遞歸算法
void MergeSort(Record r[], int n)
{
    int h = 1;
    Record* r1 = new Record[n];

    while (h < n)
    {
        MergePass(r, r1, n - 1, h);
        h = 2 * h;
        MergePass(r1, r, n - 1, h);
        h = 2 * h;
    }
}

經(jīng)驗(yàn)總結(jié)

image.png
  • 從平均時(shí)間來(lái)看歹篓,快速排序是效率最高的瘫证,但快速排序在最壞情況下的時(shí)間性能不如堆排序和歸并排序。而后者相比較的結(jié)果是庄撮,在n較大時(shí)歸并排序使用時(shí)間較少背捌,但使用輔助空間較多;
  • 上面說(shuō)的簡(jiǎn)單排序包括除希爾排序之外的所有冒泡排序洞斯、插入排序毡庆、簡(jiǎn)單選擇排序。其中直接插入排序最簡(jiǎn)單烙如,但序列基本有序或者n較小時(shí)扭仁,直接插入排序是好的方法,因此常將它和其他的排序方法厅翔,如快速排序乖坠、歸并排序等結(jié)合在一起使用;
  • 快速排序刀闷、堆排序熊泵、希爾排序等時(shí)間性能較好的排序方法都是不穩(wěn)定的。穩(wěn)定性需要根據(jù)具體需求選擇甸昏;
  • 上面的算法實(shí)現(xiàn)大多數(shù)是使用線性存儲(chǔ)結(jié)構(gòu)顽分,像插入排序這種算法用鏈表實(shí)現(xiàn)更好,省去了移動(dòng)元素的時(shí)間施蜜。具體的存儲(chǔ)結(jié)構(gòu)在具體的實(shí)現(xiàn)版本中也是不同的卒蘸。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子缸沃,更是在濱河造成了極大的恐慌恰起,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趾牧,死亡現(xiàn)場(chǎng)離奇詭異检盼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)翘单,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門吨枉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人哄芜,你說(shuō)我怎么就攤上這事貌亭。” “怎么了认臊?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵属提,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我美尸,道長(zhǎng),這世上最難降的妖魔是什么斟薇? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任师坎,我火速辦了婚禮,結(jié)果婚禮上堪滨,老公的妹妹穿的比我還像新娘胯陋。我一直安慰自己,他們只是感情好袱箱,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布遏乔。 她就那樣靜靜地躺著,像睡著了一般发笔。 火紅的嫁衣襯著肌膚如雪盟萨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天了讨,我揣著相機(jī)與錄音捻激,去河邊找鬼。 笑死前计,一個(gè)胖子當(dāng)著我的面吹牛胞谭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播男杈,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼丈屹,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了伶棒?” 一聲冷哼從身側(cè)響起旺垒,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤彩库,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后袖牙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侧巨,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年鞭达,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了司忱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡畴蹭,死狀恐怖坦仍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叨襟,我是刑警寧澤繁扎,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站糊闽,受9級(jí)特大地震影響梳玫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜右犹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一提澎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧念链,春花似錦盼忌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至君编,卻和暖如春跨嘉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吃嘿。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工偿荷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唠椭。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓跳纳,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親贪嫂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子寺庄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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