排序是数据处理中十分常见且核心的操作,虽说实际项目开发中很小几率会需要手动实现,毕竟每种语言的类库中都有多种关于排序算法的实现。但是了解这些精妙的思想还是很有好处的。以下简单温习下最基础的三类算法:选择,冒泡,插入。
首先定义一个交换数组元素的函数,以供调用
package yrwan08;
public class Swap {
/**
*
* @param arr
* @param a
* @param b
*/
public static void swap(int[] arr, int a, int b) {
int temp;
temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
// // 利用异或不可交换相同数据!
// public static void swap(int[] arr, int a, int b) {
// arr[a] ^= arr[b];
// arr[b] ^= arr[a];
// arr[a] ^= arr[b];
// }
}
直接选择排序
直接选择排序是每一次从待排序的数据元素中选出最小的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
分为三步:
- 从待排序序列中,找到最小的元素
- 如果最小元素不是待排序序列的第一个元素,将其与第一个元素互换
- 从余下的 N - 1 个元素中,找出最小的元素,重复(1)、(2)步,直到排序结束
package yrwan02;
public class SelectSort {
/**
* 改进的选择排序 设置一个变量minIndex存储较小元素的数组下标
*
* @param arr
*/
public static void selectSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {// 第i轮,i前的元素已排好
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[minIndex] > arr[j]) {
minIndex = j;
}
}
if (minIndex != i) {
Swap.swap(arr, minIndex, i);// 如果minIndex发生变化,则进行交换
}
}
}
}
冒泡排序
冒泡排序的基本思想是,对相邻的元素进行比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素“浮”到顶端,最终达到完全有序。
注:若某一趟中,一次交换都没有进行,则说明剩下的序列已经是有序的,排序操作也就可以完成了
package yrwan02;
public class BubbleSort {
/**
* 冒泡排序 外循环仅代表排序的趟数,内循环表示每轮参与比较的元素下标,向右冒泡
*
* @param arr
*/
public static void bubbleSort1(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {// 第i轮
boolean flag = true;
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
Swap.swap(arr, j, j + 1);
}
flag = false;
}
if (flag) {
break;// 某一轮没有交换,则意味着已排好顺序
}
}
}
/**
* 冒泡排序 从左冒泡
*
* @param arr
*/
public static void bubbleSort2(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {// 第i轮,i前的元素已排好
boolean flag = true;
for (int j = arr.length - 1; j > i; j--) {
if (arr[j] < arr[i]) {
Swap.swap(arr, j, i);
}
flag = false;
}
if (flag) {
break;// 某一轮没有交换,则意味着已排好顺序
}
}
}
}
直接插入排序
直接插入排序基本思想是每一步将一个待排序的记录,插入到前面已经排好序的有序序列中去,直到插完所有元素为止。
package yrwan02;
public class InsertSort {
/**
* 插入排序:每一次将一个待排序的元素,插入到前面已经排好序的有序序列中去,直到插完所有元素为止。
*
* @param arr
*/
public static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {// 第i轮,i前的元素已排好
int temp = arr[i];// 暂存第i个元素
int j = i;
while (j > 0 && arr[j - 1] > temp) {// 第j-1个元素与temp比较, 比 temp大就右移
arr[j] = arr[j - 1];// 右移
j--;
}
arr[j] = temp;
}
}
}
测试类:
package yrwan02;
import org.junit.Test;
public class SortTest {
/**
* junit测试类
*/
@Test
public void sortTest() {
int[] arr = new int[] { 45, 30, 1, 5, -7 };
for (int i : arr) {
System.out.print(i + " ");
}
System.out.println();
// BubbleSort.bubbleSort(arr);
SelectSort.selectSort(arr);
// InsertSort.insertSort(arr);
for (int i : arr) {
System.out.print(i + " ");
}
}
}
总结
这三种排序算法的时间复杂度均为O(n2),一般不会选择冒泡排序,虽然冒泡排序书写是最简单的,但是平均性能是没有选择排序和插入排序好的。
选择排序可以把交换次数降低到最低,但是比较次数还是挺大的。当数据量小,并且交换数据相对于比较数据更加耗时的情况下,可以应用选择排序。
在大多数情况下,假设数据量比较小或基本有序时,插入排序是三种算法中最好的选择。