排序總結,插入排序 選擇排序 交換排序 歸併排序 計數排序 時間複雜度空間複雜度穩定性詳解
阿新 • • 發佈:2019-01-06
排序大體分為兩類:比較排序和非比較排序一 各個排序的基本實現1.直接插入排序和希爾排序
2.選擇排序和堆排序
穩定性:應用場景:成績排名:成績相同,先交卷子在前,後交卷子在後 具體操作:先拿時間排,再拿一個穩定的的排序對成績排,就能保證
各個排序的穩定性首先要明白所有的穩定排序都可以變成不穩定的。
插入 穩定:我能做到比你小讓你往後挪,和你相等放你後面如此便可以保證有序
希爾 不穩定:相同的值可能被分到不同的組裡面 把相對順序打亂
選擇排序 不穩定 :先選到的放到最後面取
堆排序 不穩定:父親大於等於孩子 把父親的放到後面了,孩子的放次後面 相對位置變了
冒泡 穩定 :大的往後冒泡,相等不往後冒泡
快排 不穩定:比它大的往右翻,比它小的往左翻 最後後面的那個到中間 。 // 1 9 5 7 6 4 5 8 5
歸併排序 穩定:如果相等時先拿左邊的
//整體思路:往一段有序區間插入元素,end標記為有序區間的最後一個元素下標,要插入的元素下標為end+1此處就稱tmp,先把他儲存起來,(否則可能被覆蓋)如果end+1這個元素 //比有序區間的小,就把有序區間元素依次向後移動,移動結束條件有兩個,1.end變為-1,2.有序區間內找到比tmp小的數。 void PrintArray ( int *a, size_t n ) { for ( size_t i = 0; i < n ; i++ ) { cout << a[i] << " "; } cout << endl; } //時間複雜度:o(N^2) 最壞情況,每次插入要把前面的全移動 1+2+....n-1等差數列求和(n-1)*n/2也就是o(N^2)逆序時最壞 // o(N) 順序最好,只比較了一遍沒進去 void InsertSort ( int *a, size_t n ) { assert ( a ); for ( size_t i = 0; i < n-1; i++ ) { int end = i; //單趟邏輯 int tmp = a[end + 1]; while (end>=0&& a[end] >tmp ) { a[end + 1] = a[end]; --end; } a[end + 1] = tmp; } } //整體思路1.預排序(使大的數很快移到後面去)分組在每組內移動接近有序 gap越大越不接近有序 2.插入排序 //1.gap>1 預排序 //2.gap==1 插入排序 void ShellSort ( int*a, size_t n )//是針對插入排序逆序的情況下,移動次數太多而設計。 希爾排序用於資料量較大 { assert ( a ); int gap=n; //預排序:排完說明分組為gap的這些元素各自有序 while ( gap > 1 ) { gap = gap / 3+1;//加1保證了最後一次為gap=1,絕對會有序 for ( size_t i = 0; i<n - gap; i++ )//i++保證了不是一組走完,走另一組。而是一起走。 { int end = i; //單趟排序 int tmp = a[end + gap]; while ( end >= 0 && a[end] > tmp ) { a[end+gap] = a[end ]; end -= gap; } a[end + gap] = tmp; } } } void TestInsertSort ( ) { int a[] = { 2, 5, 4, 9, 3, 6, 8, 7, 1 }; //InsertSort ( a, sizeof(a) / sizeof(a[0])); PrintArray ( a, sizeof(a) / sizeof(a[0]) ); } void TestShellSort ( ) { int a[] = { 2, 5, 4, 9, 3, 6, 8, 7, 1 }; ShellSort ( a, sizeof(a) / sizeof(a[0])); PrintArray ( a, sizeof(a) / sizeof(a[0]) ); }
2.選擇排序和堆排序
3.氣泡排序和快速排序氣泡排序//選擇排序 每次可選一個最小的數,一個最大的數 //時間複雜度 最壞o(N^2) n-1+n-2+1 也是等差數列 //時間複雜度 最好o(N^2) 儘管你有序,可是我不知道還是要每次遍歷一遍 void SelectSort ( int *a, size_t n ) { int end = n-1; int begin= 0; while ( begin< end ) { int min = begin; int max = begin; for ( size_t i = begin; i <= end; i++ ) { if ( a[i]>a[max] ) { max = i; } if ( a[i] < a[min ]) { min = i; } } /* swap ( a[min], a[begin] ); if (begin== max ) { max = min; } swap ( a[max], a[end] );*/ swap ( a[max], a[end] ); if ( min == end ) { min = max; } swap ( a[min], a[begin] ); begin++; end--; } } //堆排序 升序 建大堆,把最大的數選出來,換到後面去,然後把剩下的數向下調整看成一個堆 //選第一個數要建堆N*lg N 其他lgn 即N*lgN+(N-1)lgN=o(NlgN) void AdjustDown ( int *a, size_t n, int root ) { int parent = root; int child = 2 * parent; while ( child<n )//如果孩子都不存在說名到葉節點,就停止向下調整 { if ( child+1<n && a[child + 1] > a[child] ) { child++; } if ( a[child] > a[parent] ) { swap ( a[child], a[parent] ); parent = child; child = parent * 2; } else { break; } } } void HeapSort ( int *a, size_t n ) { for ( int i = (n - 2) / 2; i >= 0; i-- )//建堆NlgN { AdjustDown ( a, n, i );//lgN } //(N-1)lgN int end = n - 1; while ( end > 0 ) { swap ( a[0], a[end] ); AdjustDown ( a, end, 0 ); --end; } } void TestHeapSort ( ) { int a[] = { 2, 5, 4, 9, 3, 6, 8, 7, 1, 0 }; HeapSort ( a, sizeof(a) / sizeof(a[0]) ); PrintArray ( a, sizeof(a) / sizeof(a[0]) ); } void TestSelectSort ( ) { int a[] = { 2, 5, 4, 9, 3, 6, 8, 7, 1 ,0}; SelectSort ( a, sizeof(a) / sizeof(a[0])); PrintArray ( a, sizeof(a) / sizeof(a[0]) ); } int main ( ) { //TestSelectSort ( ); TestHeapSort ( ); system ( "pause" ); return 0; }
快速排序://交換排序 //時間複雜度0(N^2) n-1+n-2+.....1 也是等差數列 //最好的情況下:0(N) 有序 //冒泡和插入的區別:插入比冒泡好,冒泡要求更嚴格的有序 //比如:123465 如果插入排序 是比較N-1次,插入一次 N 氣泡排序:第一趟比較 N-1次之後,已經有序可是不知道,又要來一遍 N-1 //因此插入比冒泡好,冒泡要求更嚴格有序 void BubbleSort ( int *a, size_t n ) { //int end = n - 1; //while ( end > 0 ) //{ // bool ExChange = 0; // for ( size_t i = 0; i < end; i++ )//單趟 // { // if ( a[i]>a[i + 1] ) // { // swap ( a[i], a[i + 1] ); // ExChange = 1; // } // } // if ( ExChange == 0 ) // { // break; // } // end--; //} for ( size_t end = n - 1; end > 0; end-- ) { bool ExChange = 0; for ( size_t i = 0; i < end; i++ )//單趟 { if ( a[i]>a[i + 1] ) { swap ( a[i], a[i + 1] ); ExChange = 1; } } if ( ExChange == 0 ) { break; } } } void TestBubbleSort ( ) { int a[] = { 2, 5, 4, 9, 3, 6, 8, 7, 1, 0 }; BubbleSort ( a, sizeof(a) / sizeof(a[0]) ); PrintArray ( a, sizeof(a) / sizeof(a[0]) ); }
//時間複雜度:遞迴的次數乘以每次遞迴 遞迴的次數N每次遞迴lgN 因此時間複雜度為o(NlgN)
//最壞情況:0(N^2) 有序 三數取中法,解決了有序的這種情況
int GetMidindex ( int *a, int begin, int end )
{
int mid = begin + ((end - begin) >> 1);
if ( a[mid] > a[begin] )
{
if ( a[begin] > a[end] )
{
return begin;
}
else if ( a[mid] > a[end] )
{
return end;
}
else
{
return mid;
}
}
else
{
if ( a[begin] > a[end] )
{
return begin;
}
else if ( a[end] > a[mid] )
{
return mid;
}
else
{
return end;
}
}
}
//左右指標法
int PartSort2 ( int *a, int begin, int end )
{
//int& key= a[end];//為什莫給a[end]而不是和講的一樣是a[end-1],注意考慮有序情況:如果給a[end-1]反而錯了,如果給a[end]就自己和自己交換
int mid = GetMidindex ( a, begin, end );
swap ( a[mid], a[end] );
int keyIndex = end;
int key = a[end];
while ( begin < end )
{
while ( begin<end&&a[begin] <= key )
{
begin++;
}
while (begin<end&& a[end]>=key )
{
end--;
}
swap ( a[end], a[begin] );
}
swap(a[keyIndex], a[begin]);
return begin;
}
//挖坑法
int PartSort1 ( int*a, int begin, int end )
{
int key = a[end];
while ( begin < end )
{
while (begin<end&& a[begin] <=key )
{
begin++;
}
a[end] = a[begin];
while (begin<end&& a[end] >= key )
{
end--;
}
a[begin] = a[end];
}
a[begin] = key;
return begin;
}
//前後指標法
int PartSort3 ( int *a, int begin, int end )
{
int& key = a[end];
//int key=a[end]
int cur = begin;
int prev = begin - 1;
while ( cur < end )
{
if ( a[cur] < key && (++prev) != cur )
{
swap ( a[prev], a[cur] );
}
cur++;
}
swap ( a[++prev], key );//swap(a[++prev],a[end]);
return prev;
}
void QuicksortNonR ( int *a, int left, int right )
{
stack<int >st;
st.push ( right );
st.push ( left );
while ( !st.empty ( ) )
{
int begin = st.top ( );
st.pop ( );
int end = st.top ( );
st.pop ( );
int div = PartSort3 ( a, begin, end );
if ( begin < div - 1 )
{
st.push ( div - 1 );
st.push ( begin );
}
if ( div + 1 < end )
{
st.push ( end );
st.push ( div + 1 );
}
}
}
void Quicksort ( int* a, int left,int right )
{
if ( left >= right )
{
return;
}
//小區間優化
if ( right - left < 8)//省去最後3層
{
InsertSort ( a+left, (right - left) + 1 );
return;
}
int div = PartSort3 ( a, left, right );
Quicksort ( a, left, div - 1 );
Quicksort ( a, div + 1, right );
}
//有序有兩種情況:1.區間只剩一個值,說明有序
// 2.左邊有序,右邊有序
void TestQuickSort ( )
{
int a[] = { 5, 10, 4, 9, 20, 6, 8, 7, 1, 5 };
QuicksortNonR ( a, 0, sizeof(a) / sizeof(a[0]) - 1 );
PrintArray ( a, sizeof(a) / sizeof(a[0]) );
}
//int main ( )
//{
// //TestBubbleSort ( );
//
// TestQuickSort ( );
// system ( "pause" );
// return 0;
//}
4.歸併排序歸併排序
時間複雜度:0(NlgN) 空間複雜度:0(N)
void _MergeSort ( int *a, int left, int right, int* tmp )//tmp為什莫再外面開tmp,所有遞迴都可以用.如果在裡面開裡面每次遞迴都要開闢空間
{
if ( left >= right )//如果只剩一個元素,或者沒有元素可以看作是有序的
{
return;
}
if ( right - left < 8 )//省去最後3層
{
InsertSort ( a + left, (right - left) + 1 );
return;
}
int div = ((right - left) >> 1) + left;
//讓兩段子區間有序再歸併
//[left,div] [div+1 ,right]
_MergeSort ( a, left, div, tmp );
_MergeSort ( a, div + 1, right, tmp );
int index = left;
int begin1 = left; int end1 = div;
int begin2 = div + 1; int end2 = right;
while ((begin1<=end1)&&(begin2<=end2))
{
if ( a[begin1] <= a[begin2] )
{
tmp[index++] = a[begin1++];
}
else
{
tmp[index++] = a[begin2++];
}
}
while( begin1 <= end1 )
{
tmp[index++] = a[begin1++];
}
while (begin2<=end2 )
{
tmp[index++] = a[begin2++];
}
//每次歸併完,再拷貝到原區間上去
index = left;
while ( index <= right )
{
a[index] = tmp[index];
++index;
}
}
void MergeSort ( int *a,size_t n)
{
int * tmp = new int[n];
_MergeSort(a, 0, n - 1,tmp);
delete[]tmp;
}
void TestMergeSort ( )
{
int a[] = { 5, 10, 4, 9, 20, 6, 8, 7, 1, 5 };
MergeSort( a, sizeof(a) / sizeof(a[0]));
PrintArray ( a, sizeof(a) / sizeof(a[0]) );
}
int main ( )
{
TestMergeSort ( );
system ( "pause" );
return 0;
}
5.計數排序//非比較排序
//基數排序:只能用於排整型 不多講
//計數排序:
//直接定址法的雜湊
//時間複雜度 0(max(n,range)) 資料範圍比較集中的時候適合用計數排序
void CountSort ( int *a, int n )
{
int max = a[0];
int min = a[0];
for ( int i = 0; i < n; i++ )
{
if ( a[i]>max )
{
max = a[i];
}
if ( a[i] < min )
{
min = a[i];
}
}
int range = max - min + 1;
int * hashtable = new int[range];//不能開n,是相對位置
memset ( hashtable, 0, sizeof(int)*range );
for ( size_t i = 0; i < n; i++ )
{
hashtable[a[i] - min]++;// a[i]是絕對位置,記清楚此處是相對位置
}
size_t j = 0;
for ( size_t i = 0; i < range; i++ )
{
while ( hashtable[i]-- )
{
a[j] = i + min;
++j;
}
}
delete[] hashtable;
}
void TestCountSort ( )
{
int a[] = { 5, 10, 4, 9, 20, 6, 8, 7, 1, 5 };
CountSort( a, sizeof(a) / sizeof(a[0]));
PrintArray ( a, sizeof(a) / sizeof(a[0]) );
}
int main ( )
{
TestCountSort ( );
system ( "pause" );
return 0;
}
二 時間複雜度和空間複雜度三 穩定性穩定性:應用場景:成績排名:成績相同,先交卷子在前,後交卷子在後 具體操作:先拿時間排,再拿一個穩定的的排序對成績排,就能保證
各個排序的穩定性首先要明白所有的穩定排序都可以變成不穩定的。
插入 穩定:我能做到比你小讓你往後挪,和你相等放你後面如此便可以保證有序
希爾 不穩定:相同的值可能被分到不同的組裡面 把相對順序打亂
選擇排序 不穩定 :先選到的放到最後面取
堆排序 不穩定:父親大於等於孩子 把父親的放到後面了,孩子的放次後面 相對位置變了
冒泡 穩定 :大的往後冒泡,相等不往後冒泡
快排 不穩定:比它大的往右翻,比它小的往左翻 最後後面的那個到中間 。 // 1 9 5 7 6 4 5 8 5
歸併排序 穩定:如果相等時先拿左邊的