排序算法
冒泡排序、选择排序、插入排序共有特性
- 均为简单排序
- n个数,比较n-1轮
- 第i轮比较时,需比较n-i次
- 时间复杂度均为O(n^2)
冒泡排序
/**
* 冒泡排序
*
* 每一轮,前一个数和后一个数比,若是前数大,则交换位置,第一轮找出最大值放在末尾,下一轮不参与比较
* 以此类推
*/
public class Bubble {
public static void bubbleSort(int[] a){
for(int i=1;i<a.length;i++){
// 优化,增加flag,若当前轮数未出现交换,则证明此时数组已经排序结束
boolean flag= true;
for(int j=0;j<a.length-i;j++){
if(a[j]>a[j+1]){
Test.swap(a,j,j+1);
flag= false;
}
}
if(flag){
break;
}
}
}
}
选择排序
/**
* 选择排序
*
* 第一轮,找到所有数中的最小值,放在第一个位置
* 第二轮,找到除去第一个之外的最小值,放在第二个位置
* 以此类推
*/
public class Select {
public static void selectSort(int[] a) {
// i=0, 第一轮比较,备份第一个数,下标为i,假设当前数为最小值
for (int i = 0; i < a.length - 1; i++) {
int min = i;
for (int j = i + 1; j < a.length; j++) {
if (a[min] > a[j]) {
min = j;
}
}
if (min != i) {
Test.swap(a, i, min);
}
}
}
}
插入排序
/**
* 插入排序(打扑克原理)
*
* 左边数据当做有序,右侧当做无序
* 第一轮,与左侧数据比较,当小于左侧数据时,交换位置
* 以此类推
*/
public class Insert {
public static void insertSort(int[] a) {
for (int i = 1; i < a.length ; i++) {
int temp=i;
for (int j = i-1; j >= 0; j--) {
if (a[temp] < a[j]) {
Test.swap(a, j, temp);
temp = j;
}
}
}
}
}
堆排序
/**
* 堆排序
*
* 构建大顶堆
* 将堆顶(即最大值)与末尾元素交换,则数组最大值已确定
* 除去末尾值重新构建大顶堆
* 以此类推
*/
public class Heap {
public static void heapSort(int[] a) {
int len = a.length;
// 所有非叶子节点进行大顶堆的构建,从下至上
for (int i = (len - 2) / 2; i >= 0; i--) {
adjustHeap(a, i, len);
}
for (int i = len - 1; i > 0; i--) {
Test.swap(a, i, 0);
adjustHeap(a, 0, i);
}
}
/**
* 构建大顶堆
* @param a
*/
private static void adjustHeap(int[] a, int i, int len) {
// 备份当前节点
int temp = a[i];
// 从左至右,找到当前节点的左孩子
for (int j = i * 2 + 1; j < len; j = j * 2 + 1) {
// 左孩子与右孩子比较,找到最大值
if (j + 1 < len && a[j] < a[j + 1]) {
j++;
}
// 父节点值更大
if (temp >= a[j]) {
break;
}
// 子节点值更大,与子节点交换
else {
a[i] = a[j];
i = j;
}
}
a[i] = temp;
}
}
快速排序
public class Quick {
public static void quickSort(int[] a, int left, int right) {
if (left < right) {
// 使用分治法策略来把一个串行(list)分为两个子串行(sub-lists)
int meetting = partition(a, left, right);
quickSort(a, 0, meetting - 1);
quickSort(a, meetting + 1, right);
}
}
/**
* i<j
* j从右侧开始,找到比基准值小的数时,停止
* i从左侧开始,找到比基准值大的数停止,交换i与j的值
* 继续寻找,直至相遇,相遇位置与基准值进行交换
*/
private static int partition(int[] a, int left, int right) {
// 优化,通常取第一个数为基准值,若该数过大或过小,增加算法复杂度,取首尾中间三数的中间值作为基准,更为合适
setBase(a,left,right);
int temp = a[left];
while (left < right) {
while (left < right && a[right] >= temp) {
right--;
}
a[left] = a[right];
while (left < right && a[left] <= temp) {
left++;
}
a[right] = a[left];
}
a[left] = temp;
return left;
}
// 三数取中,设置基准值,在第一个位置
public static void setBase(int[] a, int left, int right) {
int mid = (left + right) / 2;
if (a[mid] > a[right]) {
Test.swap(a, mid, right);
}
if (a[left] > a[right]) {
Test.swap(a, left, right);
}
if (a[mid] > a[left]) {
Test.swap(a, left, mid);
}
}
}
测试及总结
设定某一基准值,小于该值则选择插入排序(插入排序是简单排序中性能最好的),大于该值,选择快速排序(复杂排序中速度最快)
public class Test {
public static void main(String[] args) {
int[] a = new int[5];
for (int i = 0; i < a.length; i++) {
a[i] = (int) (Math.random() * a.length * 10);
}
System.out.println(Arrays.toString(a));
// Bubble.bubbleSort(a);
// Insert.insertSort(a);
// Select.selectSort(a);
// Heap.heapSort(a);
// Quick.quickSort(a, 0, a.length - 1);
sort(a);
System.out.println(Arrays.toString(a));
}
public static void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
public static void sort(int[] a) {
int pivot = 50;
if (a.length < pivot) {
Insert.insertSort(a);
} else {
Quick.quickSort(a, 0, a.length - 1);
}
}
}