5类(8种)常用内部排序算法(适用于C、C++、Java)

/**

 * 5类(8种)常用内部排序算法(适用于C、C++、Java)

 *

 * 内部排序分类:

 * 1)插入排序(1.直接插入排序、2.希尔排序)

 * 2)交换排序(1.冒泡排序、2.快速排序)

 * 3)选择排序(1.直接选择排序、2.堆排序)

 * 4)归并排序

 * 5)分配排序(基数排序)

 *

 * author 炜sama

*/


(1)平均时间性能:以快速排序法最佳,但最坏情况下不如堆排序和归并排序在n较大时,归并排序比堆排序快,但所需辅助空间最多

(2)简单排序以直接插入排序最简单,当记录“基本有序“或n值较小时,是最佳的排序方法。因此常和其他排序方法结合使用。

(3)基数排序最适用于n值很大而关键字较小的序列。若关键字也很大,而序列中大多数记录的”最高位关键字”均不同,则也可以先按“最高位关键字”不同将序列分成若干个子序列,而后用直接插入排序。

(4)从稳定性来看,基数排序是稳定的排序方法,大部分时间复杂度为O(n2)的简单排序法都是稳定的。然而,快速排序、堆排序和希尔排序等时间性能较好的排序都是不稳定的

以下是排序算法的实现,其中交换函数

void swap(int a[], int low, int high )

{

int temp = a[low];

a[low] = a[high];

a[high] = temp;

}

除了基数排序在Java中要做小改外,其他函数可以适用于C/C++、Java(当然用于Java时还可以缩减形参个数并精简代码)。

 1.直接插入排序

1)基本思想:在要排序的一组数中,假设前面(n-1)[n>=2] 个数已经是排好顺序的,现在要把第n个数插到前面的有序数中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。

2)实例



void insertSort( int a[], int n )

{

int i,j;

for( i = 1; i < n; i++ )

{

for(j=i;j>0&&a[j]<a[j-1];j--)

swap( a, j, j-1 );

}

}


1.2 @@希尔排序(最小增量排序)

1)基本思想:算法先将要排序的一组数按某个增量dn/2,n为要排序数的个数)分成若干组,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1,进行直接插入排序后,排序完成。

2)实例:


void shellSort( int a[], int n )

{

for(int gap = n/2; gap>=1; gap /= 2)//最外层循环负责决定步长

for(int i=gap; i<n; i++)//2层循环对每组进行直接插入排序

//内循环是直接插入排序

for(int j = i; j>=0 && a[j]<a[j-gap]; j -= gap)

swap(a,j,j-gap);

}

2.1 冒泡排序

1)基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。

2)实例:


void bubbleSort( int a[], int n )

{

for(int i=1;i<n;i++)//表示最多比较n-1次

for(int j=0;j<n-i;j++)//从前往后排序

if(a[j]>a[j+1])

swap(a,j,j+1);

}

2.2 @@快速排序

1)基本思想:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。递归结束条件:l>=r

2)实例:


void quickSort(int a[], int left, int right){

int l=left;//保存left

int r=right;//保存right

int p=a[l];//中轴的值

if(l>=r)//长度不够或者递归结束标志

return;

while(l<r){

//以下先右后左顺序不能调换!因为默认中轴为l,如果调换了大事不妙!

while(a[r]>=p&&l<r)r--;//从后往前扫,比中轴小的放到中轴左边

a[l]=a[r];

while(a[l]<=p&&l<r)l++;//从前往后扫,比中轴大的放到中轴右边

a[r]=a[l];

}

a[l]=p;//一趟排序完成时,l=r处为空位,把中轴放到那里

//继续递归调用排序

quickSort(a,left,l-1);//中轴不用再参与下一轮排序

quickSort(a,l+1,right);//中轴不用再参与下一轮排序

}

3.1 简单选择排序

1)基本思想:在要排序的一组数中,选出最小的一个数与第一个位置的数交换;然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止

2)实例:


void selectSort(int a[], int n){

for (int i=0;i<n-1;i++){//n-1趟排序

int l=i;//默认最小值为i下标元素

for (int j=n-1;j>i;j--)//j从后往前遍历

if(a[j]<a[l])

l=j;//找到比a[l]更小的值下标

swap(a,i,l);

}

}

3.2 @@堆排序

1)基本思想:堆排序是一种树形选择排序,是对直接选择排序的有效改进。

堆的定义如下:具有n个元素的序列(h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1(i=1,2,...,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。

2)实例:

初始序列:46,79,56,38,40,84

建堆:


交换,从堆中踢出最大数


剩余结点再建堆,再交换踢出最大数


依次类推:最后堆中剩余的最后两个结点交换,踢出一个,排序完成。

/*

以下堆排序算法包括两个函数:heapAdjust+heapSort

堆排序就是以下两个过程的反复操作:

1、建堆;

2、交换堆顶元素和堆底元素。

直到最后堆中剩余的最后两个结点交换,踢出一个,排序完成。

 */

//对数组从0到end建大顶堆

void heapAdjust(int a[], int end) {

for(int i=(end-1)/2;i>=0;i--){//(end-1)/2表示最后一个非叶子结点。往前搜索其他根结点

int f=i;//f保存正在判断的节点

//先比较当前结点f的左右孩子结点,记下较大者下标cmax,然后比较a[f]与a[cmax],

//大者交换为父结点,小者作为下一层的父结点再继续比较下去

while(f*2+1<=end){//如果当前k节点的孩子节点存在

int cmax=2*f+1;//f节点的左孩子节点的索引默认以此为较大的孩子结点的下标。

if(cmax+1<=end){//cmax+1代表的k节点的右孩子,如果右孩子结点存在

if(a[cmax]<a[cmax+1]){//若右孩子节点的值较大

cmax++;//那么右孩子才是cmax

}

}

if(a[f]<a[cmax]){//如果f节点的值小于cmax结点

swap(a,f,cmax);

f=cmax;//以cmax为父继续比较下去

}else

break;//如果f本身就是最大的,就没必要再循环下去了!

}

}

}

//堆排序

void heapSort(int a[],int n){

for(int i=0;i<n-1;i++){//循环建堆、输出堆顶元素。n-1趟排序

heapAdjust(a,n-1-i);//当前堆的末尾元素下标为n-1-i

swap(a,0,n-1-i);//交换堆顶和最后一个元素

}

}

4 @@归并排序

1)基本排序:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

2)实例:


/*

以下归并排序算法包括两个函数:merge+mergeSort

将数组分为两半,对每一边进行归并排序,以此递归下去,直到只剩下两个或者一个元素时停止递归。

先将原数组两边元素的较小者复制到一个缓存数组,从小到大排好后再复制回来。

*/

//合并已经排好序的两边数组

void merge(int a[],int c[], int l, int m, int r) {

int mid=m+1;//右边数组的开始下标

int i=l;//i记录缓存数组的索引

int ci=l;//复制缓存数组到原数组时用到的下标

while(l<=m&&mid<=r)

//从两边数组中取出较小的放入缓存数组

if(a[l]<=a[mid])

c[i++]=a[l++];

else

c[i++]=a[mid++];

//循环结束后一定有一边已经完全搬到了缓存数组,但不知道是哪一边,

//只需把剩余部分依次放入缓存数组即可,顺序随便,因为下面的循环只有一个会执行

while(l<=m)

c[i++]=a[l++];

while(mid<=r)

c[i++]=a[mid++];

//将缓存数组中的内容复制回原数组

while(ci<=r)

a[ci]=c[ci++];

}

//归并排序

void mergeSort(int a[],int c[], int l, int r) {

if(l<r){

int m=(l+r)/2;//找出中间索引

mergeSort(a,c,l,m);//对左边数组进行递归

mergeSort(a,c,m+1,r);//对右边数组进行递归

merge(a,c,l,m,r);//合并

}

}

5 @@基数排序

1)基本思想:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

2)实例:


/*

以下基数排序算法包括两个函数:maxbit+radixSort

多关键字排序。以10进制整数为例:将整形按每位拆分,然后从低位到高位依次比较各个位。主要分为两个过程:

(1)分配,先从个位开始,根据位值(0-9)分别放到0~9号桶中(比如53,个位为3,则放入3号桶中)

(2)收集,再将放置在0~9号桶中的数据按顺序放到数组中

重复(1)(2)过程,从个位到最高位(比如32位无符号整形最大数4294967296,最高位10位)

*/

//求数据的最大位数

int maxbit(int a[], int n){

int d = 1;

int max=a[0];

for (int i = 0; i < n; i++) {

if(a[i]>max)

max=a[i];

}

while(max/10 > 0){

d++;

max/= 10;

}

return d;

}

//基数排序

void radixSort(int a[], int n){

int d = maxbit(a, n);//最大位宽,即关键字个数

int * temp = new int[n];//缓存数组。Java:int[] temp=new int[n];

int * count = new int[10];//计数器,又叫桶。Java:int[] count=new int[10];

int i, j, k;

int bit = 1;//位,即个位1,十位10,百位100等等

for (i = 0; i < d; i++){//进行d次排序

for (j = 0; j < n; j++)

count[j] = 0;//每次分配前清空计数器

//分配过程

for (j = 0; j < n; j++){

k = (a[j]/bit)%10;//求出当前数的当前分配位,对应桶的下标

count[k]++;//对应的桶记录数+1

}

for (j = 1; j < 10; j++)

count[j]=count[j-1]+count[j];//@@难点!求得的count[j]是桶j中的最后一个元素在temp[n]中的序号。

                                 //注意是序号不是下标!序号=下标+1,因为数组元素下标从0开始!

                                 //所以在下面的收集过程中count[j]-1才是元素真正的下标!

//收集过程。将桶中的记录收集到temp中。@@注意必须要逆序收集!不然所得排序结果是不稳定的!

for (j = n-1; j >= 0; j--){

k = (a[j]/bit)%10;//求出a[j]所在的桶下标

count[k]--;//从桶中拿出一个元素,-1后的count[k]代表被拿出的元素在tenp中的下标!

temp[count[k]] = a[j];

}

//将临时数组的内容复制到a

for(j = 0; j < n; j++)

a[j] = temp[j];

bit*=10;

}

delete [] temp;//Java代码应该删掉这行!

delete [] count;//Java代码应该删掉这行!

}

/* 测试结果 */

int main(){

int i;

const int n=10;

int ia[]={9,8,7,6,5,4,3,2,1,0};

insertSort(ia,n);

for(i=0;i<n;i++)

cout<<ia[i]<<",";

cout<<endl;

int ia2[]={9,8,7,6,5,4,3,2,1,0};

shellSort(ia2,n);

for(i=0;i<n;i++)

cout<<ia2[i]<<",";

cout<<endl;

int ia3[]={9,8,7,6,5,4,3,2,1,0};

bubbleSort(ia3,n);

for(i=0;i<n;i++)

cout<<ia3[i]<<",";

cout<<endl;

int ia4[]={9,8,7,6,5,4,3,2,1,0};

quickSort(ia4,0,n-1);

for(i=0;i<n;i++)

cout<<ia4[i]<<",";

cout<<endl;

int ia5[]={9,8,7,6,5,4,3,2,1,0};

selectSort(ia5,n);

for(i=0;i<n;i++)

cout<<ia5[i]<<",";

cout<<endl;

int ia6[]={9,8,7,6,5,4,3,2,1,0};

heapSort(ia6,n);

for(i=0;i<n;i++)

cout<<ia6[i]<<",";

cout<<endl;

int ia7[]={9,8,7,6,5,4,3,2,1,0};

int ia7c[n];

mergeSort(ia7,ia7c,0,n-1);

for(i=0;i<n;i++)

cout<<ia7[i]<<",";

cout<<endl;

int ia8[]={987,876,765,654,532,432,321,12,198,12};

radixSort(ia8,n);

for(i=0;i<n;i++)

cout<<ia8[i]<<",";

cout<<endl;

return 0;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值