冒泡排序
冒泡排序的基本思想是两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。
标准代码
package com.weixuan.sort.bubble;
public class BubbleSort {
public static void bubbleSort(int[] data) {
for (int i = 0; i < data.length; i++) {
for (int j = data.length - 2; j >= i; j--) {
if (data[j] > data[j + 1])
swap(data, j, j + 1);
}
System.out.println("第 " + (i+1) + " 次");
printArray(data);
System.out.println("\n");
}
}
private static void swap(int[] data, int j, int i) {
if (data == null || data.length <= 0)
return;
int temp = data[j];
data[j] = data[i];
data[i] = temp;
}
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++)
System.out.print(array[i] + "\t");
}
}
过程详解 – {9,1,5,8,3,7,4,6,2}
第一趟:i==0,j = 8,j从j = 8 反循环遍历到j = 0,逐个比较,直到找到最小值放在i=0的位置上。
2 < 6 : 9, 1, 5, 8, 3, 7, 4, 2, 6 j = 8
2 < 4 : 9, 1, 5, 8, 3, 7, 2, 4, 6 j = 7
2 < 7 : 9, 1, 5, 8, 3, 2, 7, 4, 6 j = 6
2 < 3 : 9, 1, 5, 8, 2, 3, 7, 4, 6 j = 5
2 < 8 : 9, 1, 5, 2, 8, 3, 7, 4, 6 j = 4
2 < 5 : 9, 1, 2, 5, 8, 3, 7, 4, 6 j = 3
1 < 2<不交换> : 9, 1, 2, 5, 8, 3, 7, 4, 6 j = 2
1 < 9 : 1, 9, 2, 5, 8, 3, 7, 4, 6 j = 1
第一趟的结果就是:
1, 9, 2, 5, 8, 3, 7, 4, 6
第二趟:i==1,j = 8,j从j = 8 反循环遍历到j = 1,逐个比较,直到找到次最小值放在i=1的位置上。
4 < 6<不交换> : 1, 9, 2, 5, 8, 3, 7, 4, 6 j = 8
4 < 7 : 1, 9, 2, 5, 8, 3, 4, 7, 6 j = 7
3 < 4<不交换> : 1, 9, 2, 5, 8, 3, 4, 7, 6 j = 6
3 < 8 : 1, 9, 2, 5, 3, 8, 4, 7, 6 j = 5
3 < 5 : 1, 9, 2, 3, 5, 8, 7, 4, 6 j = 4
2 < 3<不交换> : 1, 9, 2, 3, 5, 8, 7, 4, 6 j = 2
2 < 9 : 1, 2, 9, 3, 5, 8, 7, 4, 6 j = 2
1 < 2<不交换> : 1, 2, 9, 3, 5, 8, 7, 4, 6 j = 1
第二趟的结果就是:
1, 2, 9, 3, 5, 8, 7, 4, 6
后面的同理
第 1 次
1 9 2 5 8 3 7 4 6
第 2 次
1 2 9 3 5 8 4 7 6
第 3 次
1 2 3 9 4 5 8 6 7
第 4 次
1 2 3 4 9 5 6 8 7
第 5 次
1 2 3 4 5 9 6 7 8
第 6 次
1 2 3 4 5 6 9 7 8
第 7 次
1 2 3 4 5 6 7 9 8
第 8 次
1 2 3 4 5 6 7 8 9
第 9 次
1 2 3 4 5 6 7 8 9
可以优化的地方
假设有序列{2,1,3,4,5,6,7,8,9},除了2和1需要交换之外,其他的已经有序了,无需再次循环。
优化之前
输出
第 1 次
1 2 3 4 5 6 7 8 9
第 2 次
1 2 3 4 5 6 7 8 9
第 3 次
1 2 3 4 5 6 7 8 9
第 4 次
1 2 3 4 5 6 7 8 9
第 5 次
1 2 3 4 5 6 7 8 9
第 6 次
1 2 3 4 5 6 7 8 9
第 7 次
1 2 3 4 5 6 7 8 9
第 8 次
1 2 3 4 5 6 7 8 9
第 9 次
1 2 3 4 5 6 7 8 9
可以看到,从第二次开始就是已经有序了,但是这个傻瓜式的程序还循环了7次!!!
优化的代码
public static void bubbleSortSuper(int[] data) {
boolean flag = true;
for (int i = 0; i < data.length && flag; i++) {
flag = false;/*初始为false*/
for (int j = data.length - 2; j >= i; j--) {
if (data[j] > data[j + 1]) {
swap(data, j, j + 1);
flag = true; // 有数据交换,flag = true
}
}
System.out.println("第 " + (i + 1) + " 次");
printArray(data);
System.out.println("\n");
}
}
输出,只循环了两次
第 1 次
1 2 3 4 5 6 7 8 9
第 2 次
1 2 3 4 5 6 7 8 9
性能分析 序列长度为N
最好的情况:序列本身有序,那么就是 N−1 次比较,没有交换,时间复杂度是 O(N)
最差的情况:序列逆序,需要比较
基本也是这个数量级的交换,所以时间复杂度是 O(N2)
快速排序
基本思想:每一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录关键字小,然后分别对这两部分进行排序,打到整个序列有序的目的。
package com.weixuan.sort.quicksort;
public class QuickSort {
private static void swap(int[] data, int j, int i) {
if (data == null || data.length <= 0)
return;
int temp = data[j];
data[j] = data[i];
data[i] = temp;
}
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++)
System.out.print(array[i] + "\t");
}
private static int partition(int[] data, int low, int high) {
int pivot = data[low];// 轴值
while (low < high) {
while (low < high && data[high] >= pivot)
high--;
// 交换
swap(data, low, high);
while (low < high && data[low] <= pivot)
low++;
// 交换
swap(data, low, high);
}
System.out.println("------");
printArray(data);
System.out.println("------");
return low;
}
private static void quickSort(int[] data, int low, int high) {
if (low < high) {
int k = partition(data, low, high);
quickSort(data, low, k - 1);
quickSort(data, k + 1, high);
}
}
public static void quickSort(int[] data) {
quickSort(data, 0, data.length - 1);
}
}
排序过程 – {50,10,90,30,70,40,80,60,20}
第一趟:
初始pivot = 50,low = 0,high = 8,data[low] = 50,data[high] = 20
data[high] = 20 < pivot ,交换,但是low和high没有变,直接跳出循环。
20,10,90,30,70,40,80,60,50 [此时 low = 0,high = 8]
data[low] = 20 < pivot,low++,只到low = 2,data[low] = 90 >pivot,退出循环,交换
20,10,50,30,70,40,80,60,90 [此时 low = 2,high = 8]
low = 2 < high = 8 ,继续大循环
初始pivot = 50,low = 2,high = 8
data[high]=90 > pivot,high–,只到high = 5,data[high] = 40 < pivot,跳出循环,交换
20,10,40,30,70,50,80,60,90 [此时 low = 2,high = 5]
data[low] = 40 < pivot,low++,只到low = 4,data[low] = 70 > pivot,跳出循环,交换
20,10,40,30,50,70,80,60,90 [此时 low = 4,high = 5]
low = 4 < high = 5 ,继续大循环
low = high = 4,退出循环,
返回low = 4
第一趟的结果就是
[20,10,40,30,50,70,80,60,90]
然后就是递归调用
对[20,10,40,30] 和 [70,80,60,90]分别执行第一趟的操作。
性能分析
快速排序的事件性能取决于快速排序递归的深度,上述的递归过程第一个关键字是50,正好是待排序序列的中间值,因此整个树是平衡的。此时性能最好。
在最优情况下,长度为n的序列递归的深度是 O(log2N)+1 , O(log2N) 向下取整。即仅需递归 O(log2N) 次,需要时间为 T(n) 的话,第一次partition对整个数组扫描一遍,做n次比较,然后一分为二,各自需要 T(n/2) 的时间。
也就是说,在最优情况下,时间复杂度为 O(nlogn) ,空间复杂度是 O(logN)
最差情况下,序列为有序或者逆序,此时需要n-1次递归调用,第i次划分需要n-i次比较才能找到第i个记录,因此比较的次数是
时间复杂度是 O(N2) ,空间复杂度是 O(N)
由于快速排序的关键字比较是跳跃进行的,所以是不稳定的排序方法。
快速排序优化
快排最好情况是第一个轴值就是整个序列的中间值,也就是说跨拍的性能瓶颈在与pivot在序列中的位置。
那么可以在low与hight之间随机选择一个数,随机选取轴值法。这样有些碰运气的感觉。
更优化的是三数取中法,即取三个关键字先进行排序,将中间数作为轴值,一般是取左端,右端和中间三个数。
- 选择合适的轴值
private static int partition(int[] data, int low, int high) {
//int pivot = data[low];// 轴值
int pivot = medianOfThree(data,low,high);
while (low < high) {
while (low < high && data[high] >= pivot)
high--;
// 交换
swap(data, low, high);
while (low < high && data[low] <= pivot)
low++;
// 交换
swap(data, low, high);
}
System.out.println("------");
printArray(data);
System.out.println("------");
return low;
}
private static void quickSort(int[] data, int low, int high) {
if (low < high) {
int k = partition(data, low, high);
quickSort(data, low, k - 1);
quickSort(data, k + 1, high);
}
}
public static void quickSort(int[] data) {
quickSort(data, 0, data.length - 1);
}
private static int medianOfThree(int[] data, int low, int high) {
int pivot;
int media = low + (high - low) / 2;
// 保证左端较小
if (data[low] > data[high])
swap(data, low, high);
// 保证中间较小
if (data[media] > data[high])
swap(data, high, media);
// 保证左端较小
if (data[media] > data[low])
swap(data, media, low);
pivot = data[low];
return pivot;
}
- 避免不必要的交换
private static void quickSort(int[] data, int low, int high) {
if (low < high) {
// int k = partition(data, low, high);
int k = partitionSuper(data, low, high);
quickSort(data, low, k - 1);
quickSort(data, k + 1, high);
}
}
public static void quickSort(int[] data) {
quickSort(data, 0, data.length - 1);
}
private static int medianOfThree(int[] data, int low, int high) {
int pivot;
int media = low + (high - low) / 2;
// 保证左端较小
if (data[low] > data[high])
swap(data, low, high);
// 保证中间较小
if (data[media] > data[high])
swap(data, high, media);
// 保证左端较小
if (data[media] > data[low])
swap(data, media, low);
pivot = data[low];
return pivot;
}
private static int partitionSuper(int[] data, int low, int high) {
int pivot = medianOfThree(data, low, high);
while (low < high) {
while (low < high && data[high] >= pivot)
high--;
// 将交换改为替换
// swap(data, low, high);
data[low] = data[high];
while (low < high && data[low] <= pivot)
low++;
// 将交换改为替换
// swap(data, low, high);
data[high] = data[low];
}
return low;
}
选择排序
简单选择排序的基本思想是每一趟在 N−i+1 个记录中选取最小的记录作为有序序列的第i个记录。
package com.weixuan.sort.select;
public class SelectSort {
private static void swap(int[] data, int j, int i) {
if (data == null || data.length <= 0)
return;
int temp = data[j];
data[j] = data[i];
data[i] = temp;
}
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++)
System.out.print(array[i] + "\t");
}
public static void selectSort(int[] data) {
int minIndex;
for (int i = 0; i < data.length; i++) {
minIndex = i;
for (int j = i + 1; j < data.length; j++) {
if (data[minIndex] > data[j])
minIndex = j;
}
if (i != minIndex) //找到最小值
swap(data, i, minIndex);
}
}
}
过程说明 – {9,1,5,8,3,7,4,6,2}
i = 0,minIndex = 0,j = 1
data[minIndex] > data[1],minIndex = 1, (比较8次)
minIndex = 1,j = 2…一直循环到j = 8
1,9,5,8,3,7,4,6,2
i = 1,minIndex = 1,j = 2
在剩余的部分找最小值,交换 (比较7次)
1,2,5,8,3,7,4,6,9
i = 2,minIndex = 1,j = 3
在剩余的部分找最小值,交换
1,2,3,8,5,7,4,6,9
i = 3,minIndex = 1,j = 4
在剩余的部分找最小值,交换
1,2,3,4,5,7,8,6,9
i = 4,minIndex = 1,j = 5
在剩余的部分找最小值,交换
1,2,3,4,5,6,8,7,9
i = 5,minIndex = 1,j = 6
在剩余的部分找最小值,交换
1,2,3,4,5,6,7,8,9
i = 6,minIndex = 1,j = 7
在剩余的部分找最小值,交换
1,2,3,4,5,6,7,8,9
i = 7,minIndex = 1,j = 1
在剩余的部分找最小值,交换
1,2,3,4,5,6,7,8,9
i = 8,minIndex = 1,j = 1
在剩余的部分找最小值,交换
1,2,3,4,5,6,7,8,9
性能分析
- 无论最好最差情况,比较次数是一样多。第i趟需要进行n-i次关键字比较,总的比较次数
∑i=0n−1(N−i)=1+2+3+...+(n−1)=n(n−1)2 - 交换次数,最好时,本身有序,交换0次,最差的时候,逆序,交换n-i次
- 时间复杂度是 O(N2)