十大排序算法之冒泡排序

排序算法是一类比较重要和经典的一类算法,主要十类排序算法。主要有两大类,一类是非线性时间比较类排序(通过比较来决定元素间相对次序,其时间复杂度不能突破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,以下是测试结果

 从测试结果可以看出,优化后的冒泡排序比优化后的冒泡排序的时间效率要高,以上整个过程就是冒泡排序整个实现过程。

 

猜你喜欢

转载自blog.csdn.net/zhangjun62/article/details/82966500