`
吃货吃货
  • 浏览: 31975 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

各大排序算法的实现及性能比较哦

    博客分类:
  • java
 
阅读更多

排序算法的实现及性能分析

——(java版)

排序是对数据元素序列建立某种有序排列的过程。更确切的说,排序是把一个数据元素序列整理成按关键字递增(或递减)排列的过程。

不过首先,我们必须先解释一下关键字这个词。关键字是要排序的数据元素集合中的一个域,排序是以关键字为基准进行的。而关键字也分为主关键字和次关键字。对于要排序的数据元素集合来说,如果关键字满足数据元素值不同时,该关键字也不同,这样的关键字称为主关键字。不满足主关键字定义的就称为次关键字。

简单来说,排序分为内部排序和外部排序两种。内部排序是把待排序的数据元素全部调入内存中进行的排序。如果数据元素的数量过大,需要分批导入内存,分批导入内存的数据元素排好序后在分批导出到磁盘和磁带外介质上的排序方法称为外部排序。在本篇博客中将只讨论分析内部排序算法的性能。

那么通常比较内部排序算法优劣的标准有如下3个:

<!--[if !supportLists]-->(1)<!--[endif]-->时间复杂度。时间复杂度是衡量排序算法好坏最重要的标准,它是一个函数,定量描述了某个算法的运行时间,时间复杂度通常使用大O符号表述,比如说直接插入排序算法的最差时间复杂度为O(n^2)。

<!--[if !supportLists]-->(2)<!--[endif]-->空间复杂度。空间复杂度用于衡量算法中使用额外内存空间的多少。当排序算法中使用的额外内存空间与要排序数据元素的个数n无关时,其空间复杂度为O(1),大多数排序算法的空间复杂度都为O(1)。

<!--[if !supportLists]-->(3)<!--[endif]-->稳定性。当使用主关键字排序时,任何排序算法对相同的数据元素序列的排序结果必定是相同的。但使用次关键字排序时,其排序结果有可能相同,也用可能不同。设待排序的数据元素共有n个,设Ki和Kj分表表示第i个数据元素的关键字和第j个数据元素的关键字,设Ri和Rj分别表示第i个数据元素和第j个数据元素。若Ki=Kj,且在排序之前数据元素Ri排在数据元素Rj之前,在排序之后数据元素Ri仍然排在数据元素Rj之前的排序算法称为稳定的排序算法,否则称为不稳定的排序算法。

常见的内部排序算法有以下几种:

<!--[if !supportLists]-->(1)<!--[endif]-->直接插入排序;

<!--[if !supportLists]-->(2)<!--[endif]-->希尔排序;

<!--[if !supportLists]-->(3)<!--[endif]-->直接选择排序;

<!--[if !supportLists]-->(4)<!--[endif]-->堆排序;

<!--[if !supportLists]-->(5)<!--[endif]-->冒泡排序;

<!--[if !supportLists]-->(6)<!--[endif]-->快速排序;

<!--[if !supportLists]-->(7)<!--[endif]-->归并排序;

<!--[if !supportLists]-->(8)<!--[endif]-->基数排序;

 

直接插入排序

基本思想:顺序地将待排序的数据元素按其值得大小插入到已排序的数据元素子集合的适当位置。

实现代码:

public class InsertSort {
	public int[] insertSort(int[] a){
		int i,j,temp;
		int n = a.length;
		for(i = 0;i < n-1;i++){
			temp = a[i+1];
			j = i;
			while(j > -1  && temp <= a[j]){
				a[j+1] = a[j];
				j--;
			}
			a[j+1] = temp;
		}
		return a;
	}
}

 

直接插入排序算法的时间复杂度在O(n)和O(n^2)之间,即最好情况下是O(n),最坏情况下是O(n^2),其空间复杂度是O(1),是一种稳定的排序算法。

 

希尔排序

基本思想:把待排序的数据元素分为若干个小组,对同一组内的数据元素用直接插入排序;小组的个数逐次递减;当完成了所有数据元素都在一个组内的排序后排序过程结束。希尔排序又称为缩小增量排序。

演示效果图:



  

实现代码:

 

public class ShellSort {
	/**
	 * 希尔排序实现方法
	 * @param a 需要排序的数组
	 * @param d 增量数组
	 * @param numOfD 增量数组的长度
	 * @return
	 */
	public int[] shellSort(int[]a,int[]d,int numOfD){
		int i,j,k,m,span;
		int temp;
		int n = a.length;
		
		for(m = 0;m < numOfD;m++){
			span = d[m];
			for(k = 0;k < span;k++){
				for(i = k;i < n - span;i = i + span){
					temp = a[i + span];
					j = i;
					while(j > -1&&temp <= a[j]){
						a[j + span] = a[j];
						j = j - span;
					}
					a[j + span] = temp;
				}
			}
		}
		return a;
	}
}

 

希尔排序的时间复杂度是O(n(lbn)^2),空间复杂度是O(1),由于希尔排序是按增量分组进行的排序,两个相同的数据元素有可能分在不同的组内,因此希尔排序算法是一种不稳定的排序算法。

 

直接选择排序

基本思想:从待排序的数据元素集合中选取最小的数据元素并将它与原始数据元素集合中的第一个数据元素交换位置;然后从不包括第一个位置上的数据元素集合中选取最小的数据元素并将它与原始数据元素集合中的第二个数据元素交换位置;如此重复,知道数据元素集合只剩下一个数据元素为止。

动态演示图:



 

实现代码:

public class SelectSort {
	/**
	 * 不稳定的直接插入排序算法
	 * @param a 待排序数组
	 * @return	已排序数组
	 */
	public int[] selectSort(int[] a){
		int i,j,min;
		int temp;
		int n = a.length;
		
		for(i = 0;i < n - 1;i++){
			min = i;
			//找出无序区中最小元素
			for(j = i + 1;j < n;j++){
				if(a[j] < a[min]){
					min = j;
				}
			}
			//将无序区的最小元素与无序区第一个元素交换位置
			if(min != i){//当最小元素为i时不交换位置
				temp = a[i];
				a[i] = a[min];
				a[min] = temp;
			}
			
		}
		return a;
	}
	
	public int[] selectSort2(int[] a){
		int i,j,min;
		int temp;
		int n = a.length;
		
		for(i = 0;i < n - 1;i++){
			min = i;
			//找出无序区中最小元素
			for(j = i + 1;j < n;j++){
				if(a[j] < a[min]){
					min = j;
				}
			}
			//将无序区的最小元素与无序区第一个元素交换位置
			if(min != i){//当最小元素为i时不交换位置
				temp = a[min];
				for(j = min;j > i;j--){
					a[j] = a[j-1];
				}
				a[i] = temp;
			}
			
		}
		return a;
	}
}

 

直接选择排序算法的时间复杂度是O(n^2),其空间复杂度O(1),直接选择排序算法是不稳定的排序算法,这是由于每次从无序区下半选出最小元素后,与无序区第一个元素交换而引起的,因为交换可能引起相同的数据元素位置发生变化。如果在选出最小元素后,将它前面的无序记录依次后移,然后再将最小记录放在有序区的后面,这样就能保证排序算法的稳定性

 

 

堆排序

在直接选择排序中,放在数组的n个数据元素排成一个线性序列(即线性结构),要从有n个数据元素的数组中选择出一个最小的数据元素需要比较n-1次,如果能把待排序的数据元素集合构造成一个完全二叉树结构,则每次选择出一个最大(或最小)的数据元素只需比较完全二叉树的高度次,即lbn次。

基本思想:循环执行过程(1)——(3)直到数组为空为止。

<!--[if !supportLists]-->(1)<!--[endif]-->把堆顶a[0]元素(最大元素)和当前最大堆的最后一个元素交换;

<!--[if !supportLists]-->(2)<!--[endif]-->最大堆元素个数减一;

<!--[if !supportLists]-->(3)<!--[endif]-->调节新的堆使之满足最大堆的定义。

动态演示:



 

实现代码:

 * 保持最大堆的性质
	 * @param a 原始元素序列
	 * @param n 数组的长度,即待排序元素的个数
	 * @param h 以h为根结点元素下标
	 */
	public static void createHeap(int[] a,int n,int h){
		int i,j,flag;
		int temp;
		i = h;
		j = 2 * i + 1;
		temp = a[i];
		flag = 0;
		//沿左右孩子较大者重复向下筛选
		while(j < n&&flag != 1){
			//寻找左右孩子结点中的较大者,j为其下标
			if(j < n - 1&&a[j] < a[j+1]){
				j++;
			}
			if(temp > a[j]){
				flag = 1;
			}else{
				a[i] = a[j];
				i = j;
				j = 2 * i + 1;
			}
		}
		a[i] = temp;
	}
	
	public static void initCreateHeap(int[] a){
		int n = a.length;
		for(int i = (n - 1) / 2;i >= 0;i--){
			createHeap(a, n, i);
		}
	}
	
	public static int[] heapSort(int[] a){
		int temp;
		int n = a.length;
		initCreateHeap(a);
		for(int i = n - 1;i > 0;i--){
			temp = a[0];
			a[0] = a[i];
			a[i] = temp;
			createHeap(a, i, 0);
		}
		return a;
	}

 

推排序算法的时间复杂度是O(nlbn),其空间复杂度是O(1),而且该算法是一种不稳定的排序算法。

 

冒泡排序

基本思想:设数组a中存放了n个数据元素,循环进行n-1次如下的排序过程:第一次时,依次比较相邻两个数据元素a[i]和a[i+1](i=0,1,2……,n-2),若为逆序,即a[i]>a[i+1],则交换两个数据元素,否则不交换,这样数值最大的数据元素将被放置在a[n-1]中。第2次时,循环次数减一,即数据元素个数为n-1,操作方法和第一次的类似,这样整个n个数据元素集合中数值较大的数据元素将放置在a[n-2]中。当第n-1次结束时,整个n个数据元素中较小的数据元素中次小的数据元素将被放置在a[1]中,a[0]中放置了最小的数据元素。

动态演示:



 

实现代码:

public static int[] bubbleSort(int[] a){
		int i,j,flag = 1;
		int temp;
		int n = a.length;
		for(i = 1;i < n&&flag == 1;i++){
			flag = 0;
			for(j = 0;j < n - i;j++){
				if(a[j] > a[j+1]){
					flag = 1;
					temp = a[j];
					a[j] = a[j+1];
					a[j+1] = temp;
				}
			}
		}
		return a;
	}

 

冒泡排序算法最好情况的时间复杂度是O(n),最坏情况下的时间复杂度是O(n^2),而冒泡排序算法的空间复杂度是O(1),冒泡排序算法是一种稳定的排序算法。

 

快速排序

基本思想:设数组a中存放了n个数据元素,low为数组的低端下标,high为数组的高端下标,从数组a中任取一个元素(通常取a[low])作为标准元素,以该标准元素为基准来调整数组a中其他各个元素的位置,使排在标准元素前面的元素均小于标准元素,排在标准元素后面均大于或等于标准元素。这样一次排序过程结束后,一方面将标准元素为中心分为了两个子数组,位于标准元素左边子数组中的元素均小于标准元素,位于标准元素右边子数组中的元素均大于或等于标准元素。对于这两个子数组中的元素分别在进行方法类同的递归快速排序。算法的递归出口条件是low>=high。

动态演示:



 

实现代码:

public static int[] quickSort(int[] a,int low,int high){
		int i,j;
		int temp;
		i = low;
		j = high;
		temp = a[low];
		
		while(i < j){
			//在数组右端开始扫描
			while(i < j&&temp <= a[j]){
				j--;
			}
			if(i < j){
				a[i] = a[j];
				i++;
			}
			//在数组左端开始扫描
			while(i < j&&a[i] < temp){
				i++;
			}
			if(i < j){
				a[j] = a[i];
				j--;
			}
		}
		a[i] = temp;
		if(low < i){
			quickSort(a, low, i-1);
		}
		if(i < high){
			quickSort(a, j+1, high);
		}
		return a;
	}

 

快速排序的时间复杂度是O(nlbn),空间复杂度为O(lbn),最坏情况下的空间复杂度是O(n),而且该排序算法是一种不稳定的排序算法。

 

归并排序

基本思想:设数组a中存放了n个数据元素,初始时我们把它们看成是n个长度为1的有序子数组,然后从第一个子数组开始,把相邻的子树组两两合并,得到n/2个(若n/2为小数则向上取整)长度为2的新的有序子数组(当n为奇数时最后一个新的有序子数组的长度为1);对这些新的有序子数组在两两归并,如此重复,直到得到一个长度为n的有序数组为止。

动态演示:



 

实现代码:

/**
	 * 归并排序的实现
	 * @param a 待排序数组
	 * @param swap 在排序过程中用于存放数组
	 * @param k 第一个有序子数组的长度
	 */
	public static void merge(int[] a,int[] swap,int k){
		int n = a.length;
		int m = 0,u1,l2,i,j,u2;
		
		int l1 = 0;//是第一个有界数组的下界为0
		while(l1 + k <= n - 1){
			l2 = l1 + k;//计算第二个有序子数组下界
			u1 = l2 - 1;//计算第一个有序子数组上界
			u2 = (l2 + k - 1 <= n - 1)?l2 + k -1:n - 1;//计算第二个有序子数组上界
			
			for(i = l1,j = l2;i <= u1&&j <= u2;m++){
				if(a[i] <= a[j]){
					swap[m] = a[i];
					i++;
				}else{
					swap[m] = a[j];
					j++;
				}
			}
			
			//子数组2已归并完毕,将子数组1中剩余的元素存放到数组swap中
			while(i <= u1){
				swap[m] = a[i];
				m++;
				i++;
			}
			//子数组1已归并完毕,将子数组2中剩余的元素存放到数组swap中
			while(j <= u2){
				swap[m] = a[j];
				m++;
				j++;
			}
			l1 = u2 + 1;
		}
		//将原数组中只够一组的数据元素存放在数组swap中
		for(i = l1;i < n;i++,m++){
			swap[m] = a[i];
		}
		
	}
	
	public static int[] mergeSort(int[] a){
		int i;
		int n = a.length;
		int k = 1;//归并长度从1开始
		int[] swap = new int[n];
		

		while(k < n){
			merge(a,swap,k);
			for(i = 0;i < n;i++){
				a[i] = swap[i];
			}
			k = 2*k;//归并长度翻倍
		}
		return a;
	}

 

对n个元素进行一次二路归并排序时,归并的次数约为lbn,任何一次的二路归并排序元素的比较次数都约为n-1,所以,二路归并排序算法的时间复杂度是O(nlbn);二路归并排序时使用了n个临时内存空间存放数据元素,所以,二路归并排序算法的空间复杂度是O(n)。二路归并排序算法是一种稳定的排序算法。

 

基数排序

基数排序也成为桶排序,是一种当待排序数据元素为整数类型时非常高效的排序方法。

基本思想:设待排序的数据元素是m位d进制整数(不足m位的在高位补0),设置n个桶,令其编号分别为0,1,2,…,d-1。首先按最低位(即个位)的数值一次把各数据元素放到相应的桶中,然后按照桶号从小到大和进入桶中数据元素的先后次序手机分配在各桶中的数据元素,这样就形成了数据元素集合的一个新的排列,我们称这样的一次排序过程为一次基数排序;对一次基数排序得到的数据元素序列再按次低位(即十位)的数值一次把各数据元素放到相应的桶中,然后按照桶号从小到大和进入桶中数据元素的先后次序收集分配在各桶中的数据元素;这样的过程重复进行,当完成了第m次基数排序后,就得到了排好序的数据元素序列。

 

实现代码:

/**
	 * 基数排序的实现方法
	 * @param a 尚未排序的方法
	 * @param m	数据元素的最大位数
	 * @param d	进制的基数
	 * @return 已排好序的数组
	 * @throws Exception 抛出异常
	 */
	public static int[] radixSort(int[] a,int m,int d) throws Exception{
		int n = a.length;
		int i,j,k,l,power = 1;
		LinQueue[] queue_sort = new LinQueue[d];
		
		//创建链式队列数组对象
		for(i = 0;i < d;i++){
			LinQueue queue = new LinQueue();
			queue_sort[i] = queue;
		}
		
		//进行m次排序
		for(i = 0;i < m;i++){
			if(i == 0){
				power = 1;
			}else{
				power = power * d;
			}
			//一次将n个数据元素按第k为的大小放到相应的队列中
			for(j = 0;j < n;j++){
				k = a[j]/power - (a[j]/(power * d)) * d;//计算k值
				queue_sort[k].insert(new Integer(a[j]));//将a[j]存储到队列k中
			}
			//顺序回收各队列中的数据元素到数组a中
			l = 0;
			for(j = 0;j < d;j++){
				while(queue_sort[j].notEmpty()){
					a[l] = ((Integer)queue_sort[j].delete()).intValue();
					l++;
				}
			}
		}
		return a;
	}
	
	//生成100个从零到1000的随机数
	public static int[] random(int[] a){
		for(int i = 0;i < 100;i++){
			a[i] = (int)(Math.random()*1000);
		}
		for(int i = 0;i < 100;i++){
			System.out.print(" a["+i+"]:"+a[i]);
		}
		return a;
	}

 

基数排序算法的时间复杂度为O(m*n),该排序算法的空间复杂度是O(n),并且基数排序算法是一种稳定的排序算法。

 

各排序算法的性能比较

排序方法

最好时间复杂度

平均时间复杂度

最坏时间复杂度

空间复杂度

稳定性

插入排序

O(n)

O(n^2)

O(n^2)

O(1)

稳定

希尔排序

 

O(n^1.3)

 

O(1)

不稳定

选择排序

O(n^2)

O(n^2)

O(n^2)

O(1)

稳定

堆排序

O(nlbn)

O(nlbn)

O(nlbn)

O(1)

不稳定

冒泡排序

O(n)

O(n^2)

O(n^2)

O(1)

稳定

快速排序

O(nlbn)

O(nlbn)

O(n^2)

O(lbn)

不稳定

归并排序

O(nlbn)

O(nlbn)

O(nlbn)

O(n)

稳定

基数排序

O(m*n)

O(m*n)

O(m*n)

O(n)

稳定

 

 

 

  • 大小: 741 KB
  • 大小: 13.1 KB
  • 大小: 274.4 KB
  • 大小: 96.5 KB
  • 大小: 90.8 KB
  • 大小: 13.1 KB
0
1
分享到:
评论

相关推荐

    常见排序算法的实现与性能比较JAVA版

    常见排序算法的实现与性能比较JAVA 问题描述:实现合并排序,插入排序,希尔排序,快速排序,冒泡排序,桶排序算法 实验要求: A. 在随机产生的空间大小分别为 N = 10, 1000,10000,100000 的排序样本(取值为[0...

    常见排序算法的实现与性能比较

    实现合并排序,插入排序,希尔排序,快速排序,冒泡排序,桶排序算法。 A 在随机产生的空间大小分别为 N = 10, 1000,10000,100000  的排序样本(取值为[0,1])上测试以上算法。 B.结果输出:  1) N=10时,...

    [总结]各大内部排序算法性能比较+程序实现

    总结整理的,5大内部排序算法性能的比较+程序实现。 已编译通过,测试正确。 --------- 搜遍网上,亦不可得的资料。 July、2010/11/01。:)。

    不同排序算法的实现和性能比较

    分别实现插入排序、冒泡排序、堆排序、合并排序、快速排序,以不同规模(100,1000,2000,5000,10000,100000个数据)的随机数作为测试数据集,分别设置比较操作计数器,验证各个算法随着测试数据集增加比较次数的变化趋势。...

    多种排序算法性能比较

    六种排序算法的c语言实现,可选择排序数据量大小,文件在D盘生成

    常见排序算法的实现与性能比较C++

    实现合并排序,插入排序,希尔排序,快速排序,冒泡排序,桶排序算法 随机产生空间大小为: N = 10, 1000,10000,100000 的排序样本,取值为[0,1]区间 输出: 1) N=10时,排序结果。 2) N=1000,10000,...

    算法实现及性能比较与红黑树

    1.(必做题) 常见排序算法的实现与性能比较 问题描述:实现合并排序,插入排序,希尔排序,快速排序,冒泡排序,桶排序算法 实验要求:  A. 在随机产生的空间大小分别为 N = 10, 1000,10000,100000 的...

    各种排序算法性能的比较

    用于比较几种排序算法的性能。不过程序中有几个小错误,要加几个系统头文件和两个比较大小的函数

    算法设计与分析-1排序算法性能分析-冒泡/选择/插入/合并/快速排序-pre ppt

    选择排序 冒泡排序 插入排序 合并排序 快速排序算法原理及代码实现 不同排序算法时间效率的经验分析方法 验证理论分析与经验分析的一致性 当面临巨大数据量的排序的时候,还是优先选择合并排序算法和快速排序算法。...

    各种排序算法时间性能的比较

    上述各种排序方法都是基于比较的内排序,其时间主要消耗在排序过程中进行的记录的比较和移动,因此,统计在相同数据状态下不同排序算法的比较次数和移动次数,即可实现比较各种排序算法的目的。 [思考题]如果测算每...

    中科大算法导论课程实验 常见排序算法的实现与性能比较 报告

    中科大软件学院 算法导论课程实验 正式题目一 常见排序算法的实现与性能比较 实验报告 包括合并排序、插入排序、希尔排序、快速排序、冒泡排序、桶排序

    不同排序算法实现及性能分析(研究生项目作业)

    算法老师要求做的,希望对学弟学妹有所帮助,里面包含了性能分析对比图

    七种经典排序算法[代码+详细注释+性能测试]

    七种经典排序算法。算法实现代码+详细的注释;性能测试代码+测试结果;算法总结七种经典排序算法。七种经典排序算法。

    实验7: 内部排序算法比较.doc

    实验7: 内部排序算法比较.doc 实验7: 内部排序算法比较.doc 实验7: 内部排序算法比较.doc

    实验 9 排序算法实验比较

    基于教材内容,任选两种排序算法,实现并比较性能。 基本要求 (1)至少要有一种排序算法的性能优于 O(n2 ) (2)对实现的排序算法进行实验比较,实验比较数据参见教材 7.8 章节 (3)排序算法要基于教材,测试输入...

    各种内排序的实现与比较

    各种内排序性能和算法的比较,包含冒泡排序,快速排序,直接插入排序,堆排序,两路合并排序等排序算法

    各种排序算法的实现及性能比较

    各种排序算法源代码,各种排序算法性能比较:直接插入排序,希尔排序,冒泡排序,快速排序,简单选择排序,堆排序,二路归并排序,STL排序算法

Global site tag (gtag.js) - Google Analytics