list sort方法調研

stl中的list 是雙向鏈表結構,最近用到其中的sort方法,文檔中有這麼兩段:

Sorts *this according to operator<. The sort is stable, that is, the relative order of equivalent elements is preserved. All iterators remain valid and continue to point to the same elements. [6] The number of comparisons is approximately N log N, where N is the list's size.

-》[6] The sort algorithm works only for random access iterators. In principle, however, it would be possible to write a sort algorithm that also accepted bidirectional iterators. Even if there were such a version of sort, it would still be useful for list to have a sort member function. That is, sort is provided as a member function not only for the sake of efficiency, but also because of the property that it preserves the values that list iterators point to.

可以看出 stl中的sort算法一般只支持 隨機訪問的結構,對於list來說,一般使用它自己的sort成員函數,並且時間複雜度在nlogn。

由於對鏈表結構的排序比較感興趣,回頭在網上搜索了一下list內部的sort算法分析,由源碼分析可以看出list內部的sort函數,實際上是通過鏈表方式實現的非遞歸的mergesort,時間複雜度在nlogn,並且空間複雜度爲O(1),下面貼兩篇分析的文章:

---------------------------------------

stl中的list被實現爲環狀的雙向鏈表,設置一個“哨”node作爲end( )。list沒有使用標準sort算法,而是實現自身的sort,本質上是mergesort(侯捷解釋的是錯的),但是採用了一個特殊的形式:

普通的mergesort直接將待排序的序列一分爲二,然後各自遞歸調用mergesort,再使用Merge算法用O(n)的時間將已排完序的兩個子序列歸併,從而總時間效率爲n*lg(n)。(mergesort是很好的排序算法,絕對時間很小,n*lg(n)之前的係數也很小,但是在內存中的排序算法中並不常見,我想可能主要還是因爲耗空間太多,也是O(n))

list_sort所使用的mergesort形式上大不一樣:將前兩個元素歸併,再將後兩個元素歸併,歸併這兩個小子序列成爲4個元素的有序子序列;重複這一過程,得到8個元素的有序子序列,16個的,32個的。。。,直到全部處理完。主要調用了swap和merge函數,而這些又依賴於內部實現的transfer函數(其時間代價爲O(1))。該mergesort算法時間代價亦爲n*lg(n),計算起來比較複雜。list_sort中預留了64個temp_list,所以最多可以處理2^64-1個元素的序列,這應該足夠了:)

爲何list不使用普通的mergesort呢?這比較好理解,因爲每次找到中間元素再一分爲二的代價實在太大了,不適合list這種非RandomAccess的容器。

爲何list不使用標準sort算法呢(標準sort要求RandomAccessIterator)?至少普通的quicksort我覺得應該是可以的,具體原因等查查標準算法實現再來說了。

下面把gcc4.02中list_sort的實現貼上:

template<typename _Tp, typename _Alloc>
    void
    list<_Tp, _Alloc>::
    sort()
    {
      // Do nothing if the list has length 0 or 1.
      if (this->_M_impl._M_node._M_next != &this->_M_impl._M_node
   && this->_M_impl._M_node._M_next->_M_next != &this->_M_impl._M_node)
      {
        list __carry;
        list __tmp[64];
        list * __fill = &__tmp[0];
        list * __counter;

        do
   {
     __carry.splice(__carry.begin(), *this, begin());

     for(__counter = &__tmp[0];
   __counter != __fill && !__counter->empty();
   ++__counter)
       {
   __counter->merge(__carry);
   __carry.swap(*__counter);
       }
     __carry.swap(*__counter);
     if (__counter == __fill)
       ++__fill;
   }
while ( !empty() );

        for (__counter = &__tmp[1]; __counter != __fill; ++__counter)
          __counter->merge(*(__counter - 1));
        swap( *(__fill - 1) );
      }
    }

--------------------------------------------------------

list::sort() 這個函數使用了非常巧妙的算法,來對list中的元素進行排序,在某些書上看到,指出該算法是快速排序,個人認爲應該是歸併排序更爲確切。。所以提供了以下測試代碼以觀察整個排序過程。

#include <iostream>
#include <list>
#include <iomanip>
using namespace std;

//the max value is 64
const int SCALE=10;

template <typename T> 
void print_dbg(list<T>& iList, list<T>& carry, list<T>* counter)
{
    typename list<T>::iterator it2;
    
    cout<<"list: "; 
    it2=iList.begin();
    while(it2!=iList.end())
    { 
        cout<<setw(3)<<*it2++;
    } 
    cout<<endl; 
    
    cout<<"carry: ";
    it2=carry.begin(); 
    while(it2!=carry.end())
    { 
        cout<<setw(3)<<*it2++;
    } 
    cout<<endl; 
    
    for(int i=0; i<SCALE; ++i)
    {
        if(!counter[i].empty())
        {
            cout<<"counter["<<i<<"]: ";
            it2=counter[i].begin();
            while(it2!=counter[i].end())
            {
                cout<<setw(3)<<*it2++;
            }
            cout<<endl;
        }
    }
    cout<<endl<<endl;

template <typename T> 
void LSort(list<T> &iList) 

    if(iList.size()<= 1)
    {
        return;
    }
     
    list<T> carry; 
    list<T> counter[64]; 
    int fill = 0;  

    typename list<T>::iterator it; 
    
    while (!iList.empty())
    {
        carry.splice(carry.begin(), iList, iList.begin());   

        print_dbg(iList, carry, counter);
     
        int i = 0; 
        while(i < fill && !counter[i].empty()) 
        { 
            counter[i].merge(carry); 
            carry.swap(counter[i++]); 
        }
        
        carry.swap(counter[i]);
        print_dbg(iList, carry, counter);

        if (i == fill) 
        {
            ++fill;
        } 
        cout<<endl<<endl<<endl;
    }
      
    for (int i = 1; i < fill; ++i)
    {
        counter[i].merge(counter[i-1]); 
    }
    
    print_dbg(iList, carry, counter);
    
    iList.swap(counter[fill-1]);


int main() 

    int arr[]={22,43,1,32,43,3,65,8,56,98,4,23,87,94,37,77,35,36,0}; 
    list<int> iList(arr,arr+sizeof(arr)/sizeof(int)); 

    list<int>::iterator it;

    LSort(iList); 
    
    cout<<"Complete! After sort: "<<endl;
    it=iList.begin(); 
    while(it!=iList.end())
    { 
        cout<<setw(3)<<*it++; 
    }
    cout<<endl<<endl; 

    system("pause");
    return 0; 
}
通過函數print_dbg()打印所有中間鏈表的內容,在以上數組的輸入時,可以得到以下輸出:

list:  43  1 32 43  3 65  8 56 98  4 23 87 94 37 77 35 36
carry:  22

list:  43  1 32 43  3 65  8 56 98  4 23 87 94 37 77 35 36
carry:
counter[0]:  22

 

list:   1 32 43  3 65  8 56 98  4 23 87 94 37 77 35 36  0
carry:  43
counter[0]:  22

list:   1 32 43  3 65  8 56 98  4 23 87 94 37 77 35 36  0
carry:
counter[1]:  22 43

 

list:  32 43  3 65  8 56 98  4 23 87 94 37 77 35 36  0
carry:   1
counter[1]:  22 43

list:  32 43  3 65  8 56 98  4 23 87 94 37 77 35 36  0
carry:
counter[0]:   1
counter[1]:  22 43

 

list:  43  3 65  8 56 98  4 23 87 94 37 77 35 36  0
carry:  32
counter[0]:   1
counter[1]:  22 43

list:  43  3 65  8 56 98  4 23 87 94 37 77 35 36  0
carry:
counter[2]:   1 22 32 43

 

list:   3 65  8 56 98  4 23 87 94 37 77 35 36  0
carry:  43
counter[2]:   1 22 32 43

list:   3 65  8 56 98  4 23 87 94 37 77 35 36  0
carry:
counter[0]:  43
counter[2]:   1 22 32 43

 

list:  65  8 56 98  4 23 87 94 37 77 35 36  0
carry:   3
counter[0]:  43
counter[2]:   1 22 32 43

list:  65  8 56 98  4 23 87 94 37 77 35 36  0
carry:
counter[1]:   3 43
counter[2]:   1 22 32 43

 

list:   8 56 98  4 23 87 94 37 77 35 36  0
carry:  65
counter[1]:   3 43
counter[2]:   1 22 32 43

list:   8 56 98  4 23 87 94 37 77 35 36  0
carry:
counter[0]:  65
counter[1]:   3 43
counter[2]:   1 22 32 43

 

list:  56 98  4 23 87 94 37 77 35 36  0
carry:   8
counter[0]:  65
counter[1]:   3 43
counter[2]:   1 22 32 43

list:  56 98  4 23 87 94 37 77 35 36  0
carry:
counter[3]:   1  3  8 22 32 43 43 65

 

list:  98  4 23 87 94 37 77 35 36  0
carry:  56
counter[3]:   1  3  8 22 32 43 43 65

list:  98  4 23 87 94 37 77 35 36  0
carry:
counter[0]:  56
counter[3]:   1  3  8 22 32 43 43 65

 

list:   4 23 87 94 37 77 35 36  0
carry:  98
counter[0]:  56
counter[3]:   1  3  8 22 32 43 43 6

list:   4 23 87 94 37 77 35 36  0
carry:
counter[1]:  56 98
counter[3]:   1  3  8 22 32 43 43 65

 

list:  23 87 94 37 77 35 36  0
carry:   4
counter[1]:  56 98
counter[3]:   1  3  8 22 32 43 43 65

list:  23 87 94 37 77 35 36  0
carry:
counter[0]:   4
counter[1]:  56 98
counter[3]:   1  3  8 22 32 43 43 65

 

list:  87 94 37 77 35 36  0
carry:  23
counter[0]:   4
counter[1]:  56 98
counter[3]:   1  3  8 22 32 43 43 65

list:  87 94 37 77 35 36  0
carry:
counter[2]:   4 23 56 98
counter[3]:   1  3  8 22 32 43 43 65

 

list:  94 37 77 35 36  0
carry:  87
counter[2]:   4 23 56 98
counter[3]:   1  3  8 22 32 43 43 65

list:  94 37 77 35 36  0
carry:
counter[0]:  87
counter[2]:   4 23 56 98
counter[3]:   1  3  8 22 32 43 43 65

 

list:  37 77 35 36  0
carry:  94
counter[0]:  87
counter[2]:   4 23 56 98
counter[3]:   1  3  8 22 32 43 43 65

list:  37 77 35 36  0
carry:
counter[1]:  87 94
counter[2]:   4 23 56 98
counter[3]:   1  3  8 22 32 43 43 65

 

list:  77 35 36  0
carry:  37
counter[1]:  87 94
counter[2]:   4 23 56 98
counter[3]:   1  3  8 22 32 43 43 65

list:  77 35 36  0
carry:
counter[0]:  37
counter[1]:  87 94
counter[2]:   4 23 56 98
counter[3]:   1  3  8 22 32 43 43 65

 

list:  35 36  0
carry:  77
counter[0]:  37
counter[1]:  87 94
counter[2]:   4 23 56 98
counter[3]:   1  3  8 22 32 43 43 65

list:  35 36  0
carry:
counter[4]:   1  3  4  8 22 23 32 37 43 43 56 65 77 87 94

 

list:  36  0
carry:  35
counter[4]:   1  3  4  8 22 23 32 37 43 43 56 65 77 87 94

list:  36  0
carry:
counter[0]:  35
counter[4]:   1  3  4  8 22 23 32 37 43 43 56 65 77 87 94

 

list:   0
carry:  36
counter[0]:  35
counter[4]:   1  3  4  8 22 23 32 37 43 43 56 65 77 87 94

list:   0
carry:
counter[1]:  35 36
counter[4]:   1  3  4  8 22 23 32 37 43 43 56 65 77 87 94

 

list:
carry:   0
counter[1]:  35 36
counter[4]:   1  3  4  8 22 23 32 37 43 43 56 65 77 87 94

list:
carry:
counter[0]:   0
counter[1]:  35 36
counter[4]:   1  3  4  8 22 23 32 37 43 43 56 65 77 87 94

 

list:
carry:
counter[4]:   0  1  3  4  8 22 23 32 35 36 37 43 43 56 65


Complete! After sort:
  0  1  3  4  8 22 23 32 35 36 37 43 43 56 65 77 87 94 98
 

所以個人認爲應該算是歸併排序。

 

發佈了40 篇原創文章 · 獲贊 6 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章