八大排序總結(jié)

不同的場景使用不同的排序算法本身沒有好壞之分。

(Ps:所有算法均自己測試通過沒有bug,如果小伙伴們有疑問請隨時留言提問~~~)
當(dāng)面試官問上面我說的那個問題的時候要首先考慮清楚面試官要干什么者祖?比如:
可以反問面試官:

  • 有沒有大量的重復(fù)值盅安?-->計數(shù)排序
  • 是否大部分?jǐn)?shù)據(jù)距離他們 的正確位置很近?是否近乎有序淋纲?-->插入排序
  • 是否數(shù)據(jù)的取值范圍非常有限?比如學(xué)生成績排序。-->計數(shù)排序
  • 是否需要穩(wěn)定鳞陨?-->優(yōu)先考慮歸并
  • 是否使用鏈表存儲?-->鏈表歸并排序或者鏈表快速排序(關(guān)于鏈表的排序近期會有更新)
  • 數(shù)據(jù)的大小是否可以裝載到內(nèi)存中瞻惋?-->外排序
/**
 *  冒泡排序:
 *
 *  時間復(fù)雜度:
 *      O(n)^2
 *  空間復(fù)雜度:
 *      O(1)
 *  穩(wěn)定性:
 *      穩(wěn)定
 *
 *  以升序情況下為例:
 *  1.首先指針指向數(shù)組中的第一個元素厦滤,讓當(dāng)前元素和他后一個元素做比較,
 *      如果比后一個元素大的話歼狼,交換兩個元素的位置掏导。
 *  2.指針向后挪動一個單位,繼續(xù)重復(fù)上面的的步驟羽峰,直到指針走到數(shù)組長度減一的位置趟咆,
 *        那個此時數(shù)組中的最大值就是數(shù)組中的最后一個元素,
 *  3.數(shù)組的長度減一梅屉,繼續(xù)重復(fù)上面的動作值纱,直到數(shù)組的長度為1的時候結(jié)束。
 *
 *  缺點:
 *      可能在數(shù)組長度不到1的時候坯汤,序列就已經(jīng)有序了虐唠,所以,
 *      增加一個標(biāo)志位惰聂,如果在發(fā)生交換疆偿,更改標(biāo)志位,
 *      一次排序后搓幌,判斷標(biāo)志位是否更改杆故,如果沒有更改代表排序已經(jīng)完成
 */
void bubble_sort(int arr[], int len){
    int flag = 1;
    for (int i = 0;flag && i < len - 1; i++){
        flag = 0;
        for (int j = i; j < len-1-i; j++){
            if (arr[j]>arr[j + 1]){
                flag = 1;
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

/**
 *  插入排序:
 *
 *  時間復(fù)雜度:
 *      O(n)^2
 *  空間復(fù)雜度:
 *      O(1)
 *  穩(wěn)定性:
 *      穩(wěn)定
 *  
 *  是升序情況為例:
 *  將數(shù)組分成有序和無序兩部分,每次從無序的數(shù)組中取出一個元素插入到有序的那部分中
 * 
 */

void insert_sort(int data[], int len){
    for (int i = 0; i < len - 1; i++){
        int temp = data[i + 1];
        int j = i;
        while (j >= 0 && data[j]>temp){
            data[j + 1] = data[j];
            j--;
        }
        data[j + 1] = temp;
    }
}

/**
*   選擇排序:
*
*   時間復(fù)雜度:
*       O(n)^2
*   空間復(fù)雜度:
*       O(1)
*   穩(wěn)定性:
*       不穩(wěn)定
*
*   是升序情況為例:
*   它的工作原理是每一次從待排序的數(shù)據(jù)元素中選出最懈瘸睢(或最大)的一個元素处铛,
*   存放在序列的起始位置,直到全部待排序的數(shù)據(jù)元素排完
*/
void select_sort(int data[], int len){
    int min;
    for (int i = 0; i < len-1; i++){
        int min = i;
        for (int j = i + 1; j < len; j++){
            if (data[min]>data[j]){
                min = j;
            }
        }
        if (min != i){
            int temp = data[i];
            data[i] = data[min];
            data[min] = temp;
        }
    }
}
/**
 *  歸并排序:
 *
 *  時間復(fù)雜度:
 *      O(n log n)
 *  空間復(fù)雜度:
 *      O(n)
 *  穩(wěn)定性:
 *      穩(wěn)定
 *
 *  該算法是采用分治法(Divide and Conquer)的一個非常典型的應(yīng)用。
 *  將已有序的子序列合并撤蟆,得到完全有序的序列篙贸;
 *  即先使每個子序列有序,再使子序列段間有序枫疆。若將兩個有序表合并成一個有序表
 */

void array_merge(int data[], int low, int mid, int high){
    int len = high - low;
    int *p = (int*)malloc(sizeof(int)*len);
    int i = low, j = mid, k = 0;
    while (i < mid&&j < high){
        if (data[i] < data[j]){
            p[k++] = data[i++];
        }
        else{
            p[k++] = data[j++];
        }
    }
    while (i < mid){
        p[k++] = data[i++];
    }
    while (j < high){
        p[k++] = data[j++];
    }
    for (int i = 0, j = low; i < k; i++){
        data[j++] = p[i];
    }
    free(p);
}
void merge_sort(int data[], int low, int high){
    if (NULL == data || low < 0 || high <= 0||low>=high){
        return;
    }
    if (high - low == 1){
        return;
    }
    int mid = (high + low) / 2;
    merge_sort(data, low, mid);
    merge_sort(data, mid , high);
    array_merge(data, low, mid, high);
}

/**
 *
 *  快速排序:
 *
 *  時間復(fù)雜度:
 *      O(n log n)
 *  空間復(fù)雜度:
 *      O(n)
 *  穩(wěn)定性:
 *      不穩(wěn)定
 *
 *  首先確定一個基準(zhǔn)值爵川,通過比較和交換,使得基準(zhǔn)值左邊的數(shù)字都比基準(zhǔn)值小
 *  基準(zhǔn)值右邊的數(shù)都比基準(zhǔn)值大息楔,然后以基準(zhǔn)值為分割點寝贡,分成三部分,分別進(jìn)行左遞歸和右遞歸進(jìn)行排序值依。
 *
 *
 *  缺點:
 *  傳統(tǒng)基準(zhǔn)值的選定時每次參照順序表的第一個元素作為基準(zhǔn)值圃泡,
 *  但是如果數(shù)組為有序的情況下,會大大降低排序效率愿险,退化成冒泡排序時間復(fù)雜度升為O(n^2)
 *  解決方案就是三數(shù)取中颇蜡。
 *
 *
 *  優(yōu)化1:
 *      當(dāng)待排序序列的長度分割到一定大小后,使用插入排序
 *  原因:
 *      對于很小和部分有序的數(shù)組辆亏,快排不如插排好风秤。
 *      當(dāng)待排序序列的長度分割到一定大小后,繼續(xù)分割的效率比插入排序要差扮叨,此時可以使用插排而不是快排缤弦。
 *
 *  優(yōu)化2:
 *      在一次分割結(jié)束后,可以把與Key相等的元素聚在一起彻磁,繼續(xù)下次分割時辰晕,不用再對與key相等元素分割
 *      在一次劃分后壳咕,把與key相等的元素聚在一起微王,能減少迭代次數(shù)判呕,效率會提高不少
 *      具體過程:在處理過程中,會有兩個步驟
 *      第一步磁浇,在劃分過程中斋陪,把與key相等元素放入數(shù)組的兩端
 *      第二步,劃分結(jié)束后扯夭,把與key相等的元素移到樞軸周圍
 */

void swap(int *a, int *b){
    int temp = *a;
    *a = *b;
    *b = temp;
}
//三數(shù)取中:先找到一個最大值放到data[len-1]的位置鳍贾,
//之后mid值通過data[low]和data[mid]確定
int midAndSwap(int data[], int low, int high){
    if (NULL == data || low < 0 || high <= 0){
        return 0;
    }
    int mid = (high - low) / 2 + low;
    if (data[low]>data[high - 1]){
        swap(&data[low], &data[high - 1]);
    }
    if (data[mid] > data[high - 1]){
        swap(&data[mid], &data[high - 1]);
    }
    if (data[low] < data[mid]){
        swap(&data[low], &data[mid]);
    }
    return data[low];
}
void insertSort(int data[], int len){
    for (int i = 0; i < len - 1; i++){
        int temp = data[i + 1];
        int j = i;
        while (j >= 0 && data[j]>temp){
            data[j + 1] = data[j];
            j--;
        }
        data[j + 1] = temp;
    }
}

void quickSort(int data[], int low, int high){
    //轉(zhuǎn)換成插入排序數(shù)組的長度為(5<len<15)
    if ((high - low) < 2){
        insertSort(data + low, high - low);
        return;
    }

    int left = low;
    int right = high - 1;

    int leftCopy = left;
    int rightCopy = right;

    int key = midAndSwap(data, low, high);
    while (left < right){
        for (; left < high&&data[left] <= key; left++){
            if (key == data[left]){
                swap(&data[left], &data[leftCopy]);
                leftCopy++;
            }
        }
        for (; right >= left&&data[right] >= key; right--){
            if (key == data[right]){
                swap(&data[right], &data[rightCopy]);
                rightCopy--;
            }
        }
        if (left < right){
            int temp = data[left];
            data[left] = data[right];
            data[right] = temp;
        }
    }
    //將樞紐元素移動到中間位置
    left = right;
    while (leftCopy - low){
        swap(&data[--leftCopy], &data[left--]);
    }
    if (right - left){
        right++;
    }
    while (high - 1 - rightCopy){
        swap(&data[++rightCopy], &data[right++]);
    }
    quickSort(data, low, left + 1);
    quickSort(data, right, high);
}


/**
 *  堆排序
 *
 *  時間復(fù)雜度:
 *    O(n log n)
 *  空間復(fù)雜度:
 *    O(1)
 *  穩(wěn)定性:
 *    不穩(wěn)定
 *
 *  堆排序的基本思想是:將待排序序列構(gòu)造成一個小根堆,此時交洗,整個序列的最小值就是堆頂?shù)母?jié)點。
 *  將其與末尾元素進(jìn)行交換橡淑,此時末尾就為最小值构拳。
 *  然后將剩余n-1個元素重新構(gòu)造成一個堆,這樣會得到n個元素的次小值。
 *  如此反復(fù)執(zhí)行置森,便能得到一個有序序列了斗埂。
 *  
 *  參考博客:https://www.cnblogs.com/chengxiao/p/6129630.html
 */
void heap_sort(int data[], int len){
    if (data == NULL || len <= 0){

    }
    int pa, tag , end = len;
    //end-1解釋: 在數(shù)組中只剩下一個元素的時候不用比較
    while (end - 1){
        tag = 1;
        while (tag){
            pa = end / 2;
            tag = 0;
            while (pa){
                if (data[pa] < data[2 * pa]){
                    swap(&data[pa], &data[2 * pa]);
                    tag = 1;
                }
                if (2 * pa + 1 <= end&&data[pa] < data[2 * pa + 1]){
                    swap(&data[pa], &data[2 * pa + 1]);
                    tag = 1;
                }
                pa--;
            }
        }
        swap(&data[1], &data[end--]);
    }
}

/**
 *  計數(shù)排序:
 *  
 *  時間復(fù)雜度:
 *      O(n)
 *  空間復(fù)雜度:
 *      O(1)
 *  穩(wěn)定性:
 *      不穩(wěn)定
 *  
 *  適合數(shù)據(jù)特別集中的情況。
 *  這里以升序凫海,且數(shù)據(jù)范圍在0-100之間的數(shù)
 *  根據(jù)獲得的數(shù)據(jù)表的范圍呛凶,分割成不同的100個桶,
 *  然后直接統(tǒng)計數(shù)據(jù)在桶上的頻次行贪,然后順序遍歷桶就可以得到已經(jīng)排好序的數(shù)據(jù)表漾稀。
 *      
 */
void count_sort(int data[], int len){
    if (NULL == data || len <= 0){
        return;
    }
    int table[100] = { 0 };
    for (int i = 0; i < len; i++){
        table[data[i]]++;
    }
    int index = 0;
    for (int i = 0; i < 100; i++){
        while (table[i]){
            data[index++] = i;
            table[i]--;
        }
    }
}
/*
    希爾排序:

    時間復(fù)雜度:
        O(n)
    空間復(fù)雜度:
        O(1)
    穩(wěn)定性:
        不穩(wěn)定

    比較相隔較遠(yuǎn)距離(稱為增量)的數(shù),使得數(shù)移動時能跨過多個元素建瘫,
    則進(jìn)行一次比較就可能消除多個元素交換崭捍。
    算法先將要排序的一組數(shù)按某個增量d分成若干組,每組中記錄的下標(biāo)相差d.對每組中全部元素進(jìn)行排序啰脚,
    然后再用一個較小的增量對它進(jìn)行殷蛇,在每組中再進(jìn)行排序。
    當(dāng)增量減到1時橄浓,整個要排序的數(shù)被分成一組粒梦,排序完成。
*/

/**
 *  外排序
 *
 *  基本要點:
 *      外部排序顧名思義是對外部存儲空間中數(shù)據(jù)的排序荸实,那為什么不能像選擇排序谍倦、插入排序、快速排序那樣也直接進(jìn)行排序呢泪勒?
 *      原因是外部存儲的數(shù)據(jù)量可能是非常大的昼蛀,而計算機(jī)的內(nèi)存大小要遠(yuǎn)遠(yuǎn)小于外存,計算機(jī)一下子讀不了那么多數(shù)據(jù)圆存,
 *      無法一次性對它們進(jìn)行排序叼旋,這就是外部排序產(chǎn)生的原因。
 *  基本思想:
 *      1.  我們這里借助歸并排序的思想
 *      假設(shè)我的一個文件中有10000個數(shù)據(jù)沦辙,而我的內(nèi)存每次只能讀2000個數(shù)據(jù)夫植,那我先對文件預(yù)處理一下,將原文件切割成5個小文件油讯,
 *      每個文件中有2000個有序數(shù)據(jù)(在讀入內(nèi)存2000個數(shù)據(jù)后详民,對數(shù)據(jù)排序后再寫入到新文件中)。
 *      2.  然后我們開始進(jìn)行文件合并(這里采用2-路歸并)陌兑。先分別打開兩個文件沈跨,分別讀取文件的第一行數(shù)據(jù),
 *      把數(shù)據(jù)較小的寫入到一個新文件中兔综,然后把數(shù)據(jù)較小的文件再往下讀一行饿凛,直到兩個文件中的數(shù)據(jù)全部有序地寫到新文件中為止狞玛,
 *      這樣原先5個2000個數(shù)據(jù)的文件變?yōu)?個4000個數(shù)據(jù)和1個2000個數(shù)據(jù)的文件。
 *      3.  重復(fù)步驟2涧窒,再次合并變?yōu)?個8000個數(shù)據(jù)和1個2000個數(shù)據(jù)的文件心肪,
 *      再次合并變?yōu)?個10000個數(shù)據(jù)的文件。排序完成纠吴,原先有10000個數(shù)據(jù)的文件變得有序硬鞍。
 *
 */
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市戴已,隨后出現(xiàn)的幾起案子固该,更是在濱河造成了極大的恐慌,老刑警劉巖恭陡,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹬音,死亡現(xiàn)場離奇詭異,居然都是意外死亡休玩,警方通過查閱死者的電腦和手機(jī)著淆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拴疤,“玉大人永部,你說我怎么就攤上這事∧欧” “怎么了苔埋?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蜒犯。 經(jīng)常有香客問我组橄,道長,這世上最難降的妖魔是什么罚随? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任玉工,我火速辦了婚禮,結(jié)果婚禮上淘菩,老公的妹妹穿的比我還像新娘遵班。我一直安慰自己,他們只是感情好潮改,可當(dāng)我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布狭郑。 她就那樣靜靜地躺著,像睡著了一般汇在。 火紅的嫁衣襯著肌膚如雪翰萨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天趾疚,我揣著相機(jī)與錄音缨历,去河邊找鬼以蕴。 笑死糙麦,一個胖子當(dāng)著我的面吹牛辛孵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赡磅,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼魄缚,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了焚廊?” 一聲冷哼從身側(cè)響起冶匹,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咆瘟,沒想到半個月后嚼隘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡袒餐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年飞蛹,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灸眼。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡卧檐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出焰宣,到底是詐尸還是另有隱情霉囚,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布匕积,位于F島的核電站盈罐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏闪唆。R本人自食惡果不足惜盅粪,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望苞氮。 院中可真熱鬧湾揽,春花似錦、人聲如沸笼吟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贷帮。三九已至戚揭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間撵枢,已是汗流浹背民晒。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工精居, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人潜必。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓靴姿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親磁滚。 傳聞我的和親對象是個殘疾皇子佛吓,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,697評論 2 351

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