常用排序算法的Java实现

最近在看一些代码优化相关的东西,下午看到排序这块,常用的排序方式有冒泡排序、选择排序、快速排序等,这里记录下这三种排序的Java实现。最后附有2个测试这几种排序方式的时间的代码

一、几种常用排序方式介绍

1.冒泡排序

以升序排序为例,将序列看成一排竖着的气泡,最后一个元素与倒数第二个元素进行比较,小的往前移,再将倒数第二个元素与倒数第三个元素比较,依次类推,第一轮比较后,最小的就到了位置1,同样第二轮比较后第二小的到了位置二~

#PopSortDemo.java

/**
 * 冒泡排序
 * 
 * @author admin
 *
 */
public class PopSortDemo implements Sorted {
	public void sort(int[] array) {
		for (int i = 0; i < array.length; i++) {
			for (int j = array.length - 1; j > i; j--) {
				if (array[j] < array[j - 1]) {
					SortUtil.exchange(array, j, j-1);
				}
			}
		}
	}
}

总结:

1). 需要比较的次数和数组长度有关[1+2+3+4~+(n-1)

2). 需要排序数组长度越长,效率下降明显:n为10时需要比较45次,但n为100时比较次数直线上升到4950次,相差100多倍

2.冒泡排序(改进算法)

针对冒泡算法,如果有一轮没有发生交换,说明每个相邻位置不需要交换,即每个位置已经排好了,后面就可以不用继续了

#PopSortImproveDemo.java

/**
 * 冒泡排序算法(改进):如果有一轮没有发生交换,说明位置已经排好了,后面就可以不用继续了
 * @author admin
 *
 */
public class PopSortImproveDemo implements Sorted {
	public void sort(int[] array) {
		boolean changed = true;
		for (int i = 0; i < array.length && changed; i++) {
			changed = false;
			for (int j = array.length - 1; j > i; j--) {
				if (array[j] < array[j - 1]) {
					changed = true;
					SortUtil.exchange(array, j, j - 1);
				}
			}
		}
	}
}

总结:

 1). 需要比较的次数和数组长度有关,最多比较[1+2+3+4~+(n-1)] 次,最少比较[n-1]次(初始就是有序的)

  

3.选择排序

先找出最小的数,放到第一个,找出后面数中,第二小的,放到第二个,以此类推。

/**
 * 选择排序
 * 
 * @author admin
 *
 */
public class SelectSortDemo implements Sorted {
	public void sort(int[] array) {
		int pos;
		for (int i = 0; i < array.length; i++) {
			pos=i;
			for (int j = i + 1; j < array.length; j++) {
				if (array[pos] > array[j]) {
					pos=j;
				}
			}
			if(pos!=i) SortUtil.exchange(array, pos, i);
		}
	}
}

 总结:

1). 比较次数和冒泡排序算法相同

2). 之前一直把选择排序记成冒泡排序,实际有区别的(*+﹏+*)~@

4.快速排序

选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分

/**
 * 快速排序:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分
 * @author admin
 *
 */
public class QuickSortDemo implements Sorted {

	public void sort(int[] array) {
		sort(array, 0, array.length - 1);
	}

	/**
	 * 递归调用排序:排序基准位置前后子数组
	 * 
	 * @param array
	 * @param start
	 * @param end
	 */
	public void sort(int[] array, int start, int end) {
		if (start < end) {
			int pos = findMiddle(array, start, end);
			sort(array, start, pos - 1);
			sort(array, pos + 1, end);
		}
	}

	/**
	 * 获取基准元素位置
	 */
	public int findMiddle(int[] array, int start, int end) {
		int temp = array[start];
		while (start < end) {
			// 结束位置递减,直到找到第一个比标准位置值小的
			while (start < end && array[end] >= temp) {
				end--;
			}
			// 基准位置交换到end
			array[start] = array[end];
			// 开始位置递增,直到找到第一个比标准位置值大的
			while (start < end && array[start] <= temp) {
				start++;
			}
			// 基准位置交换到start
			array[end] = array[start];
		}
		// 记录基准位置值
		array[start] = temp;
		return start;
	}
}

 总结:

1) 速度比较快,当然前提是基准位置找的好\(`▽′)/

2) 最差的情况,需要找[n-1]次基准位置,找基准位置的过程中总共循环比较[1+2+3+4~+(n-1)]次

二、排序方式时间测试

几种排序的时间复杂度

排序法

最差时间分析 平均时间复杂度 稳定度 空间复杂度
冒泡排序 O(n2) O(n2) 稳定 O(1)
快速排序 O(n2) O(n*log2n) 不稳定 O(log2n)~O(n)
选择排序 O(n2) O(n2) 稳定 O(1)
二叉树排序 O(n2) O(n*log2n) 不一顶 O(n)

插入排序

O(n2) O(n2) 稳定 O(1)
堆排序 O(n*log2n) O(n*log2n) 不稳定 O(1)
希尔排序 O O 不稳定 O(1)

这里直接测试每种排序方式消耗的时间,采用两种方式:一种是每次排序都重新设置数组每个元素的值,第二种是不同的排序方式排序同一个数组

2. 不同排序方式,不同的数组

>测试代码:

/**
 * 排序测试,每种排序方式对不同随机数组排序多次,取平均时间
 * 
 * @author admin
 *
 */
public class RandomSortTest {
	private static Logger log = LogManager.getLogger();

	public static void main(String[] args) {
		// 87, 76, 65, 51, 43, 31, 23, 17, 6, 3
		// 6, 17, 23, 31, 43, 51, 65, 76, 87, 3
		// 3, 6, 17, 23, 31, 43, 51, 65, 76, 87
		// 数组长度 & 数组元素随机生成的最大值 & 每种排序方式测试次数
		int len = 100000, numMax = 10000,sortTypeCount = 10;
		// 待排序数组
		int[] arr = new int[len];
		// 排序类
		Sorted sorter = null;
		// 循环调用多个排序方法,每次调用getSorter()会返回不同的排序方法
		while ((sorter = getSorter()) != null) {
			long time = 0;
			for (int i = 0; i < sortTypeCount; i++) {
				// 给每个数组元素设置随机值
				SortUtil.insertRandomNumber(arr, numMax);
				// 排序,并返回排序时间
				time += SortUtil.testSortTime(arr, sorter);
			}
			log.info("排序方法[{}],平均[{}/{}]耗时{}ms", sorter.getClass().getSimpleName(), time, sortTypeCount, time / sortTypeCount);
		}
	}

	/**
	 * 排序方式:1、快速排序;2、选择排序;3、冒泡排序;4、冒泡排序(改进)
	 */
	private static int sortType = 1;

	private static Sorted getSorter() {
		Sorted sorter = null;
		// 1、快速排序;2、选择排序;3、冒泡排序;4、冒泡排序(改进)
		switch (sortType++) {
		case 1:
			sorter = new QuickSortDemo();
			break;
		case 2:
			sorter = new SelectSortDemo();
			break;
		case 3:
			sorter = new PopSortDemo();
			break;
		case 4:
			sorter = new PopSortImproveDemo();
			break;
		default:
			break;
		}
		return sorter;
	}
}

>测试结果(len=10W):

2017-03-22 22:30:11.214 [main] INFO  - 排序方法[QuickSortDemo],平均[92/10]耗时9ms
cn.tinyf.demo.sort.RandomSortTest
2017-03-22 22:30:38.408 [main] INFO  - 排序方法[SelectSortDemo],平均[27182/10]耗时2718ms
cn.tinyf.demo.sort.RandomSortTest
2017-03-22 22:33:04.408 [main] INFO  - 排序方法[PopSortDemo],平均[145987/10]耗时14598ms
cn.tinyf.demo.sort.RandomSortTest
2017-03-22 22:35:26.352 [main] INFO  - 排序方法[PopSortImproveDemo],平均[141931/10]耗时14193ms
cn.tinyf.demo.sort.RandomSortTest
>测试结果(len=30):
2017-03-22 23:10:35.963 [main] INFO  - 排序方法[QuickSortDemo],耗时33843纳秒
cn.tinyf.demo.sort.SortTest
2017-03-22 23:10:35.964 [main] INFO  - 排序方法[SelectSortDemo],耗时85253纳秒
cn.tinyf.demo.sort.SortTest
2017-03-22 23:10:35.964 [main] INFO  - 排序方法[PopSortDemo],耗时224286纳秒
cn.tinyf.demo.sort.SortTest
2017-03-22 23:10:35.965 [main] INFO  - 排序方法[PopSortImproveDemo],耗时166954纳秒
cn.tinyf.demo.sort.SortTest
    3. 不同排序方式,相同的数组

>测试代码:

/**
 * 排序测试,针对相同的数组排序
 * 
 * @author admin
 *
 */
public class SortTest {

	private static Logger log = LogManager.getLogger();

	public static void main(String[] args) {
		// 数组长度,数组元素随机生成的最大值
		int len = 100000, numMax = 10000;
		// 源数组
		int[] src = new int[len];
		// 待排序数组
		int[] arr = new int[len];
		// 给每个数组元素设置随机值
		SortUtil.insertRandomNumber(src, numMax);
		// 排序类
		Sorted sorter = null;
		// 循环调用多个排序方法,每次调用getSorter()会返回不同的排序方法
		while ((sorter = getSorter()) != null) {
			long time = 0;
			// 设置待排序数组
			System.arraycopy(src, 0, arr, 0, len);
			// 排序,并返回排序时间
			time += SortUtil.testSortTime(arr, sorter);
			log.info("排序方法[{}],耗时{}ms", sorter.getClass().getSimpleName(), time);
		}
	}

	/**
	 * 排序方式:1、快速排序;2、选择排序;3、冒泡排序;4、冒泡排序(改进)
	 */
	private static int sortType = 1;

	private static Sorted getSorter() {
		Sorted sorter = null;
		// 1、快速排序;2、选择排序;3、冒泡排序;4、冒泡排序(改进)
		switch (sortType++) {
		case 1:
			sorter = new QuickSortDemo();
			break;
		case 2:
			sorter = new SelectSortDemo();
			break;
		case 3:
			sorter = new PopSortDemo();
			break;
		case 4:
			sorter = new PopSortImproveDemo();
			break;
		default:
			break;
		}
		return sorter;
	}
}
 >测试结果(len=10W): 
2017-03-22 22:28:38.228 [main] INFO  - 排序方法[QuickSortDemo],耗时13ms
cn.tinyf.demo.sort.SortTest
2017-03-22 22:28:40.854 [main] INFO  - 排序方法[SelectSortDemo],耗时2625ms
cn.tinyf.demo.sort.SortTest
2017-03-22 22:28:55.803 [main] INFO  - 排序方法[PopSortDemo],耗时14949ms
cn.tinyf.demo.sort.SortTest
2017-03-22 22:29:10.574 [main] INFO  - 排序方法[PopSortImproveDemo],耗时14770ms
cn.tinyf.demo.sort.SortTest
 3.小结
  • 数组元素比较多时快速排序效率比较高
 三、附件

#Sorted接口

public interface Sorted {
	void sort(int[] array);
}

 #SortUtil工具类

/**
 * 排序工具类
 * 
 * @author admin
 *
 */
public class SortUtil {

	/**
	 * 交换数组两个位置元素值
	 * 
	 * @param array
	 * @param pos1
	 * @param pos2
	 */
	public static void exchange(int[] array, int pos1, int pos2) {
		int temp = array[pos1];
		array[pos1] = array[pos2];
		array[pos2] = temp;
	}

	/**
	 * 随机数生成器
	 */
	private static Random random = new Random();

	/**
	 * 深层随机数
	 * 
	 * @param max
	 *            最大值
	 * @return
	 */
	public static int randomInteger(int max) {
		return random.nextInt(max);
	}

	/**
	 * 使用随机数设置数组每个位置的元素值
	 * 
	 * @param arr
	 * @param randomMax
	 */
	public static void insertRandomNumber(int[] arr, int randomMax) {
		for (int i = 0; i < arr.length; i++) {
			arr[i] = SortUtil.randomInteger(randomMax);
		}
	}

	/**
	 * 测试排序时间
	 * 
	 * @param arr
	 * @param sorter
	 * @return
	 */
	public static long testSortTime(int[] arr, Sorted sorter) {
		long start = System.currentTimeMillis();
		sorter.sort(arr);
		return System.currentTimeMillis() - start;
	}

	/**
	 * 数组反序
	 * 
	 * @param array
	 */
	public static void reverse(int[] array) {
		for (int i = array.length / 2 - 1; i >= 0; i--) {
			exchange(array, i, array.length - 1 - i);
		}
	}
}

猜你喜欢

转载自marionette.iteye.com/blog/2364843