音視頻開發之旅(26) 算法系列-選擇、插入排序以及STL中sort的實現

目錄

  1. 選擇排序
  2. 插入排序
  3. STL中sort的實現
  4. 資料
  5. 收穫

這一篇我們一起來學習實踐下選擇排序和插入排序,然後再一起分析下CPP的STL中排序算法的實現,結束排序算法的階段。

一、選擇排序

  1. 假設一個下標對應的數組內容值爲最小值(一般使用未確定的第一個),然後依次用這個值和後面的所有值進行對比大小,如果後面的值小於該值,先記錄最小值的位置以及值,在不斷後後續值進行比較,一次循環遍歷後,根據最小值和初始最小值相比十分有變化,如果有則進行交換。
  2. 下標加1,重複第一步

實現比較簡單,我們就不畫圖,分析了,代碼中加了詳細的註釋

#include <iostream>
#include<array>
#include<algorithm>

using namespace std;


void printSortArray(int myarray[],int size){

        for(int k=0; k<size; k++)
        {
            cout<<myarray[k]<<" ";
        }
        cout<<endl;
}


void swap(int *a,int i,int j)
{
    int tmp = a[i];
    a[i] = a[j];
    a[j]=tmp;
}

void selectSort(int *a,int length)
{
    for(int i = 0;i<length;i++)
    {
        //先假設第一個元素爲最小值,通過外部遍歷記錄
        int min = a[i];
        int minPos = i;

        for(int j =i+1;j<length;j++)
        {
            //進行一輪的和上述假設的最小值進行大小對比。如果出現後續的值比假定的最小值還小,則把該值賦值給最小值,
            if(min>a[j])
            {
                min =a[j];
                minPos = j;
            }
 
        }
        //一輪對比完成之後,檢查最小值的下標是否發生變化,如果是則進行交換
        if(i !=minPos)
        {
            swap(a,i,minPos);
        }
        //一輪過後,最右側的坑位被當輪的最小值只用,然後再循環確定後續的坑位的值
    }
}

int main(void) {
    //用一位數組,表示一個完全二叉樹,可以從任意節點拿到它的父節點 和它的左右子節點

   int myarray[] ={6,7,1,2,10,5,8,3,4};

   int size = sizeof(myarray)/sizeof(myarray[0]) ;

   cout<<"size="<<size<<endl;

   selectSort(myarray,size);

   printSortArray(myarray,size);

    return 0;
}

看到選擇排序,很容易想到我們前面學習實踐的冒泡排序,他們之間有什麼區別吶?

冒泡排序事兩兩相鄰對比,每次對比都可能觸發交互,冒泡排序是通過數找位置。
選擇排序則是先假設一個爲最小值,然後用這個值和後面所有的內容一一進行比較大小,每輪進行一次交換。選擇排序是先確定位置,在找值。

他們的優點都是比較簡單,但是缺點也都很明顯,時間複雜度是O(n^2)。選擇排序還會破環原來順序的穩定性(即 有相同值時,通過選擇排序相同值的前後順序會被破壞)。

二、插入排序

插入排序就像我們打打牌時,手裏的牌是已經排序好的,起一張新的牌插入到已有的有序牌中的適當位置,爲了給要插入的元素騰出空間,需要講其餘大於要插入值的元素,在插入前都向右移動一個位置。
插入適合的應用場景:對非隨機的(即有序的)隊列進行插入,效率非常高。

下面我們通過畫圖一步步的開下插入排序的過程


實現如下

#include <iostream>
#include<array>
#include<algorithm>

using namespace std;


void printSortArray(int myarray[],int size){

        for(int k=0; k<size; k++)
        {
            cout<<myarray[k]<<" ";
        }
        cout<<endl;
}


void swap(int *a,int i,int j)
{
    int tmp = a[i];
    a[i] = a[j];
    a[j]=tmp;
}

void insertSort(int *a,int length)
{
    //數組下標從1開始和前面的值進行對比大小
    int i,j,tmp;
    for(i=1;i<length;i++){

        int tmp= a[i];

        for (j = i-1; j>=0; j--)
        {
            //如果後面的小於前面的有序列表中的某位置的值,則把當前位的值向後移動一位,依次循環
            if(tmp< a[j]){
                a[j+1] = a[j];

            } else {
                break;
            }
        }
       //跳出循環後,把tmp在賦值給要插入的地方
       a[j+1] = tmp;  
    }

}

int main(void) {
    //用一位數組,表示一個完全二叉樹,可以從任意節點拿到它的父節點 和它的左右子節點

   int myarray[] ={6,7,1,2,10,5,8,3,4};

   int size = sizeof(myarray)/sizeof(myarray[0]) ;

   cout<<"size="<<size<<endl;

   insertSort(myarray,size);

   printSortArray(myarray,size);

    return 0;
}

三、STL中排序算法的實現

通過這四篇關於排序算法的的學習,我們理解了基礎的選擇排序、插入排序、冒泡排序、快速排序以及堆排序的原理和實現。下面我們來看下CPP中STL的排序算法的具體實現
[源碼地址]:https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.4/a01347.html

template<typename _RandomAccessIterator>
05206     inline void
05207     sort(_RandomAccessIterator __first, _RandomAccessIterator __last)
05208     {
05209       typedef typename iterator_traits<_RandomAccessIterator>::value_type
05210     _ValueType;
05211 
05212       // concept requirements
05213       __glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
05214         _RandomAccessIterator>)
05215       __glibcxx_function_requires(_LessThanComparableConcept<_ValueType>)
05216       __glibcxx_requires_valid_range(__first, __last);
05217 
05218       if (__first != __last)
05219     {
05220       std::__introsort_loop(__first, __last,
05221                 std::__lg(__last - __first) * 2);
05222       std::__final_insertion_sort(__first, __last);
05223     }
05224     }

__lg函數是計算遞歸深度,用來控制分割惡化,當遞歸深度達到該值改用堆排序,因爲堆排序是時間複雜度恆定爲nlogn

/// This is a helper function for the sort routines.  Precondition: __n > 0.
02308   template<typename _Size>
02309     inline _Size
02310     __lg(_Size __n)
02311     {
02312       _Size __k;
02313       for (__k = 0; __n != 0; __n >>= 1)
02314     ++__k;
02315       return __k - 1;
02316     }

快速排序的實現如下:

02242   /// This is a helper function for the sort routine.
02243   template<typename _RandomAccessIterator, typename _Size>
02244     void
02245     __introsort_loop(_RandomAccessIterator __first,
02246              _RandomAccessIterator __last,
02247              _Size __depth_limit)
02248     {
02249       typedef typename iterator_traits<_RandomAccessIterator>::value_type
02250     _ValueType;
02251 
      //區間數目大於_S_threshold採用快速排序
02252       while (__last - __first > int(_S_threshold))
02253     {
           //達到指定遞歸深度,改用堆排序
02254       if (__depth_limit == 0)
02255         {
02256           _GLIBCXX_STD_P::partial_sort(__first, __last, __last);
02257           return;
02258         }
02259       --__depth_limit;
02260       _RandomAccessIterator __cut =
02261         std::__unguarded_partition(__first, __last,
02262                        _ValueType(std::__median(*__first,
02263                                 *(__first
02264                                   + (__last
02265                                      - __first)
02266                                   / 2),
02267                                 *(__last
02268                                   - 1))));
02269       std::__introsort_loop(__cut, __last, __depth_limit);
02270       __last = __cut;
02271     }
02272     }

插入排序部分的實現:

/// This is a helper function for the sort routine.
02171   template<typename _RandomAccessIterator>
02172     void
02173     __final_insertion_sort(_RandomAccessIterator __first,
02174                _RandomAccessIterator __last)
02175     {
02176       if (__last - __first > int(_S_threshold))
02177     {
02178       std::__insertion_sort(__first, __first + int(_S_threshold));
02179       std::__unguarded_insertion_sort(__first + int(_S_threshold), __last);
02180     }
02181       else
02182     std::__insertion_sort(__first, __last);
02183     }

02093   /// This is a helper function for the sort routine.
02094   template<typename _RandomAccessIterator>
02095     void
02096     __insertion_sort(_RandomAccessIterator __first,
02097              _RandomAccessIterator __last)
02098     {
02099       if (__first == __last)
02100     return;
02101 
02102       for (_RandomAccessIterator __i = __first + 1; __i != __last; ++__i)
02103     {
02104       typename iterator_traits<_RandomAccessIterator>::value_type
02105         __val = *__i;
02106       if (__val < *__first)
02107         {
02108           std::copy_backward(__first, __i, __i + 1);
02109           *__first = __val;
02110         }
02111       else
02112         std::__unguarded_linear_insert(__i, __val);
02113     }
02114     }


圖片來源:[C++一道深坑面試題:STL裏sort算法用的是什麼排序算法?]: https://blog.csdn.net/qq_35440678/article/details/80147601

四、資料

《算法》
[冒泡排序和選擇排序的區別] : https://blog.csdn.net/weixin_41887155/article/details/85799820
[排序算法詳解(一)直接插入排序] : https://www.bilibili.com/video/BV1Jv41167eL?from=search&seid=385501809024223768
[C++一道深坑面試題:STL裏sort算法用的是什麼排序算法?]: https://blog.csdn.net/qq_35440678/article/details/80147601

五、收穫

  1. 理解並實現了選擇排序和插入排序
  2. 瞭解stl中sort使用快速排序、堆排序、插入排序的原因以及代碼實現
    排序算法還有很多其他類型,我們比如希爾排序、歸併排序、桶排序等,我們這個階段暫時不做學習實踐,根據需有再續。

感謝你的閱讀

排序算法的部分就到這篇了,下一篇我們開始進入查詢算法的學習實踐,歡迎關注公衆號“音視頻開發之旅”,一起學習成長。

歡迎交流

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章