1.冒泡排序:
思路:将相邻的逆序元素交换为顺序排列,直到整个序列有序,算法如下:
/**
* 冒泡排序-最初实现,时间复杂度O(n^2)
* @param arr 待排序的数组
* @param lo 待排序区间的起始位置
* @param hi 待排序区间的结束位置
*/
public static void bubbleSort(int[] arr, int lo, int hi) {
//对序列进行规模n-1次扫描
for (int i = lo; i < hi - 1; i++) {
//每次扫描都会将当前区间最大的元素移到末尾,所以每次扫描的范围都可以减少1
for (int j = lo; j < hi - i - 1; j++) {
//判断相邻元素是否为逆序,是的话交换
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}以上算法在任何情况都会对一个规模为n的序列进行n-1次扫描,即使该序列在中途已经变成有序。所以需要一个标示位来记录每次扫描后该序列是否已经完成排序,改进算法如下:
/**
* 冒泡排序-改进一:记录扫描结果,时间复杂度O(n^(3/2))
* @param arr 待排序的数组
* @param lo 待排序区间的起始位置
* @param hi 待排序区间的结束位置
*/
public static void bubbleSort(int[] arr, int lo, int hi) {
//对序列进行规模n-1次扫描
for (int i = lo; i < hi - 1; i++) {
//设置一个标示位记录序列是否为有序状态
boolean sorted = true;
//每次扫描都会将当前区间最大的元素移到末尾,所以每次扫描的范围都可以减少1
for (int j = lo; j < hi - i - 1; j++) {
//判断相邻元素是否为逆序,是的话交换并将有序标示置为false
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
sorted = false;
}
}
//当一次扫描后有序标示扔为true,则序列已经有序,停止扫描
if (sorted) {
break;
}
}
}
改进后的算法还是有缺陷,即使扫描的次数动态的减少了,但是每次扫描的范围还是固定的。于是接着改进:
/**
* 冒泡排序-改进二:记录逆序范围,时间复杂度O(n)
* @param arr 待排序的数组
* @param lo 待排序区间的起始位置
* @param hi 待排序区间的结束位置
*/
public static void bubbleSort(int[] arr, int lo, int hi) {
//在可能存在逆序的区间消失前一直扫描
while (lo < hi) {
//将秩设为区间的起始位置
int last = lo;
//扫描整个区间
for (int j = lo; j < hi - 1; j++) {
//判断相邻元素是否为逆序,是的话交换并记录下位置
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
last = j + 1;
}
}
//将最后一次进行交换的位置设为区间的结束位置(之后的所有元素均为有序排列)
hi = last;
}
}
三种冒泡排序的时间成本如图所示:
2.选择排序:
思路:每次选取区间内最大(小)的元素移动到区间前端,直到所有元素被选取完毕,算法如下:
/**
* 选择排序,时间复杂度O(n^2)
* @param arr 待排序的数组
* @param lo 待排序区间的起始位置
* @param hi 待排序区间的结束位置
*/
public static void selectionSort(int[] arr, int lo, int hi) {
//对序列进行规模n-1次扫描
for (int i = lo; i < hi - 1; i++) {
int max = arr[i], index = i;
//每次扫描都会记录当前区间最小的元素
for (int j = i + 1; j < hi; j++) {
if (arr[j] < max) {
max = arr[j];
index = j;
}
}
//将最小元素移到区间最前面
if (index != i) {
swap(arr, i, index);
}
}
}选择排序的主要时间成本在于选取区间内的最大元素,这个操作的时间复杂度是O(n),而有种方法可以将这个时间成本降低至O(logn),这将会在之后的堆排序中介绍。
3.归并排序
思路:将整个序列分为多个子序列,并依次将子序列排序,最后合并成有序序列,算法如下:
/**
* 归并排序,时间复杂度O(nlogn)
* @param arr 待排序的数组
* @param lo 待排序区间的起始位置
* @param hi 待排序区间的结束位置
*/
public static void mergeSort(int[] arr, int lo, int hi) {
//如果该区间只剩下一个元素,则推出递归
if (hi - lo < 2) {
return;
}
//以中点为轴点分割两个区间
int mid = (hi + lo) >> 1;
//分别递归两个区间
mergeSort(arr, lo, mid);
mergeSort(arr, mid, hi);
//将两个区间合并
merge(arr, lo, mid, hi);
}
/**
* 归并排序的合并区间
* @param arr 待排序的数组
* @param lo 待合并区间的起始位置
* @param mid 区间分隔的点
* @param hi 待合并区间的结束位置
*/
private static void merge(int[] arr, int lo, int mid, int hi) {
//建立临时数组备份待排序区间
int[] temp = new int[hi - lo];
for (int i = lo; i < hi; i++) {
temp[i - lo] = arr[i];
}
//设置两个秩分别指向两个临时区间的起始位置,设置一个秩指向整个区间的起始位置
int i = 0, j = mid - lo, k = lo;
//当整个区间的秩指向区间的结束位置时停止
while (k < hi) {
//将两个秩指向的元素中较小者存放到待排序区间
if ((i < mid - lo) && (j >= hi - lo || temp[i] < temp[j])) {
arr[k++] = temp[i++];
}
if ((j < hi - lo) && (i >= mid - lo || temp[j] < temp[i])) {
arr[k++] = temp[j++];
}
}
}
4.插入排序
思路:将每个元素取出并插入到合适的位置,使该元素与它前一个和后一个元素均为顺序排列,直到所有元素插入完毕。算法如下:
/**
* 插入排序,时间复杂度O(n^2)
* @param arr 待排序的数组
* @param lo 待排序区间的起始位置
* @param hi 待排序区间的结束位置
*/
public static void insertionSort(int[]arr, int lo, int hi) {
//扫描每一个元素
for (int i = lo + 1; i < hi; i++) {
//如果该元素比它前一个元素要小
if (arr[i] < arr[i - 1]) {
//记录下它的位置和值
int temp = arr[i];
int index = i;
//将比它大的元素依次后移
while (index > 0 && arr[index - 1] > temp) {
arr[index] = arr[index - 1];
index--;
}
//将它插入到合适的位置
arr[index] = temp;
}
}
}未完待续....
原文地址:http://blog.csdn.net/sinat_19425927/article/details/42361421