1.直接插入排序
算法原理: 每次在無序序列中選取首個元素,依次與有序元素進行比較,然後將其放在合適的位置。該算法穩定
Code:
void insertSort(int arr[],int n)
{
int temp,i,j;
for(i=1;i<n;++i)
{
temp = arr[i];
j=i-1;
while(j>=0 && temp<arr[j])
{
arr[j+1] = arr[j];
--j;
}
arr[j+1] = temp;
}
}
2.簡單選擇排序
算法原理: 每次在無序數組中挑出最小元素將其直接放置在其最終位置。注意此處爲簡單選擇排序算法之對換法,常用於數組結構,是一種不穩定的排序算法。與之相對應的是移動元素法,常用於鏈表的排序操作,是一種穩定的算法。
Code:
void simpleSelectSort(int arr[] , int n)
{
int i,j,k;
int temp;
for(i=0;i<n;++i)
{
k = i;
for(j=i+1;j<n;++j)
{
if(arr[k]>arr[j])
{
k = j;
}
}
tmep = arr[k];
arr[k] = arr[i];
arr[i] = temp;
}
}
3.冒泡排序
算法原理: 冒泡排序是指將最大或者最小元素通過依次交換使其放在其有序數組的最終位置,然後縮小無序數組的表示範圍,通過n次二重循環,應該做到將數組完成排序。是穩定算法
Code:
void bubbleSort(int arr[],int n)
{
int i,j,temp;
int flag;
for(i=n-1;i>=0;--i)
{
flag = 0;
for(j=0;j<i;++j)
{
if(arr[j]>arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = arr[j];
flag = 1;
}
}
if(flag==0)
break;
}
}
4.希爾排序
算法原理:
將待排序列劃分成若干子序列,子序列中元素的距離爲一增量gap
,在每次排序過程中將該子序列排列有序,然後以一定的規律減小gap
的值,在本次代碼實現中將在每次取gap值的1/2
。設存在下圖所示的數據數組,下面使用希爾排序依次演示
- 原表
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
49 | 38 | 65 | 97 | 76 | 13 | 27 | 49 | 55 | 4 |
- 第一次(
gap=10/2=5
)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
13 | 27 | 49 | 55 | 4 | 49 | 38 | 65 | 97 | 76 |
- 第二次(
gap=5/2=2
)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
4 | 27 | 13 | 49 | 38 | 55 | 49 | 65 | 97 | 76 |
- 第三次(
gap=2/2=1
)
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
4 | 13 | 27 | 38 | 49 | 49 | 55 | 65 | 76 | 97 |
此時整個序列業已有序,已經完成了排序,可以發現希爾排序的最後一輪排序爲冒泡排序,顯然其相比於單純的bubbleSort算法要更加優秀。需要注意的是雖然在此實例中兩個49
前後相對位置雖然未發生變化,看起來好像仍然是穩定的算法,但實際上由於其實現手段的原因,其實其是一個不穩定的算法。
代碼:
void shellSort(int arr[],int n)
{
int i,j,temp;
for(int gap=n/2;gap>0;gap/=2)
{
for(i=gap;i<n;++i)
{
temp = arr[i];
for(j=i;j>=gap&&arr[j-gap]>arr[j];j-=gap)
arr[j] = arr[j-gap];
arr[j] = temp;
}
}
}
5.快速排序
算法原理: 快排的partition函數舉世聞名,不多說了,溜了。時間複雜度:O(NlogN)
6.堆排序
算法原理: 將待排序列搞爲大根堆或者小根堆之後,將根頂元素放到最後面,然後重新調節爲小一個元素的堆即可,首先瞅一瞅從數組下標爲1開始存儲數據的大根堆的插入操作:
code:
class Heap{
vector<int> a;//存儲數據的數組
public:
Heap(){
a.push_back(0);//從1號位置開始
}
void insert(int data){
a.push_back(data);
int i = a.size()-1;
while(i/2>0 && a[i]>a[i/2]){//插入,自下向上堆化
swap(a[i],a[i/2]);
i /= 2;
}
}
void heapify(int n, int i);//從上至下堆化過程
void removeMax(); //刪除堆中最大元素
void buildHeap(vector<int> &a);//建堆
heapsort(vector<int> &a);//堆法排序
};
從上至下堆化過程
void Heap::heapify(int n,int i){
while(true){
int maxPos = i;
if(i*2<=n && a[i]<a[i*2]) maxPos = i*2;
if(i*2+1<=n && a[i*2+1]>a[maxPos]) maxPos = i*2+1;
if(maxPos==i) break;
swap(a[i],a[maxPos]);
i = maxPos;
}
}
刪除堆頂元素
void Heap::removeMax(){
if(a.size()==1)//下標爲0處不放數據
return -1;
a[1] = a[a.size()-1];
a.pop_back();
heapify(a.size()-1,1);
}
建堆
void Heap::buildHeap(vector<int> &a){
for(int i=(a.size()-1)/2;i>0;--i){
heapify(a.size()-1,i);
}
}
堆排序
void Heap::heapsort(vector<int> &a){
bulidHeap(a);
int k = a.size()-1;
while(k>1){
swap(a[1],a[k]);
--k;
heapify(k,1);
}
}
好了,以上就是一些關於堆排序的基本操作,謹記堆排序插入時自下向上最好,而刪除以及建堆時自上至下的調整是科學的否則會造成空洞現象,最後,建堆時間複雜度爲O(n),而堆排序爲O(NlogN).
7.歸併排序
遞歸的思路
void merge(int arr[],int low,int mid,int high){
int help[high-low+1];
int i=0;
int p1 = low;
int p2 = mid+1;
while(p1<=mid&&p2<=high){
help[i++] = arr[p1]<=arr[p2]?arr[p1++]:arr[p2++];
}
while(p1<=mid){
help[i++] = arr[p1++];
}
while(p2<=high){
help[i++] = arr[p2++];
}
for(i=0;i<high-low+1;i++){
arr[low+i] = help[i];
}
}
歸併排序主代碼
void mergeSort(int arr[],int low,int high)
{
if(low<high)
{
int mid = (low+high)/2;
mergeSort(arr,low,mid);
mergeSort(arr,mid+!,high);
merge(arr,low,mid,high);
}
}
8.桶排序
基數排序原理: 時間複雜度O(N)
核心思想是將要排序的數據劃分到幾個有序的桶裏面,然後對每一個桶我們使用快排或者歸併使他們有序,然後取出每一個桶裏面的元素即可。是外部排序的利器
9.計數排序
只是用在數據範圍不大的場景之中,且只能給非負整數排序,侷限性有點大。但好處是比較快。實現特別巧妙,極客時間上講的不錯。O(N)
10.基數排序
按每一數位進行排序,最知名的例子就是手機號碼排序,就是從號碼低位到高位,每次按這一位使用桶排序進行排序,最終就會完成排序。
後面三個排序可參考以下鏈接極客時間:數據結構與算法