排序大全
一、排序的分類
插入排序算法復雜度為O(n2)。因而,插入排序不適合對于數據量比較大的排序應用。
插入排序的空間復雜度為O(1)。
穩定性:穩定。
/void Insertsort(int arr[],size_t size)
// {
// for(size_t j=1;j<size;j++)
// {
// int i=j;
// while(i>0&&(arr[i]<arr[i-1]))
// {
// int key=arr[i];
// arr[i]=arr[i-1];
// arr[i-1]=key;
// i--;
// }
// }
希爾排序
基本思想:先將整個待排元素序列分割成若干個子序列(由相隔某個“增量”的元素組成的)分別進行直接插入排序,然后依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,再對全體元素進行一次直接插入排序。
圖解(假設為升序):
O(N)<希爾排序的時間復雜度<O(N^2),希爾排序,當N大時,平均的時間復雜度,大約在N^1.25--1.6N^1.25之間。
希爾排序的空間復雜度為O(1)。
void ShellSort(T* a,size_t n)//希爾排序 ?
{ ?
? ? assert(a); ?
? ? int gap = n;//gap為所給增量 ?
? ? while(gap > 1) ?
? ? { ?
? ? ? ? //實驗證明,gap=gap/3是比較優的,+1則是為了最后一次對全體數據進行插入排序 ?
? ? ? ? gap = gap/3 + 1; ?
? ? ? ? for (size_t i = gap; i < n; ++i) ?
? ? ? ? { ?
? ? ? ? ? ? int end = i - gap; ?
? ? ? ? ? ? T tmp = a[i]; ?
??
? ? ? ? ? ? while(end >= 0) ?
? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? if (Compare()(tmp,a[end])) ?
? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? a[end+gap] = a[end]; ?
? ? ? ? ? ? ? ? ? ? end -= gap; ?
? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? ? ? else ?
? ? ? ? ? ? ? ? { ?
? ? ? ? ? ? ? ? ? ? break; ?
? ? ? ? ? ? ? ? } ?
? ? ? ? ? ? } ?
? ? ? ? ? ? a[end+gap] = tmp; ?
? ? ? ? } ?
? ? } ?
選擇排序
選擇排序的比較次數O(n^2),比較次數與關鍵字的初始狀態無關,總的比較次數:
N=(n-1)+(n-2)+...+1=n*(n-1)/2。?
交換次數O(n),最好情況是,已經有序,交換0次;最壞情況是,逆序,交換n-1次。
故此,選擇排序的時間復雜度為O(N^2)。
選擇排序的空間復雜度為O(1)。
堆排序的基本思想
? ? ??堆積排序(Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆是一個近似完全二叉樹的結構,并同時滿足堆性質:即子結點的鍵值或索引總是小于(或者大于)它的父節點。
堆排序的時間復雜度為O(N*lgN)。
堆排序的空間復雜度為O(1)
-
//void ?Headadjust(int* array,int size,int parent)
快速排序
//{
// int child=parent*2+1;
// while(child<size)
// {
//
// if((child+1)<size&&array[child]<array[child+1])
// ? ? ? ? ?{
// ? ? ? ?child=child+1;
// ? ? ? }
//
//
// if (array[child]>array[parent])
// {
// std::swap(array[child],array[parent]);
// parent=child;
// child=parent*2+1;
// }
// else?
// return ;
// }
//
//}
//
//int ?head(int* array,int size)
//{
// for(int root=((size-2)/2);root>=0;root--)
// {
// Headadjust(array, size,root);
// }
//
// int end=size-1;
// while(end>0)
// {
// std::swap(array[0],array[end]);
// Headadjust(array, end,0);
// --end;
// }
// return 0;
//}
-
說到底,快速排序就是冒泡排序的一種改進,冒泡排序是通過每一趟冒泡將最大值(最小值)放到恰當位置,而快速排序則是每趟排序從待排序區間選一個基準值(也稱作樞紐值),將比它小的數據全放在其左邊,將比它大的值放在其右邊然后遞歸其左右子區間對其排序,一層層遞歸下去,某區直到間只剩一個數據時,停止遞歸,此子區間已經算是有序,繼而向其上層區間返回,一層層向上返回,當首次樞紐值的左右區間均已有序時,整個排序就算完成。
//k快速排序
//Vesion1
//int ?MID(int* array,int left,int right)
//{
// int begin=left;
// int end=right-1;
// int key=array[end];
// while(begin<end)
// {
// while(begin<end&&array[begin]<=key)
// begin++;
//
// while(begin<end&&array[end]>=key)
// end--;
//
// if(begin<end)
// {
// std::swap(array[begin],array[end]);
// }
//
// /*if(begin!=(right-1))
// {
// std::swap(array[begin],array[right-1]);
// }*/
// }
// return end;
//}
//void Quicksort(int array[],int left,int right)
//{
// if(left<right)
// {
// int b=left;
int e=right-1;
int key=array[e];
while(b<e)
{
while(b<e&&array[b]<=key)
b++;
if(b<e)
array[e--]=array[b];
while(b<e&&array[e]>=key)
e--;
if(b<e)
array[b++]=array[e];
}
array[b]=key;
Quicksort(array,left,b);
Quicksort(array,b+1,right);
}
}
//int GetmidIndex(int array[],int left,int right)
//{
//
// int mid=(left+right)/2;
// if(array[left]<array[right])
// {
// if(array[left]>array[mid])
// return left;
// else if(array[right]<array[mid])
// return right;
// else
// return mid;
// }
// else
// {
// if(array[right]>array[mid])
// return right;
// else if(array[left]<array[mid])
// return left;
// else
// return mid;
// }
//}
//int ?Quicksort(int array[],int left,int right)
//{
// ?
// ? ?int mid = GetmidIndex(array, left, right);
// ? ?if (mid != right)
// ? ?{
// ? ? ? ? ? ?swap(array[mid], array[right]);//將中間值交換到此范圍最右邊,提高效率
// ? ?}
// ? ?int key = array[right];//一般將最
//
// int begin=left;
// int end=right;
// ? ?key=array[end];
// while(begin<end)
// {
//
// ? ?while(begin<end&&array[begin]<=key)
// begin++;
//
// while(begin<end&&array[end]>=key)
// end--;
//
// if(begin<end)
// std::swap(array[begin],array[end]); ? ?
// }
// if (array[right] <array[begin])
// ? ?{
// ? ? ? ?swap(array[begin], array[right]);
// ? ? ? ?return begin;
// ? ?}
// ? ?else
// ? ? ? ?return right;
//
//}
//void QuickSort1(int *array, int left, int right)//遞歸
//?
//{
// ? ?
// ? ? ? ?if (left < right)
// ? ? ? ?{
// ? ? ? ? ? ?int boundary = Quicksort(array, left, right);//關鍵字所在位置的下標
// ? ? ? ? ? ?QuickSort1(array, left, boundary-1);
// ? ? ? ? ? ?QuickSort1(array, boundary + 1, right);
// ? ? ? ?}
// }
//
//void sort(int* array,int left,int right)
//{
// stack<int> s;
// if(left<right)
// {
// int mid=Quicksort(array,left, right);
// if(left<mid-1)
// {
// s.push(left);
// ? ?s.push(mid-1);
// }
// if(mid+1<right)
// {
// s.push(mid+1);
// s.push(right);
// }
// while(!s.empty())
// {
// int end=s.top();
// s.pop();
// int begin=s.top();
// s.pop();
// mid=Quicksort(array,begin, end);
// if(begin<mid-1)
// {
// s.push(begin);
// s.push(mid-1);
// }
// if(mid+1<end)
// {
// s.push(mid+1);
// s.push(end);
// }
//
// }
// }
//}
一、歸并排序
1、基本思想
? ?歸并排序(Merge sort,臺灣譯作:合并排序)是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。
算法//
?void Merge(int* array,int left,int right,int* temp)
?{
int mid=(left+right)/2;
int b1=left;
? ? ?int end1=mid;
int b2=mid+1;
int end2=right;
int index=left;
while(b1<=end1&&b2<=end2)
{
if(array[b1]<=array[b2])
{
temp[index++]=array[b1++];
}
else
{
temp[index++]=array[b2++];
}
}
while(b1<=end1)
{
temp[index++]=array[b1++];
}
while(b2<=end2)
{
temp[index++]=array[b2++];
}
?}//void ?_sort(int* array,int left,int right,int* temp)
// {
// if(left<right)
// {
// int mid=(left+right)/2;
// _sort(array,left,mid,temp);
// _sort(array,mid+1,right,temp);
// Merge(array,left,right,temp);
// memcpy(array+left,temp+left,(right-left+1)*sizeof(array[0]));
// }
//
// }
原理:
1>按照類似快速排序的方法遞歸地將待排序序列依次劃分為兩個區間,區間只剩一個數停止劃分;
2>如果一個區間只剩一個數,我們可將其看做有序區間,然后對左右兩個小區間進行歸并,歸并后仍要保持區間的有序性;
3>同2>提到的方法我們每次將兩個有序的子區間歸并為一個大的有序區間,并返回給上一層遞歸;
4>直到所有劃分的區間歸并為一個有序序列,歸并排序就算完成。
一、計數排序
1、基本思想
???給定一組要排序的序列,找出這組序列中的最大值,然后開辟一個最大值加1大小的數組,將這個數組里面的元素全部置零,然后用這個數組統計出要排序的序列中各個元素出現的次數。等到統計完成的時候,排序就已經完成了。
圖解(假設升序):
2、算法執行步驟:1>找出待排序序列里的最大值max;
2>開辟一個大小為max+1的臨時數組tmp ,將數組元素的所有初值賦為0;
3>遍歷待排序序列,將序列中出現值作為下標,對tmp 數組的對應位置數據進行++;
4>上述操作完成后,tmp中統計了每個數據在待排序列中出現的次數;
3>最后,將tmp數組里值不為0的所有下標拷進原序列(注意同一個下標可能有多個重復值,都要進行拷貝,不能遺漏),排序就算完成。
3、計數排序的優化
? ? ? 從上面的例子中可以看出,3是待排序序列的最小值,在tmp數組中3 之前的位置并沒有什么卵用,那么基于節省空間的角度進行考慮,我們可以開辟更小的臨時數組統計次數,同樣能完成排序。
? ? 比如要對1000,999,1008這三數進行排序,按照普通的方法前998個空間都被浪費掉了,所以這時候我們對它進行優化。找出要排序的這組元素中的最大值和最小值,這樣就確定了這組元素的范圍,然后開辟這個范圍加1大小的數組,然后再將要排序的元素映射到這個新開辟的數組中就可以了。
結論:計數排序適用于數據比較集中的序列排序。
4、時間復雜度&&空間復雜度
? ? 計數排序是一種非比較的排序方法,它的時間復雜度是O(N+K),空間復雜度是0(K),其中K是要排序的數組的范圍。可以看出計數排序是一種以空間呢換取時間的方法。如果當K>N*logN的時候,計數排序就不是好的選擇了,因為基于比較排序的算法的下限是O(N*logN)。?
5、代碼實現
[cpp]?view plain?copy ? - #pragma?once??
- #include?<iostream>??
- #include?<assert.h>??
- using?namespace?std;??
- ??
- void?PrintArray(int*?a,size_t?n)??
- {??
- ????for?(size_t?i?=?0;?i?<?n;?++i)??
- ????{??
- ????????cout<<a[i]<<"?";??
- ????}??
- ????cout<<endl;??
- }??
- ??
- ??
- void?CountSort(int*?a,size_t?n)??
- {??
- ????//查找待排序序列的最大值??
- ????int?max?=?a[0];??
- ????for?(size_t?i?=?0;?i?<?n-1;?++i)??
- ????{??
- ????????if?(max?<?a[i+1])??
- ????????{??
- ????????????max?=?a[i+1];??
- ????????}??
- ????}??
- ??
- ????//開辟臨時數組用來統計數據出現次數??
- ????int*?tmp?=?new?int[max+1];??
- ????//對臨時數組初值賦為0??
- ????memset(tmp,0,sizeof(int)*(max+1));??
- ????//統計數據出現次數??
- ????for?(size_t?i?=?0;?i?<?n;?++i)??
- ????{??
- ????????tmp[a[i]]++;??
- ????}??
- ??
- ????//將數據考回原數組??
- ????int?index?=?0;??
- ????for?(int?i?=?0;?i?<=?max;?++i)??
- ????{??
- ????????while(tmp[i]--)??
- ????????{??
- ????????????a[index]?=?i;??
- ????????????++index;??
- ????????}??
- ????}??
- ????delete?[]tmp;??
- }??
- ??
- //計數排序的優化??
- void?CountSort1(int*?a,size_t?n)??
- {??
- ????//找到待排序序列的最大值和最小值??
- ????int?max?=?a[0];??
- ????int?min?=?a[0];??
- ????for?(size_t?i?=?0;?i?<?n-1;?++i)??
- ????{??
- ????????if?(max?<?a[i+1])??
- ????????{??
- ????????????max?=?a[i+1];??
- ????????}??
- ????????if?(min?>?a[i+1])??
- ????????{??
- ????????????min?=?a[i+1];??
- ????????}??
- ????}??
- ??
- ????//開辟適當空間的數組??
- ????int?range?=?max?-?min?+?1;??
- ????int*?tmp?=?new?int[range];??
- ????memset(tmp,0,sizeof(int)*(range));??
- ????for?(size_t?i?=?0;?i?<?n;?++i)??
- ????{??
- ????????tmp[a[i]-min]++;??
- ????}??
- ??
- ????//將數據考回原數組??
- ????int?index?=?0;??
- ????for?(int?i?=?0;?i?<?range;?++i)??
- ????{??
- ????????while(tmp[i]--)??
- ????????{??
- ????????????a[index]?=?i+min;??
- ????????????++index;??
- ????????}??
- ????}??
- ????delete?[]tmp;??
- }??
- ??
- void?TestCountSort()??
- {??
- ????int?a[]?=?{2,5,4,9,3,6,8,7,1,0};??
- ????int?a1[]?=?{73,20,93,43,55,14,28,65,39,81};??
- ????size_t?sz?=?sizeof(a)/sizeof(a[0]);??
- ????size_t?sz1?=?sizeof(a1)/sizeof(a1[0]);??
- ??
- ????CountSort(a,sz);??
- ????CountSort1(a1,sz1);??
- ????PrintArray(a,sz);??
- ????PrintArray(a1,sz1);??
- }??
-
2、基本思想
? ? 基數排序又稱"桶子法",它從低位開始將待排序的數按照這一位的值放到相應的編號為0到9的桶中。等到低位排完之后得到一個序列,再將這個序列按照次低位的大小進入相應的桶中。以此類推,直到將所有的位都排完之后,這組數就已經有序了。
圖解(假設升序):
3、算法執行步驟
1》統計待排序序列中最大的數是幾位數;
2》開辟一個與原數組大小相同的臨時數組tmp;
3》用一個count數組統計原數組中某一位(從個位向高位統計)相同的數據出現的次數;
4》用一個start數組計算原數組中某一位(從個位向高位計算)相同的數據出現的位置;
5》將桶中數據從小到大(由頂至底)用tmp數組收集起來;
6》循環3》4》5》直到所有位都被統計并計算過,然后用tmp收集起來;
7》將tmp數組的數據拷回原數組,排序完成。
圖解:
4、時間復雜度&&空間復雜度
? ? 基數排序是一種非比較排序,它的時間復雜度是O(N*digit),其中digit是這組待排序中的最大的數的數量級。基數排序的空間復雜度就是在分配元素時,使用的桶空間,所以基數排序的空間復雜度是O(N)。它是一種穩定的排序方法。
5、代碼實現
[cpp]?view plain?copy ?- #pragma?once??
- #include?<iostream>??
- #include?<assert.h>??
- using?namespace?std;??
- ??
- int?GetMaxDigit(int*?a,size_t?n)//得到數組最大值的位數??
- {??
- ????int?digit?=?1;??
- ????int?base?=?10;??
- ??
- ????for?(size_t?i?=?0;?i?<?n;?++i)??
- ????{??
- ????????//每次與幾位數的最小數據(10、100、1000……)進行比較,??
- ????????//優點是相同位數的數據只統計一次??
- ????????while(a[i]?>=?base)??
- ????????{??
- ????????????++digit;??
- ????????????base?*=?10;??
- ????????}??
- ????}??
- ??????
- ????return?digit;??
- }??
- ??
- void?LSDSort(int*?a,size_t?n)??
- {??
- ????assert(a);??
- ????int?base?=?1;??
- ????int?digit?=?GetMaxDigit(a,n);//統計位數??
- ????int*?tmp?=?new?int[n];//開辟臨時數組??
- ??
- ????while(digit--)??
- ????{??
- ????????int?count[10]?=?{0};??
- ????????//統計某位相同的數據出現次數??
- ????????for?(size_t?i?=?0;?i?<?n;?++i)??
- ????????{??
- ????????????int?index?=?a[i]/base?%10;??
- ????????????count[index]++;??
- ????????}??
- ??
- ????????int?start[10]?=?{0};??
- ????????//統計個位相同的數在數組a中出現的位置??
- ????????for?(size_t?i?=?1;?i?<?n;?++i)??
- ????????{??
- ????????????start[i]?=?count[i-1]?+?start[i-1];??
- ????????}??
- ??
- ????????memset(tmp,0,sizeof(int)*n);//為tmp數組初始化??
- ????????//從桶中收集數據(從小到大)??
- ????????for?(size_t?i?=?0;?i?<?n;?++i)??
- ????????{??
- ????????????int?index?=?a[i]/base?%10;??
- ????????????tmp[start[index]++]?=?a[i];??
- ????????}??
- ??
- ????????//將數據拷回原數組??
- ????????memcpy(a,tmp,sizeof(int)*n);??
- ????????base?*=?10;??
- ????}??
- ????delete?[]tmp;??
- }??
- ??
- void?PrintArray(int*?a,size_t?n)??
- {??
- ????for?(size_t?i?=?0;?i?<?n;?++i)??
- ????{??
- ????????cout<<a[i]<<"?";??
- ????}??
- ????cout<<endl;??
- }??
- void?TestLDSort()??
- {??
- ????int?a[]?=?{73,20,93,43,55,14,28,65,39,81};??
- ????size_t?sz?=?sizeof(a)/sizeof(a[0]);??
- ??
- ????LSDSort(a,sz);??
- ????PrintArray(a,sz);??
- }??
運行結果:
二、基數排序
1、基數排序的分類
LSD-- Least Significant Digit first(從低位向高位排)
MSD-- Most Significant Digit first(從高位向低位排)
由于原理差不多,此文只針對LSD進行分析。
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
頂1?踩0
總結
- 上一篇: vector的实现
- 下一篇: select poll 与epoll模