排序算法是一类比较重要和经典的一类算法,主要十类排序算法。主要有两大类,一类是非线性时间比较类排序(通过比较来决定元素间相对次序,其时间复杂度不能突破O(n logn)),另一类是线性时间非比较类排序(不通过比较来决定元素间的相对次序,可以突破基于比较排序的时间下届,以线性时间运行)。第一类有交换排序(冒泡排序、快速排序),插入排序(简单插入排序、希尔排序),选择排序(简单选择排序、堆排序),归并排序,第二类有计数排序、桶排序、基数排序。
首先是冒泡排序,数组中每一个元素与右边元素进行比较,若大于右边元素,则交换位置,继续向右比较,否则停止比较,进行下一趟的比较,整个过程第一次循环会将最大元素放在最大位置,第二次次大元素放在倒数第二个位置,依次类推,最后将整个数组进行排序,这整个过程就像泡泡不断往前冒,越来越大,所以叫冒泡排序。以下是实现和测试过程,使用Java语言编写
首先编写测试工具类SortTestUtil
import java.lang.reflect.Method;
import java.util.Random;
//排序测试工具类
//无需实例化,私有构造函数
//所有方法采用静态修饰符修饰,类名可直接调用
public class SortTestUtil {
private SortTestUtil() {
}
//对调数组中相应位置元素
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//产生一个拥有n个元素整型素组,元素大小为输入的左右边界形成的闭区间中
public static int[] generateRandomArray(int n, int rangeL, int rangR) {
//创建相应大小数组
int[] arr = new int[n];
//创建随机类,给一个固定种子
Random random = new Random(0xff);
//为数组中添加相应数量和相应大小的随机数
for (int i = 0; i < n; i++)
arr[i] = random.nextInt(Integer.MAX_VALUE) % (rangR - rangeL + 1) + rangeL;
return arr;
}
//产生一个近乎有序的数组,根据输入的交换次数调节数组的有序性
public static int[] generateNearlyOrderedArray(int n, int swapTime) {
//产生相应大小的数组
int[] arr = new int[n];
//为数赋上相应有序的数据,从小到大
for (int i = 0; i < n; i++)
arr[i] = i;
//创建随机类,给定固定种子
Random random = new Random(0xff);
//根据交换次数,随机交换数组相应次数的元素位置,形成一个近乎有序的数组
for (int i = 0; i < swapTime; i++) {
int posx = random.nextInt(Integer.MAX_VALUE) % n;
int posy = random.nextInt(Integer.MAX_VALUE) % n;
swap(arr, posx, posy);
}
return arr;
}
//复制一个整型数组,并返回相应的数组
public static int[] copyIntArray(int[] a, int n) {
int[] arr = new int[n];
for (int i = 0; i < n; i++)
arr[i] = a[i];
return arr;
}
//打印一个数组
public static void printArray(int[] arr, int n) {
for (int e : arr) {
System.out.println(e + " ");
}
System.out.println();
}
//判断整个数组是否有序
public static boolean isSorted(int[] arr, int n) {
//遍历整个数组,不断比较左右相邻元素的小,如果有一个左大于右,则表明排序失败
for (int i = 0; i < n - 1; i++)
if (arr[i] > arr[i + 1])
return false;
return true;
}
//测试排序函数,需要输入排序算法名称, 排序算法完整类名, 排序方法名, 数组, 元素个数
//利用反射机制来执行相应的排序方法
public static void testSort(String sortName, String className, String methodName, int arr[], int n) {
long startTime;
long endTime;
try {
Class c = Class.forName(className);
Object obj = c.newInstance();
Method metod = c.getMethod(methodName, int[].class, int.class);
startTime = System.nanoTime();
metod.invoke(obj, arr, n);
endTime = System.nanoTime();
System.out.println(sortName + ": " + (endTime - startTime) / 1000000000.0 + " s");
} catch (Exception e) {
e.printStackTrace();
}
if (!isSorted(arr, n))
throw new IllegalStateException(sortName + " failed!");
}
}
然后编写冒泡排序类BubbleSort
import cn.zjut.util.SortTestUtil;
public class BubbleSort {
public void bubbleSort(int[] arr, int n) {
boolean swapped = false;
do {
swapped = false;
for (int i = 1; i < n; i++)
if (arr[i - 1] > arr[i]) {
SortTestUtil.swap(arr, i - 1, i);
swapped = true;
}
n--;
} while (swapped);
}
}
以上程序每次循环可能会遍历到以前已经遍历交换过的元素,所以可以记录上一趟循环最后交换元素的位置,下一次循序就以此为循环又边界,这样处理会减少比较时间,特别是对于近乎有序的数组更有效
以下是冒泡排序优化类BubbleSortImprove
import cn.zjut.util.SortTestUtil;
public class BubbleSortImprove {
public void bubbleSort(int[] arr, int n) {
int newN;
do {
newN = 0;
for (int i = 1; i < n; i++)
if (arr[i - 1] > arr[i]) {
SortTestUtil.swap(arr, i - 1, i);
newN = i;
}
n = newN;
} while (newN > 0);
}
}
以下是测试类Main
import cn.zjut.util.SortTestUtil;
public class Main {
public static void main(String[] args){
int n = 10000;
System.out.println("Test for Random Array, size = " + n + ", random range [0, " + n + ']');
int[] arr1 = SortTestUtil.generateRandomArray(n, 0, n);
int[] arr2 = SortTestUtil.copyIntArray(arr1, n);
SortTestUtil.testSort("BubbleSort", "cn.zjut.sort.BubbleSort", "bubbleSort", arr1, n);
SortTestUtil.testSort("BubbleSortImprove", "cn.zjut.sort.BubbleSortImprove", "bubbleSort", arr2, n);
System.out.println("--------------------------------");
int swapTime = 10;
System.out.println("Test for Random Nearly Ordered Array, size = " + n + ", swap time = " + swapTime);
arr1 = SortTestUtil.generateNearlyOrderedArray(n, swapTime);
arr2 = SortTestUtil.copyIntArray(arr1, n);
SortTestUtil.testSort("BubbleSort", "cn.zjut.sort.BubbleSort", "bubbleSort", arr1, n);
SortTestUtil.testSort("BubbleSortImprove", "cn.zjut.sort.BubbleSortImprove", "bubbleSort", arr2, n);
}
}
冒泡排序平均时间复杂度是O(n ^ 2)级别,所以测试数据不要过多,本次测试是10000,以下是测试结果
从测试结果可以看出,优化后的冒泡排序比优化后的冒泡排序的时间效率要高,以上整个过程就是冒泡排序整个实现过程。