假设排序都为升序排序。
冒泡排序
思想:遍历二维数组,如果左边的数字比右边的大,则交换两者
public class BubbleSort {
public static void bubbleSort(int[] arr, int startIndex, int endIndex) {
for (int i = 0; i < endIndex; i++) {
for (int j = 0; j < endIndex; j++) {
if (arr[j] > arr[j + 1]) {
int tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
}
}
快速排序
思想:采用分治法,进行递归排序。选取一个数字作为基准值,比它小的元素放在左边,比它大的元素放在右边,具体表现为:
取第一个元素,作为基准元素;
使用一个mark指针代表小于基准元素的区域边界,初始边界为0;遍历区间,如果遇到比基准元素小的,需要做两件事:
1.mark指针加1,因为比基准元素小的区域增加了1
2.交换当前遍历到的元素与mark指针所在位置的元素(因为比基准元素小,所以要把它放到小的那个区域里)
最后,交换基准元素与mark指针所在的元素。
public class QuickSort {
public static void quickSort(int[] arr, int startIndex, int endIndex) {
if (startIndex >= endIndex) {
return;
}
// 得到分区下标
int pivotIndex = partition(arr, startIndex, endIndex);
// 分成左右两部分进行递归排序
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
private static int partition(int[] arr, int startIndex, int endIndex) {
// 取第一个元素的位置作为基准元素
int pivot = arr[startIndex];
// mark指针代表小于基准元素的区域边界
int mark = startIndex;
for (int i = startIndex + 1; i <= endIndex; i++) {
// 如果遍历到的元素小于基准元素,则需要:
// 1.mark指针右移一位,因为小于pivot的区域边界增大了1
// 2.让最新遍历到的元素与mark指针所在位置的元素交换位置,因为最新遍历到的元素归属于小于pivot的区域
if (arr[i] < pivot) {
mark++;
int tmp = arr[mark];
arr[mark] = arr[i];
arr[i] = tmp;
}
}
// 最后,交换基准元素与mark的元素
arr[startIndex] = arr[mark];
arr[mark] = pivot;
return mark;
}
}
堆排序
思想:
1.建立大根堆(遍历每个父节点,通过调整让其大于它的任何一个子节点),如果不满足则进行下沉操作(父节点与最大的那个子节点交换,直到满足条件)
2.从最后一个元素开始,每次交换第一个元素与最后一个元素的值,然后依次进行堆下沉调整。第一次调整得到最大的值,第二次调整得到第二大,以此类推…
public static void heapsort2(int[] arr, int n) {
// 1.把无序数组构建成最大堆:每个父节点依次做调整成父节点大于任何一个子节点
buildMaxHeap(arr, n);
// System.out.println(Arrays.toString(arr));
// 2,循环删除堆顶元素,移到集合尾部,调整产生新的堆顶
for (int i = n - 1; i >= 0; i--) {
// 将第一个元素和最后一个元素交换
swap(arr, 0, i);
// 每次砍断一个(把最大的那个数已经拿出来了,就不用再计算这个数了),下沉调整
downHeapify(arr, i, 0);
}
}
// 构造一个大根堆
private static void buildMaxHeap(int[] arr, int n) {
int lastNode = n -1;
int parent = (lastNode - 1) / 2;
// 对每个父节点进行调整
for (int i = parent; i >= 0; i--) {
downHeapify(arr, n, i);
}
}
// 下沉调整:使父节点大于任何一个子节点
private static void downHeapify(int[] arr, int length, int parent) {
if (parent >= length) {
return;
}
int c1 = parent * 2 + 1;
int c2 = parent * 2 + 2;
int maxIndex = parent;
if (c1 < length && arr[c1] > arr[maxIndex]) {
maxIndex = c1;
}
if (c2 < length && arr[c2] > arr[maxIndex]) {
maxIndex = c2;
}
if (maxIndex != parent) {
swap(arr, maxIndex, parent);
downHeapify(arr, length, maxIndex);
}
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
测试
public class SortTest {
public static void main(String[] args) {
Random random = new Random();
int[] lengthArr = new int[]{100, 1000, 10000, 100000, 1000000};
for (int value : lengthArr) {
int[] arr = new int[value];
for (int j = 0; j < value; j++) {
arr[j] = random.nextInt();
}
testQuickSort(arr);
testHeapSort(arr);
testBubbleSort(arr);
System.out.println();
}
}
private static void testQuickSort(int[] arr) {
long start = System.currentTimeMillis();
QuickSort.quickSort(arr, 0, arr.length - 1);
// System.out.println(Arrays.toString(arr));
long end = System.currentTimeMillis();
System.out.println("数据量为"+ arr.length + "时,快排耗时:" + (end - start) + "ms");
}
private static void testHeapSort(int[] arr) {
long start = System.currentTimeMillis();
HeapSort.heapsort2(arr, arr.length);
// System.out.println(Arrays.toString(arr));
long end = System.currentTimeMillis();
System.out.println("数据量为"+ arr.length + "时,堆排序耗时:" + (end - start) + "ms");
}
private static void testBubbleSort(int[] arr) {
long start = System.currentTimeMillis();
BubbleSort.bubbleSort(arr, 0, arr.length - 1);
// System.out.println(Arrays.toString(arr));
long end = System.currentTimeMillis();
System.out.println("数据量为"+ arr.length + "时,冒泡排序耗时:" + (end - start) + "ms");
}
}
在数据量为100、1000、10000、100000、1000000时,三种排序方法的运行时间