内排序
提供插入排序、冒泡排序、选择排序、Shell排序、归并排序、快速排序、堆排序、分配排序、基数排序的实现代码(附注释)和图解.
插入排序
//顾名思义,每处理一组新的数据,
//都需要吧这个数据加到之前已经是有序的部分中
//时间复杂度是O(n^2)
template<typename E>
void inssort(E A[],int n){
for(int i=1;i<n;i++){
for(int j=i;j>0;j--) //i之前都是有序的
if(A[j-1]>A[j]) //仅仅需要一个一个与之前的进行比较
swap(A[j-1],A[j]);
}
}
冒泡排序
//自下往上,从j处一个一个同上面的元素进行比较,
//满足则交换
//时间复杂度是O(n^2)
template<typename E>
void bubsort(E A[],int n){
for(int i=0;i<n-1;i++)
for(int j=n-1;j>i;j--)
if(A[j]<A[j-1]) //逐个与之前的一个进行比较
swap(A[j],A[j-1]);
选择排序
//从数组中选择最小的,然后放到数组的第一个位置
//然后选取次小的放到第二个位置,循环至有序
//时间复杂度是O(n^2)
template<typename E>
void selsort(E A[],int n){
for(int i=0;i<n-1;i++){
int lowIndex=i; //记为本次循环最小的
for(int j=n-1;i>i;j--)
if(A[lowIndex]>A[j])
lowIndex=j;
swap(A[lowIndex],A[i]);
}
}
Shell排序
//Shell试图将待排序序列变成基本有序的,
//再利用插入排序完成
//处理n个长度的数组时,依次比较相邻为n/2、n/4、……、1个长度的子序列
//时间复杂度是O(n^1.5)
//仅仅对一个截至下标为n,相隔(长度)为incr的子序列进行排序
template<typename E>
void inssort2(E A[],int n,int incr){
//incr是分割后子序列的增量
for(int i=incr;i<n;i+=incr)
for(int j=i;j>=incr;j-=incr)
if(A[j]<A[j-incr])
swap(A[j],A[j-incr]);
}
template<typename E>
void shellsort(E A[],int n){
for(int i=n/2;i>=2;i/=2) //i是子序列长度
for(int j=0;j<i;j++)
inssort2(&A[j],n-j;i);
inssort2(A,n,1);
}
归并排序
//运用分治法,将待排序的列表分成片段
//再处理个片段,最后通过某种方式将片段重组
//时间复杂度是O(nlogn)
template<typename E>
void MergeSortHelp(E A[],E reg[],int left,int right){
//temp用来存放有序的序列
if(left==right) return;
int mid=(right+left)/2;
//分别做有进行迭代
mergesort(A,reg,left,mid);
mergesort(A,reg,mid+1;right);
int k=left;
//将两个序列从头往后进行比较,
//将两个序列中较小的加到有序数组reg中
while(start1<=end1 && start2<=end2)
reg[k++]=arr[start1]<arr[start2] ?
arr[start1++] : arr[start2];
//若序列2到达了结尾但是序列1仍有数据未加入有序reg中
//由于只有一个,直接加入
while(start1<=end1)
reg[k++]=arr[start1++];
//若序列1到达了结尾但是序列2仍有数据未加入有序reg中
//由于只有一个,直接加入
while( start2<=end2)
reg[k++]=arr[start2++];
//对原数组进行覆盖
for(k=left;k<=right;k++)
arr[k]=reg[k];
delete[] reg;
}
void mergesort(E A[],const int n){
E reg[n];
MergeSortHelp(A,reg,0,n-1);
}
快速排序
//选定一个轴值,由这个轴值将数组分成两部分
//左侧元素都比轴值小,右侧均大于或等于轴值
//重复操作至排序完成
//时间复杂度O(nlog2n)
template<typename E>
void QuickSortOnce(int A[],int first,int end){
int i=first,j=end;
while(i<j){ //找轴值--尽量确保之在中间位置
while(i<j && [i]<=A[j]) j--; //在满足条件的情况下先对j进行往前移动
if(i<j) //i<j满足则说明A[i]>A[j]
swap(arr[i],arr[j]);
//否则满足A[i]<=A[j],则i=j,,不执行i++操作直接结束,找到轴值
while(i<j && A[i]<=A[j]) i++; //在满足条件的情况下再对i进行往后移动
if(i<j)
swap(arr[i],arr[j]);
}
//找到轴值(此时i==j,返回哪个都行)
return i;
}
template<typename E>
void QuickSortHelp(int A[],int first,int end){
int p=QuickSortOnce(A,first,end);
QuickSortOnce(A,first,p);
QuickSortHelp(A,p+1,end);
}
template<typename E>
void QuickSort(int A[],int n){
//长为n的数组A进行排序
QuickSortHelp(A,0,n-1);
}
堆排序
buildHeap()及removefirst()方法请参考我的另一篇博客
//利用最大堆实现从小到大排序
//通过逐渐移除最大堆的第一个元素并放到数组后面实现
//时间复杂度是O(n+klogn):建堆O(n)、移动堆顶元素是O(logn)
//k表示找到数组中第k大的元素
template<typename E>
void HeapSort(E A[],const int n){
heap<E> H(A,n,n);
H.buildHeap(); //以A为数据源建立堆H
for(int i=n-1;i>=0;i--)
A[i]=H.removefirst(); //该方法实现意思是移除堆顶的元素
}
分配排序
//通过一次遍历将总数据分别放到不同“盒子”中--通过关键码
//在对每个“”盒子中数据按照各自的进行排序
//即--通过记录关键码,直接取,直接放
//时间复杂度是O(n)--在满的情况下
//通过数组+链表实现
template<typename E>
void AlloSort(E A[],const int n){
E reg[n];
int t=0;
List<E> B[10]; //MaxKeyNum是能容纳关键码数量-- 0~9
E item;
for(int i=0;i<n;i++)
B[A[i]%10].push_back(A[i]);
for(int i=0;i<10;i++)
for(B[i].begin();B[i].getValue(item);B[i].next()){
reg[t++]=item;
}
for(int i=0;i<n;i++)
A[i]=reg[i];
}
//只适用于满的盒子
基数排序
//对分配排序的扩展,同样用到了关键码
//但是是将某个空的没有用的位置留给其他基数使用
//时间复杂度是 O(nk+rk)--对n和数据,r个基数,k轮分配工作
template<typename E>
void RadixSort(E A[],int n,int k,int r,
int cnt[]){
//cnt数组用于存放关键码
//n是A数组的长度
//k是要排序的数的位数--个位是1,千位是4……
//r表示进制数,一般对10进制排序r就取10(从0~9)
int j;
for(int i=0,rtoi=1;i<k;i++,rtoi*=r) //rtoi表示1~10~100~……
//初始化
for(j=0;j<r;j++) cnt[j]=0;
//统计各关键码(数字)出现的次数并放入cnt数组中
for(j=0;j<n;j++) cnt[(A[j]/rtoi)%r]++;
//计算各关键码(数字)在数组中的下标并将结果放入cnt数组
for(j=1;j<r;j++) cnt[j]=cnt[j-1]+cnt[j];
//依次从中取出并放入B数组形成有序的
for(j=n-1;j>=0;j--)
B[--cnt[(A[j]/rtoi)%r]]=A[j];
for(j=0;j<n;j++)
A[i]=B[j];
}