对一些常见的排序算法进行演算测试,对比效率。(JAVA 实现)

大三下了,正在找实习,把一些常用的排序算法重新复习一遍,同时简单的比较了一下各个算法的效率,总的来说,快排效率最高,冒泡效率最低。—–具体的排序算法包括:直接插入排序、折半插入排序、Shell排序(插入排序) 冒泡、快速排序(交换排序)

欢迎大家一起讨论,如有错误务必指出~~~

先验证排序算法的正确性:

《对一些常见的排序算法进行演算测试,对比效率。(JAVA 实现)》

接着比较效率(注释掉了打印数组的功能):

《对一些常见的排序算法进行演算测试,对比效率。(JAVA 实现)》

得出结论:当待排数的数量较大时(示例中采用102400个随机整数),效率对比为:   

快速排序   >  折半插入排序  >  Shell排序  >  直接插入排序  >  冒泡排序

package com.ky415.loop;

import java.util.Arrays;
import java.util.Random;
import java.util.Scanner;

/**
 * 测试数组的常用方法:最大值、求和、平均值、数组的翻转
 * 使用多种排序方法对输入的数组进行排序(升序)
 * 依次包括:直接插入排序、折半插入排序、Shell排序(插入排序)
 * 冒泡、快速排序(交换排序)
 * @author Mypc
 */
public class TestArray {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.println("请输入需要生成的随机数的个数:");
		int count = input.nextInt();
		Random random = new Random();
		int[] number = new int[count];
		for (int i = 0; i < number.length; i++) {
			number[i] = random.nextInt();
		}
//		System.out.println("生成的随机数如下:");
//		printArray(number);
		
		System.out.println("对" + count + "个数进行排序,各个排序所需时间的对比如下" );
		
		directInsertSort(number);
		
		halfInsertSort(number);

		shellSort(number);
		
		bubbleSort(number);
		
		quickSort(number);
		
	}
	
	
	/**
	 * 打印整型数组的值
	 * @param number
	 */
	public static void printArray(int [] number) {
		int time = 0;
		for(int i : number) {
			System.out.print(i + "\t");	
			time ++;
			if(time % 5 == 0) {
				time = 0;
				System.out.println();
			}
		}
		System.out.println();
	}
	
	
	
	/**
	 * 测试数组的常用方法:最大值、求和、平均值、数组的翻转
	 * @param number
	 */
	public static void arrayCommonMethod(int[] number) {
		//求数组最大值
		int[] number1 = number;
		System.out.print("数组的最大值为:");
		int max = number1[0];
		for (int i = 1; i < number1.length; i++) {
			max = number1[i] > max ? number1[i] : max;	
		}
		System.out.println(max);
		
		//求数值所有的元素之和
		System.out.print("数组的和为:");
		int sum = number1[0];
		for (int i = 1; i < number1.length; i++) {
			sum += number1[i];
		}
		System.out.println(sum);
		
		//求数组元素的平均值
		System.out.print("数组的平均值为:");
		int avge = 0;
		for (int i : number1) {
			avge += i;
		}
		System.out.println(avge / number1.length);
		
		//对数组进行翻转
		System.out.print("数组翻转后为:");
		int[] number2 = new int[number.length];
		for (int i = 0, j = number1.length - 1; j >= 0; i++, j--) {
			number2[i] = number1[j];
		}
		for(int i : number2) {
			System.out.print(i + "\t");
		}
	}
	
	
	/**
	 * 直接插入排序
	 * --基本思想是每一趟将一个待排序的记录,按其关键字的大小插入到已经排好序的一组记录的适当位置上,直到所有待排序记录全部插入为止。
	 * @param number
	 */
	public static void directInsertSort(int[] number) {
		//用以记录排序开始的时间
		long start = System.currentTimeMillis();
		int[] number1 = new int[number.length];
		for (int i = 0, j = number.length - 1; j >= 0; i++, j--) {
			number1[i] = number[j];
		}
		for (int i = 1; i < number1.length; i++) {
			//判断当前值是否需要移动
			if(number1[i] < number1[i - 1]) {
				//记录需要移动的关键字位置
				int moveSubscript = i;
				//暂存需要移动的关键字
				int temp = number1[moveSubscript];
				//将已经排序好的记录,从需要移动的关键字前一个开始,将每一个比关键字大的元素都向后移一位
				for (int j = i - 1; j >= 0 && number1[j] > temp; j--) {
					moveSubscript = j;
					number1[j + 1] = number1[j];
				}
				//将需要移动的关键字,放置在最终的位置上
				number1[moveSubscript] = temp;
			}
		}
		//用以记录排序完成的时间
		long end = System.currentTimeMillis();
		System.out.println("直接插入排序--"+ "完成排序需要的时间为:" + (end - start) +" :");
	}
	
	
	/**
	 * 折半插入排序 -- 折半插入算法是对直接插入排序算法的改进,排序原理同直接插入算法。
	 * 两者区别在于:在有序表中寻找待排序数据的正确位置时,使用了折半查找/二分查找。
	 * @param number
	 */
	public static void halfInsertSort(int [] number) {
		//构建一个新的数组进行排序操作
		int[] number1 = new int[number.length];
		for (int i = 0, j = number.length - 1; j >= 0; i++, j--) {
			number1[i] = number[j];
		}
		//用以记录排序开始的时间
		long start = System.currentTimeMillis();
		for (int i = 1; i < number1.length; i++) {
			//判断当前值是否需要移动
			if(number1[i] < number1[i - 1]) {
				//暂存需要移动的关键字
				int temp = number1[i];
				//查找需要移动的关键字应该插入的位置 目标位置为low
				int low = 0, high = i - 1, mid = 0;
				while(low <= high){
					mid = (low + high) / 2;
					if(number1[mid] < temp) {
						low = mid + 1;
					}
					else {
						high = mid - 1;
					}
				}				
				//将已经排序好的记录,从需要移动的关键字前一个开始,将每一个比关键字大的元素都向后移一位
				for (int j = i;j > low; j--) {
					number1[j] = number1[j - 1];
				}
				//将需要移动的关键字,放置在最终的位置上
				number1[low] = temp;
			} 
		}
		//用以记录排序完成的时间
		long end = System.currentTimeMillis();
		System.out.println("折半插入排序--"+ "完成排序需要的时间为:" + (end - start) +" :");
 	}
	
	
	/**
	 *  Shell排序--也是对直接插入排序的改进;   基本思想:先将整个待排元素序列切割成若干个子序列(由相隔某个“增量”的元素组成的)
	 * 分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。
	 * @param number
	 */
	public static void shellSort(int[] number) {
		//构建一个新的数组进行排序操作
		int[] number1 = new int[number.length];
		for (int i = 0, j = number.length - 1; j >= 0; i++, j--) {
			number1[i] = number[j];
		}
		//用以记录排序开始的时间
		long start = System.currentTimeMillis();
		//设置每次分组的大小--当 gap 为  0  时排序完成
		for(int gap = number1.length / 2; gap > 0; gap /= 2) {
			for(int j =  gap; j < number1.length; j += gap) {
				if(number1[j] < number1[j - gap]) {
					int temp = number1[j];
					int k = j;
					while(k - gap >= 0 && number1[k - gap] > temp) {
						number1[k] = number1[k - gap];
						k -= gap;
					}
					number1[k] = temp;
				}
			}
		}
		//用以记录排序完成的时间
		long end = System.currentTimeMillis();
		System.out.println("Shell排序--"+ "完成排序需要的时间为:" + (end - start) +" :");
//		printArray(number1);
	}
	
	
	/**
	 * 冒泡排序--依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。
	 * 然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。
	 * @param number
	 */
	public static void bubbleSort(int[] number) { 
		//构建一个新的数组进行排序操作
		int[] number1 = new int[number.length];
		for (int i = 0, j = number.length - 1; j >= 0; i++, j--) {
			number1[i] = number[j];
		}
		//用以记录排序开始的时间
		long start = System.currentTimeMillis();
		for (int i = 0; i < number1.length -1; i++) {
			for (int j = 0; j < number1.length - 1 - i; j++) {
				//遇到不满足排序要求的数,交换两个数的位置
				if(number1[j] > number1[j + 1]) {
					int temp = number1[j];
					number1[j] = number1[j + 1];
					number1[j + 1] = temp;
				}
			}
		}
		//用以记录排序完成的时间
		long end = System.currentTimeMillis();
		System.out.println("冒泡排序--"+ "完成排序需要的时间为:" + (end - start) +" :");
//		printArray(number1);
	}
	
	
	/**
	 * 基本思想主要分为三个步骤:1、先从数列中取出一个数作为基准数(标兵-通常取第一个数),
	 * 2、先从右到左找到第一个比基准数小数,然后停下来(大哨兵移动 j--),从左到右找到第一个比基准数大的数,然后停下来(小哨兵移动 i++)
	 * 此时大小哨兵交换值,然后继续移动,直到大小哨兵相遇(i >= j),然后以此值为界限,将数列进行左右两区
	 * 3、再对左右区间重复第二步,直到各区间只有一个数
	 * @param number
	 */
	public static void quickSort(int [] number) {
		//构建一个新的数组进行排序操作
		int[] number1 = new int[number.length];
		for (int i = 0, j = number.length - 1; j >= 0; i++, j--) {
			number1[i] = number[j];
		}
		//用以记录排序开始的时间
		long start = System.currentTimeMillis();
		//调用快速排序算法
		quick(number1, 0, number1.length - 1);
		//用以记录排序完成的时间
		long end = System.currentTimeMillis();
		System.out.println("快速排序--"+ "完成排序需要的时间为:" + (end - start) +" :");
//		printArray(number1);
	}
	
	/**
	 * 对数组的 low 到 high 的区间进行一次快速排序
	 * @param number	数组名
	 * @param low	快排区间开始的下标
	 * @param high	快排区间结束的下标
	 */
	public static void quick(int[] number, int low, int high) {
		//递归跳出条件--所有的数都已经归位
		if(low >= high) {
			return ;
		}
		//取出基准数(标兵)
		int key = number[low];
		//取左右标兵
		int left = low;
		int right = high;
		//只有当左哨兵与右哨兵相等时,才结束循环
		while(left < right) {
			//右哨兵左移--直到遇见一个比key小的值
			while(number[right] >= key && right > left) {
				right --;
			}
			//左哨兵右移--直到遇见一个比key大的值
			while (number[left] <= key && right > left) {
				left ++;
			}
			//交换左右哨兵的值
			int temp = number[right];
			number[right] = number[left];
			number[left] = temp; 
		}
		//将标兵置于最终的位置--标兵归位
		number[low] = number[left];
		number[left] = key;
		//对余下的左右两部分继续进行快排
		quick(number, low, left - 1);
		quick(number, left + 1, high);
	}
}

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注