經典排序算法與STL

排序算法

按照是否將元素放入到內存中,排序分為內部排序和外部排序。內部排序適合元素不多的文件,按照元素的排序原則,內部排序分為:

  1. 插入排序
  2. 交換排序
  3. 選擇排序
  4. 歸并排序

插入排序

基本方法是:尋找一個指定元素在待排序元素中的位置,然后將它插入

直接插入

基本思想:每次將一個待排序的元素按其關鍵碼的大小插入到一個已經排好序的有序序列中,知道全部元素排序好。

void insert_sort(int arr[], int len){
 for (int i=1; i<len; i++) {
    int temp = arr[i] ;
    for (int j = i-1; j>=0; j--) {
      //j+1位置一直是空的,
        if (arr[j]>temp) {
            arr[j+1] = arr[j] ;
        }else {
            arr[j+1] = temp ;
            break ;
        }
    }
    
  }

 }

直接插入的兩個特點: 基本有序的序列,直接插入最快。 記錄個數很少的無序序列,直接插入也很快

希爾排序

希爾排序是對直接插入排序的一種改進。它的基本思想是:將待排序的元素分成多個子集,分別對這些子集進行直接插入排序,待整個序列都有序時,再對元素進行一次直接插入排序。

void Sort::shell_sort(int arr[] ,int len) {
//增長區間
for (int d = len /2; d>=1; d = d/2)
{   //一次希爾排序
    for (int i = d; i < len; i++)
    {    //和有序的數組的最后一個元素比較
        if (arr[i]<arr[i-d])
        {
            int temp = arr[i] ;
            int j = i-d ;
            while(j>=0&&arr[j]>temp){
                arr[j+d] = arr[j];
                j = j-d;
            }
            arr[j+d] = temp ;
        }
    }
}

}

交換排序

交換排序的基本方法是:在待排序的元素中選擇兩個元素,將他們的關鍵碼進行比較,進行排序。

冒泡排序

基本思想是:兩兩比較相鄰的元素,如果反序,則交換位置,直到沒有反序的元素為止。

void bubble_sort(int arr[], int len){
//從小到大
for (int i = 0; i<len; i++) {
    for (int j = len-1; j>i; j--) {
        if (arr[j]<arr[j-1]) {
            int temp = arr[j] ;
            arr[j] = arr[j-1] ;
            arr[j-1] = temp ;
        }
    }
}    
}

快速排序

快速排序是冒泡排序的改進算法,快速排序元素的比較設移動是序列的兩端到中間進行的。它的基本思想是:
在分區中選擇一個元素為軸值,將待排序的元素劃分為兩個分區,左側元素的關鍵碼都小于或者等于軸值,右側元素的關鍵碼的值都大于或者等于軸值。重復上述過程,直到整個序列有序。

void quick_sort(int arr[], int left, int right){
//遞歸結束條件
if (left<right) {
    //一次遍歷
    int i = left , j = right , key = arr[left] ;
     while (i<j) {
        while (i<j&&arr[j]>key) 
        j-- ;
        if (i<j)
        arr[i++] =arr[j];
        while (i<j&& arr[i]<key)
        i++ ;
        if (i<j) 
        arr[j--] = arr[i] ; 
     }
    //遞歸
    arr[i]=key ;
    quick_sort(arr, left, i-1);
    quick_sort(arr, i+1, right); 
}

}

選擇排序

選擇排序的基本思想是:每趟排序在待排序列表中選擇關鍵碼最小的元素,順序添加到已將排序好的有序序列中去,直到全部記錄都排序完成

簡單選擇排序

簡單選擇排序的基本思想是:每趟在待排序的序列中找出最小的記錄加到序列頭部。直到都排序完成。

void select_sort(int arr[], int len){
for (int i= 0; i<len-1; i++) {
        int temp = i ;
    for (int j=i+1; j<len; j++) {
        if(arr[j]<arr[temp]){
            temp = j ;
        }
    }
    if (temp != i) {
        int arrTemp = arr[i] ;
        arr[i] = arr[temp] ;
        arr[temp] =arrTemp ;
    }
}
}

堆排序

堆排序是對簡單選擇排序的改進。堆排序利用了前一趟比較的結果,減小了比較的次數,從而提高整個排序的效率。

//調整堆
void Sort::heap_adjust(int arr[], int i, int len){
int child = 2 * i + 1;
int temp;
while (child<len) {
 // 得到子結點中鍵值較大的結點 這里求的是大根堆, 如果是小根堆,下面變為arr[child + 1] < arr[child]
    if (child < len - 1 && arr[child + 1]>arr[child])
        child ++;
    //這里求的是大根堆,如果求小根堆arr[i] > arr[child]
    if (arr[i] <arr[child])
    {
        temp = arr[i];
        arr[i] = arr[child];
        arr[child] = temp;
        i =child ;
        child = 2 * i + 1;
    }
    else
        break;
}   
}

//堆排序
void Sort::heap_sort(int arr[], int len){
int i;
// 調整序列的前半部分元素,調整完之后第一個元素是序列的最大的元素
for (int i = len / 2 - 1; i >= 0; i--)
{
    heap_adjust(arr, i, len);
}

for (i = len - 1; i > 0; i--)
{
    int temp = arr[0];
    arr[0] = arr[i];
    arr[i] = temp;
    // 不斷縮小調整heap的范圍,每一次調整完畢保證第一個元素是當前序列的最大值
    heap_adjust(arr, 0, i);
}

}

歸并排序

歸并排序的基本思想方法是:將兩個或者兩個以上的有序序列歸并成一個有序序列

void merge(int arr[], int temp_arr[], int start_index, int mid_index, int end_index){
int i=start_index;
int k=0;
int j=mid_index+1 ;

//直到一個組數被遍歷完
while (i<mid_index+1 && j<1+end_index) {
    if (arr[i]>arr[j]) {
        temp_arr[k++] =arr[j++];
    }else{
        temp_arr[k++] =arr[i++];
    }
}
//將沒被遍歷完的數組添加到數組中去
while (i<1+mid_index) {
    temp_arr[k++]= arr[i++];
}

while (j<1+end_index) {
    temp_arr[k++] =arr[j++];
}

for (i = 0, j = start_index; j <1+end_index; i ++, j ++)
     arr[j] = temp_arr[i];
}

void merge_sort(int arr[], int arr_temp[],int start_index, int end_index){
if (start_index<end_index) {
    int mid = (start_index+end_index)/2 ;
    merge_sort(arr, arr_temp, start_index, mid);
    merge_sort(arr, arr_temp, mid+1, end_index);
    merge(arr, arr_temp, start_index, mid, end_index);
}

排序的比較

排序方法 | 平均復雜度 | 空間復雜度 | 穩定性
------------ | ------------- | ------------
直接插入排序 | O(n2)| O(1) | 穩定
希爾排序 | O(nlog2n) | O(1) |不穩定
冒泡排序 | O(n2)|O(1)|穩定
快速排序 | O(nlog2n) | O(nlog2n)|不穩定
簡單選擇排序 | O(n2) | O(1)| 穩定
堆排序 | O(nlog2n) | O(1)| 不穩定
歸并排序 | O(nlog2n) | O(n)| 穩定

STL中相關排序算法

所有STL sort算法函數的名字列表:

函數名 功能描述
sort 對給定區間所有元素進行排序
stable_sort 對給定區間所有元素進行穩定排序
partial_sort 對給定區間所有元素部分排序
partial_sort_copy 對給定區間復制并排序
nth_element 找出給定區間的某個位置對應的元素
is_sorted 判斷一個區間是否已經排好序
partition 使得符合某個條件的元素放在前面
stable_partition 相對穩定的使得符合某個條件的元素放在前面

比較函數:

當你需要按照某種特定方式進行排序時,你需要給sort指定比較函數,否則程序會自動提供給你一個比較函數

vector < int > vect;

sort(vect.begin(), vect.end());//此時相當于調用

sort(vect.begin(), vect.end(), less<int>() );

sort 中的其他比較函數

equal_to 相等
not_equal_to 不相等
less 小于
greater 大于
less_equal 小于等于
greater_equal 大于等于

上述例子中系統自己為sort提供了less仿函數。在STL中還提供了其他仿函數,以下是仿函數列表: 不能直接寫入仿 函數的名字,而是要寫其重載的()函數: less<int>();
當你的容器中元 素時一些標準類型(int float char)或者string時,你可以直接使用這些函數模板。但如果你時自己定義的類型或者你需要按照其他方式排序,你可以有兩種方法來達到效果:一種是自己寫比較函數。另一種是重載類型的'<'操作賦。

全排序:

全排序即把所給定范圍所有的元素按照大小關系順序排列。sort采用的是成熟的"快速排序算法"(目前大部分STL版本已經不是采用簡單的快速排序,而是結合內插排序算法)。復雜度為nlog2n。stable_sort采用的是"歸并排序",分派足夠內存時,其算法復雜度為nlog2n, 否則 其復雜度為nlog2n*log2n,其優點是會保持相等元素之間的相對位置在排序前后保持一致。

用于全排序的函 數有:

  1. void sort(RandomAccessIterator first, RandomAccessIterator last);

  2. void sort(RandomAccessIterator first, RandomAccessIterator last,StrictWeakOrdering comp);

  3. void stable_sort(RandomAccessIterator first, RandomAccessIterator last);

  4. void stable_sort(RandomAccessIterator first, RandomAccessIterator last, StrictWeakOrdering comp);

局部排序:

partial_sort采用的堆排序(heapsort),它在任何情況下的復雜度都是nlog2n。

局部排序其實是為了減少不必要的操作而提供的排序方式。

其函數原型為:

  1. void partial_sort(RandomAccessIterator first, RandomAccessIterator middle,RandomAccessIterator last);

  2. void partial_sort(RandomAccessIterator first,RandomAccessIterator middle,RandomAccessIterator last, StrictWeakOrdering comp);

  3. RandomAccessIterator partial_sort_copy(InputIterator first, InputIteratorlast,RandomAccessIteratorresult_first,RandomAccessIterator result_last);

  4. RandomAccessIterator partial_sort_copy(InputIterator first, InputIteratorlast,RandomAccessIteratorresult_first,RandomAccessIterator result_last, Compare comp);

例如:班上有1000個學生,我想知道分數最低的5名是哪些人。
partial_sort(vect.begin(),vect.begin()+5,vect.end(),less<student>());

nth_element 指定元素排序

  1. void nth_element(RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last);

  2. void nth_element(RandomAccessIterator first, RandomAccessIterator nth,RandomAccessIterator last,StrictWeakOrdering comp);

例如:班上有1000個學生,我想知道分數排在倒數第4名的學生。
nth_element(vect.begin(), vect.begin()+3, vect.end(),less<student>());

partition 和stable_partition

partition就是把一個區間中的元素按照某個條件分成兩類,并沒有排序。

其函數原型為:

  1. ForwardIterator partition(ForwardIterator first, ForwardIterator last, Predicate pred)

  2. ForwardIterator stable_partition(ForwardIterator first, ForwardIterator last, Predicate pred);

例如:班上10個學生,計算所有沒有及格(低于60分)的學生:

 student exam("pass", 60);

 stable_partition(vect.begin(), vect.end(), bind2nd(less<student>(), exam));

效率由高到低(耗時由小變大)

partion | |
-----|
stable_partition |
nth_element|
partial_sort|
sort|
stable_sort|

Effective STL對如何選擇排序函數總結的很好:

  1. 若需對vector, string, deque, 或array容器進行全排序,你可選擇sort或stable_sort;

  2. 若只需對vector, string, deque, 或array容器中取得top n的元素,部分排序partial_sort是首選.

  3. 若對于vector, string, deque,或array容器,你需要找到第n個位置的元素或者你需要得到top n且不關系top n中的內部順序,nth_element是最理想的;

  4. 若你需要從標準序列容器或者array中把滿足某個條件 或者不滿足某個條件的元素分開,你最好使用partition或stable_partition;

  5. 若使用的list容器,你可以直接使用partition和stable_partition算法,你可以使用list::sort代替sort和stable_sort排序。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375

推薦閱讀更多精彩內容

  • STL算法部分主要由頭文件,,組成。要使用STL中的算法函數必須包含頭文件,對于數值算法須包含,中則定義了一些模板...
    eb51589b1211閱讀 612評論 0 1
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個記錄插入到已排序好...
    依依玖玥閱讀 1,266評論 0 2
  • 作者:郭耀華cnblogs.com/guoyaohua/p/8600214.html 最近幾天在研究排序算法,看了...
    xiaotian是個混子閱讀 1,254評論 0 8
  • 概述 排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    zwb_jianshu閱讀 1,183評論 0 0
  • 概述 排序有內部排序和外部排序,內部排序是數據記錄在內存中進行排序,而外部排序是因排序的數據很大,一次不能容納全部...
    printf200閱讀 777評論 0 0