網上關於排序算法的總結太多了,這篇文章就寫得不錯。
http://m.blog.csdn.net/blog/likaiwalkman/23713373
經典就是經典,個人覺得這些經典的算法被反覆研究幾十遍都不爲過。同時也參考了很經典的書籍《數據結構與算法分析——C語言描述》,溫故而知新,每次回頭看這些算法的時候都爲其中博大精深的思想所折服,呵呵,不扯了。這裏只貼出一份用代碼敲出來的各個排序算法,純粹是想練一下手。主要關注快排和堆排序。
交換排序
快速排序
快排的算法思想很精妙,經常在倆指針問題中用到。
假設待排序的元素個數爲n,分別存放在數組a[1,…,n]中,令第一個元素爲參考元素(稱爲樞紐元,pivot),pivot=a[1],初始時,i=1,j=n,按照以下方法操作:
(1) 從第j個元素開始向前依次將每個元素與樞紐元pivot比較。如果當前元素大於等於pivot,則比較前一個元素與pivot,即比較a[j-1]與pivot;否則,將當前元素移動到第i個位置,並轉到步驟(2)。
(2) 從第i個元素開始向後依次將每個元素與樞紐元pivot比較。如果當前元素小於pivot,則比較後一個元素與pivot,即比較a[i+1]與pivot;否則,將當前元素移動到第j個位置,並轉到步驟(3)。
(3) 重複執行(1)和(2),直到出現i>=j,將元素移動到a[i]中。此時,整個元素序列被劃分成兩個部分,第i個位置之前的元素都小於a[i],第i個位置後的元素都大於等於a[i]。至此,完成一趟快速排序。
按照以上的方法,將每個部分都進行類似的劃分操作,直到每個部分都只有一個元素爲止,這樣整個元素序列就構成了一個有序的序列。
#include<iostream>
using namespace std;
void print(int a[],int n)
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
void quicksort(int a[],int start,int end)//對a[start]到a[end]的元素進行排序
{
int i=start,j=end;
int temp;
if(start<end)
{
temp=a[start];
while(i!=j)
{
while(j>i&&a[j]>=temp)
j--;
a[i]=a[j];
while(i<j&&a[i]<=temp)
i++;
a[j]=a[i];
}
a[i]=temp;
quicksort(a,start,i-1);
quicksort(a,i+1,end);
}
}
int main()
{
int a[]={17,1,5,6,98,78,79,54,32,11,15,14,13,20,29};
int n=sizeof(a)/sizeof(a[0]);
quicksort(a,0,n-1);
print(a,n);
return 0;
}
冒泡排序
#include<iostream>
using namespace std;
void print(int a[],int n)
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
int main()
{
int a[]={1,5,78,94,48,34,32,47,55,96,19,5};
int n=sizeof(a)/sizeof(a[0]);
int i,j,temp,flag=1;
for(i=0;i<n&&flag;i++)
{
flag=0;
for(j=0;j<n-i;j++)
{
if(a[j]>a[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
flag=1;
}
}
print(a,n);
}
return 0;
}
選擇排序
選擇排序的算法思想很簡單,從待排序的元素中選擇最小(最大)的元素放在已經排序的序列的最後,直到全部排完爲止。
堆排序
關於堆排序,C++的STL中已經提供了很成熟的函數,像入堆push_heap、創建堆make_heap、出堆pop_heap,判斷是否爲堆is_heap,堆排序sort_heap等,實際應用中可以直接調用。但是,算法的思想還是要掌握的。
假設一個大根堆中有n個元素,如果將堆中的根節點元素輸出後,再將剩下的n-1個元素重新建立成一個新堆,並將根節點元素輸出,然後將剩下的n-2元素重新建立成堆,重複執行以上過程,直到堆中沒有元素爲止。所以堆排序的關鍵就是創建堆和調整堆。
建立大根堆的算法思想大概過程是這樣的。從位於元素序列中的最後一個一個非葉子結點,即第n/2向下取整個元素開始,逐層比較並調整元素的位置,使其滿足a[i]>=a[2*i]且a[i]>=a[2*i+1],直到根節點爲止。假設當前結點的序號爲i,則當前元素爲a[i],其左右孩子結點分別爲a[2*1]和a[2*i+1]。將a[2*1]和a[2*i+1]較大者與a[i]比較,如果孩子結點元素值大於當前結點值,則交換兩者;否則,不進行交換。逐層向上執行此操作,直到根節點爲止,這樣就建立了一個大根堆。建立小根堆的算法與此類似。
調整堆其實也是重新建立堆的過程。除了堆頂元素外,剩下的元素本身就具有a[i]>=a[2*i]且a[i]>=a[2*i+1],(i=1,2,3,…,[n/2]向下取整)的性質,也就是說剩下的元素由大到小逐層排列。所以,將剩下的元素調整成大根堆只需要從上往下逐層比較,找出最大的元素將其放在根節點的位置上就可以構成新的大根堆了。
調整堆的大概過程是這樣的。輸出堆頂元素可以將堆頂元素放在堆的最後,也就是將第一個元素與最後一個元素交換,即a[1]與a[n]交換一下,那麼,接下來需要調整的元素就是a[1,…,n-1]。從根節點開始,如果其左、右子樹結點元素值大於根節點元素值,選擇較大的一個與根節點進行交換,否則,不交換。逐層重複執行這個操作,直到葉子結點,就完成了堆的調整,構成了一個新的堆。
#include<iostream>
//#include<algorithm>
using namespace std;
void print(int a[],int n)
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
void adjustheap(int a[],int low,int high)//調整a[low,high],使其成爲堆
{
int i=low,j=2*i;//j指向i的左孩子結點
int temp=a[i];//根節點暫存臨時變量中
while(j<=high)
{
if(j<high&&a[j]<a[j+1])//右孩子較大
j++;
if(temp<a[j])
{
a[i]=a[j];
i=j;//修改i和j的值,以便繼續向下篩選
j=2*i;
}
else
break;
}
a[i]=temp;
}
/*void adjustheap(int a[],int s,int m)
{
int t,j;
t=a[s];
for(j=2*s+1;j<=m;j*=2+1)
{
if(j<m&&a[j]<a[j+1])
j++;
if(t>a[j])
break;
a[s]=a[j];
s=j;
}
a[s]=t;
} */
void heapsort(int a[],int n)
{
int i,temp;
for(i=n/2;i>=1;i--)//創建堆
adjustheap(a,i,n);
for(i=n;i>1;i--)
{
temp=a[1];
a[1]=a[i];
a[i]=temp;
adjustheap(a,1,i-1);
}
}
int main()
{
int a[]={0,11,12,54,47,86,87,98,75,34,70};
int n=sizeof(a)/sizeof(a[0]);
//make_heap(a,a+n);
//sort_heap(a,a+n);
heapsort(a,n-1);
print(a,n);
return 0;
}
注:細心的人,會發現上面的程序有個問題,在創建和調整堆的過程中,下標的對應容易出問題,即數據下標是從1開始的。調用C++ STL中的相關函數接口,就沒這個問題。上面的程序是對着教材敲的,沒仔細看這個問題,糾結了好久,呵呵。
這個版本就沒問題。
#include <stdio.h>
void exch(int& a, int &b)
{
int tmp = a;
a = b;
b = tmp;
}
void fixdown(int a[], int k, int N)
{
while(2*k <= N)
{
int j=k*2;
if(j<N && a[j]<a[j+1]) j++;
if(!(a[k]<a[j])) break;
exch(a[k], a[j]);
k = j;
}
}
void heapsort(int a[], int l, int r)
{
int k, N=r-l+1;
int *pq = a+l-1; //notice!
for(k = N/2; k>=1; k--)
fixdown(pq, k, N);
while(N>1)
{
exch(pq[1], pq[N]);
fixdown(pq, 1, --N);
}
}
int main()
{
int a[10] = {11,12,54,47,86,87,98,75,34,70};
heapsort(a, 0, 9);
for(int i=0; i<10; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
注:參考了這篇文章
http://www.cnblogs.com/wouldguan/archive/2012/11/12/2766070.html
簡單選擇排序
#include<iostream>
using namespace std;
void print(int a[],int n)
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
void selectsort(int a[],int n)
{
int i,j,k;
int temp;
for(i=0;i<n-1;i++)
{
k=i;
for(j=i+1;j<n;j++)
{
if(a[j]<a[k])
k=j;//k記錄最小元素的位置
}
if(k!=i)
{
temp=a[i];
a[i]=a[k];
a[k]=temp;
}
}
}
int main()
{
int a[]={11,12,54,47,86,89,98,75,34,59};
int n=sizeof(a)/sizeof(a[0]);
selectsort(a,n);
print(a,n);
return 0;
}
插入排序
直接插入排序
//直接插入排序,屬於穩定的排序算法
#include<iostream>
using namespace std;
void print(int a[],int n)
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
int main()
{
int a[]={17,46,32,15,1,10,6,7,9,18};
int n=sizeof(a)/sizeof(a[0]);
int temp,i,j;
for(i=1;i<n;i++)
{
temp=a[i];//取出待排元素放入臨時變量
for(j=i-1;j>=0&&temp<a[j];j--)//尋找插入位置
{
a[j+1]=a[j];
}
a[j+1]=temp;
print(a,n);
}
return 0;
}
折半插入排序
#include<iostream>
using namespace std;
void print(int a[],int n)
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
int main()
{
int a[]={1,48,46,57,98,96,16,13,18,29,30,45,47};
int n=sizeof(a)/sizeof(a[0]);
int temp,i,j,low,high,mid;
for(i=1;i<n;i++)
{
temp=a[i];//待排元素放入臨時變量
for(low=0,high=i-1;high>=low;)//尋找插入位置
{
mid=(low+high)/2;
if(temp<a[mid])
high=mid-1;
else
low=mid+1;
}
for(j=i-1;j>=low;j--)//元素移動
a[j+1]=a[j];
a[low]=temp;
print(a,n);
}
return 0;
}
希爾排序
與直接插入排序、折半插入排序相比,希爾排序將待排序的元素劃分爲若干個子序列,在每個組內進行直接插入排序。
#include<iostream>
using namespace std;
void print(int a[],int n)
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
int main()
{
int a[]={1,5,78,94,48,34,32,47,55,96,19,5};
int n=sizeof(a)/sizeof(a[0]);
int i,j,inc,temp;
for(inc=n/2;inc>0;inc/=2)
{
for(i=inc;i<n;i++)
{
temp=a[i];//取待排元素放入臨時變量
for(j=i;j>=inc;j-=inc)//組內進行插入排序,尋找插入位置
{
if(temp<a[j-inc])
{
a[j]=a[j-inc];
}
else
break;
}
a[j]=temp;
print(a,n);
}
}
return 0;
}
歸併排序
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
using namespace std;
void print(int a[],int n)
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
void copy(int source[],int dest[],int len,int start)
{
int i,j=start;
for(i=0;i<len;i++)
{
dest[j]=source[i];
j++;
}
}
void merge(int a[],int left,int right)
{
int begin1,begin2,mid,k=0,len,*b;
begin1=left;
mid=(left+right)/2;
begin2=mid+1;
len=right-left+1;
b=(int*)malloc(len*sizeof(int));
while(begin1<=mid&&begin2<=right)
{
if(a[begin1]<=a[begin2])
b[k++]=a[begin1++];
else
b[k++]=a[begin2++];
}
while(begin1<=mid)
b[k++]=a[begin1++];
while(begin2<=right)
b[k++]=a[begin2++];
copy(b,a,len,left);
}
void mergesort(int a[],int left,int right)
{
int i;
if(left<right)
{
i=(left+right)/2;
mergesort(a,left,i);
mergesort(a,i+1,right);
merge(a,left,right);
}
}
int main()
{
int a[]={11,12,54,47,86,89,98,75,34,59};
int n=sizeof(a)/sizeof(int);
mergesort(a,0,n-1);
print(a,n);
return 0;
}
除了以上幾種算法以外,還有基數排序、桶排序、外部排序等等,不想寫了,知道排序的思想就行。