雜談:指標和數組
對於數組編譯器可以自動推到出內部元素的大小,可以通過sizeof或者模板,
//1<br />int myints[] = {10,20,30,5,15};<br />int length = sizeof(myints)/sizeof(myints[0]);</p><p>//2<br />template <typename T,int size><br />int GetArraySize(T (&ptr)[size])<br />{<br />return size;<br />}<br />GetArraySize(myints);
但是指標,則不能通過模板或者sizeof推到出尺寸大小,原因在於sizeof和模板都是在編譯期起作用~
int* ptr = new int[5];<br />sizeof(ptr) == 5;//true<br />GetArraySize(ptr); //參數不匹配
所以使用模板寫排序函數時候,為了相容指標和數組,要麼傳首指標和size,要麼像STL中的傳首位指標。本文使用前者。
快速排序
快速排序是最實用的排序方法,雖然它最壞已耗用時間為O(n^2), 但是它的平均期望已耗用時間為O(nlgn),std::sort在元素個數小於32的情況下使用的就是quick_sort排序(大於32則使用堆排序)。
快速排序的核心思想是將數組劃分為三部分,如果大到小順序排序,則一次劃分將數組分為小於樞軸值的區間,等於樞軸值,大於樞軸值的區間。然後分段遞迴劃分。
代碼如下:
#include <iostream><br />#include <algorithm><br />#include <limits><br />#include <vector><br />#include <ctime><br />#include <cassert></p><p>using namespace std;</p><p>// Avoid collide with std::swap.<br />void swap(int& a, int& b)<br />{<br />typedef int type;</p><p>type temp = a;<br />a = b;<br />b = temp;<br />}</p><p>template<typename _Ty><br />int quick_partition(_Ty* ptr, int begin, int end, int length/*for debug*/)<br />{<br />typedef _Ty type;<br />typedef type* ptr_type;</p><p>type pivot = ptr[end];// compare value.<br />int section1 = begin-1;// first section index.</p><p>#ifdef _DEBUG<br />cout << "----------------------------------------------" << endl;<br />cout << "Pivot is = " << pivot << endl;<br />cout << "Before partition : ";<br />for (int i(0); i<length; i++) cout << ptr[i] << " ";<br />cout << endl;<br />#endif</p><p>for (int idx = begin; idx<end; idx++)<br />{<br />if (ptr[idx]<=pivot)//逆序排序,改為ptr[idx]>=pivot<br />{<br />section1++;<br />swap( ptr[section1], ptr[idx] );</p><p>#ifdef _DEBUG<br />cout << "Swap : " << section1 << " " << idx << endl;<br />#endif</p><p>}<br />}</p><p>swap( ptr[section1+1], ptr[end] );</p><p>#ifdef _DEBUG<br />cout << "After partition : ";<br />for (int i(0); i<length; i++)<br />if (i==section1+1) cout << "{" << ptr[i] << "}" << " ";<br />elsecout << ptr[i] << " ";<br />cout << endl;<br />cout << "----------------------------------------------" << endl;<br />#endif</p><p>return section1+1;<br />}</p><p>template<class _Ty><br />void quick_sort(_Ty* ptr, int begin, int end, int length/*for debug*/)<br />{<br />if (begin>end)<br />return;</p><p>int size = sizeof(ptr)/sizeof(ptr[0]);</p><p>int pos = quick_partition(ptr, begin, end, length);</p><p>quick_sort(ptr, begin, pos-1, length);<br />quick_sort(ptr, pos+1, end, length);<br />}</p><p>template<typename _Ty><br />void stooge_sort(_Ty* ptr, int begin, int end)<br />{<br />if (ptr[begin] > ptr[end])<br />swap(ptr[begin], ptr[end]);</p><p>if (begin+1>=end)<br />return;</p><p>int k = (end-begin+1)/3;</p><p>stooge_sort(ptr, begin, end-k);<br />stooge_sort(ptr, begin+k, end);<br />stooge_sort(ptr, begin, end-k);<br />}</p><p>int main ()<br />{<br />#define P_1</p><p>#ifdef P_1<br />{<br />int myints[] = {10,20,30,5,15};<br />int length = sizeof(myints)/sizeof(myints[0]);<br />int* p = new int[5];</p><p>quick_sort(myints, 0, length-1, length);<br />//stooge_sort(myints, 0, length-1);</p><p>for (int i(0); i<length; i++) cout << myints[i] << " ";<br />cout << endl;<br />}<br />#endif // P_1</p><p>#ifdef P_2<br />{<br />const int test_num = 1000000;// 1 million<br />srand((int)time(0));</p><p>cout << "Test number " << "/t:/t/t" << test_num << std::endl;</p><p>int* testPtr = new int[test_num];<br />int* testPtr1 = new int[test_num];</p><p>for(int idx=0; idx<test_num; idx++)<br />testPtr1[idx] = testPtr[idx] = rand();</p><p>clock_t t = clock();<br />quick_sort(testPtr, 0, test_num-1, test_num);<br />//stooge_sort(testPtr, 0, test_num-1);<br />cout << "heap_sort " << "/t:/t/t" << clock() - t << std::endl;</p><p>t = clock();<br />std::sort(testPtr1, testPtr1+test_num);<br />cout << "std::sort_heap " << "/t:/t/t" << clock() - t << std::endl;</p><p>// Test the result validity<br />for(int idx=0; idx<test_num; idx++)<br />if (testPtr[idx]!=testPtr1[idx])<br />cout << "wrong";</p><p>delete[] testPtr;testPtr = 0;<br />delete[] testPtr1;testPtr1 = 0;<br />}<br />#endif // P_2</p><p>#ifdef P_3<br />#endif // P_3</p><p>system("PAUSE");</p><p>return 0;<br />}<br />
在debug模式下,輸出快速排序過程:
----------------------------------------------<br />Pivot is = 15<br />Before partition : 10 20 30 5 15<br />Swap : 0 0<br />Swap : 1 3<br />After partition : 10 5 {15} 20 30<br />----------------------------------------------<br />----------------------------------------------<br />Pivot is = 5<br />Before partition : 10 5 15 20 30<br />After partition : {5} 10 15 20 30<br />----------------------------------------------<br />----------------------------------------------<br />Pivot is = 10<br />Before partition : 5 10 15 20 30<br />After partition : 5 {10} 15 20 30<br />----------------------------------------------<br />----------------------------------------------<br />Pivot is = 30<br />Before partition : 5 10 15 20 30<br />Swap : 3 3<br />After partition : 5 10 15 20 {30}<br />----------------------------------------------<br />----------------------------------------------<br />Pivot is = 20<br />Before partition : 5 10 15 20 30<br />After partition : 5 10 15 {20} 30<br />----------------------------------------------<br />5 10 15 20 30
後記
如果要逆序從大到小排序,只需要修改劃分函數中的if比較運算式,或者像STL中傳入一個函數指標進去。導論中提到了一個stooge_sort排序演算法,效率實在是低~
快排分區演算法常見的是:雙邊交換的演算法。
分區演算法-2011-9-15
int partition(int* ptr, int beg, int end){int povit = ptr[beg];int h = beg;for (int k=h; k<=end; k++){if (ptr[k] < povit){h = h+1;swap(ptr[h], ptr[k]);}}swap( ptr[h], ptr[beg] );return h;}int double_partition(int* ptr, int beg, int end){int h = beg;int k = end;int povit = ptr[beg];while ( h < k){while (h < k && ptr[k] > povit)--k;ptr[h] = ptr[k];while (h < k && ptr[h] < povit)++h;ptr[k] = ptr[h];}ptr[h] = povit;return h;}
int select_kth_element(int* ptr, int beg, int end, int kth) { if (beg > end) return -1; // int pos = partition(ptr, beg, end); int pos = double_partition(ptr, beg, end); /*if (pos == kth)// 直接位置比較return ptr[pos]; else if (pos > kth) return select_kth_element(ptr, beg, pos-1, kth); return select_kth_element(ptr, pos+1, end, kth); */int offset = pos - beg;// 位移量比較if (offset == kth) return ptr[pos]; else if (offset > kth) return select_kth_element(ptr, beg, pos-1, kth); // k - (offset+1)return select_kth_element(ptr, pos+1, end, kth-offset-1); }