一些简单的说明:
1:代码可能有点多,而且使用了范型,你可以先运行看一下排序后的效果。
2:之所以将插入排序和快速排序放在一章里面说明,是因为在要排序的数据很少的时候,插入排序是最理想的排序方式,所以在快速排序到数据很少的时候就是调用插入排序来完成排序的任务。
3:插入排序提供了两种实现,一种是针对整个排序数据进行排序操作,一种只对排序数据的部分区间进行排序(方便快速排序调用)。
4:快速排序里面有一个sortFirstMiddleLast方法来挑选中间元素,这个是非常必要的!是用来尽量让中点元素是真正的中点,这样可以大大加快快速排序。
5:也可以使用上百万的数据对排序进行测试,就能看出快速排序确实很快。
请看代码,全部都有详细的注释,希望有助于大家理解这两种排序的实现。
public class SortDemo {
/**
* 当数组个数为LIMIT的时候使用插入排序
*/
public static int LIMIT = 5;
/**
* 插入排序,当数组有序的时候是比较快的排序方法 非稳定算法,最快(当数组有序时)为线性(O(n)) 最慢以及平均情况都是为(O(n^2))二次算法
* @param <T>
* 范型参数
* @param data
* 要排序的数组
*/
public static <T extends Comparable<T>> void insertionSort(T[] data) {
if (data == null) // 检查数组参数
throw new IllegalArgumentException("data is empty!");
int i, j;
T temp;
for (i = 1; i < data.length; i++) {
temp = data[i]; // 当检查数组中的元素不在正确的位置就和前面的元素交换,直到插入到正确的位置为止。
for (j = i; j > 0 && data[j - 1].compareTo(temp) > 0; j--) {
data[j] = data[j - 1];
}
data[j] = temp;
}
}
/**
* 插入排序,当数组有序的时候是比较快的排序方法 非稳定算法,最快(当数组有序时)为线性(O(n)) 最慢以及平均情况都是为(O(n^2))二次算法
* @param <T>
* 范型参数
* @param data
* 要排序的数组
* @param first
* 要排序的起始元素下标
* @param last
* 要排序的结束元素下标
*/
public static <T extends Comparable<T>> void insertionSort(T[] data, int first, int last) {
if (data == null) // 检查数组参数
throw new IllegalArgumentException("data is empty!");
int i, j;
T temp;
for (i = first + 1; i <= last; i++) {
temp = data[i]; // 当检查数组中的元素不在正确的位置就和前面的元素交换,直到插入到正确的位置为止。
for (j = i; j > first && data[j - 1].compareTo(temp) > 0; j--) {
data[j] = data[j - 1];
}
data[j] = temp;
}
}
/**
* 快速排序,非稳定的排序算法,确定为基数选择错误的时候,排序效率为二次 平均情况为(O(nlogn)),最坏情况为(O(n^2))
* @param <T>
* 范型参数
* @param data
* 要排序的数组
*/
public static <T extends Comparable<T>> void quickSort(T[] data) {
quickSort(data, 0, data.length - 1);
}
/**
* 快速排序,非稳定的排序算法,确定为基数选择错误的时候,排序效率为二次 平均情况为(O(nlogn)),最坏情况为(O(n^2))
* @param <T>
* 范型参数
* @param data
* 要排序的数组
* @param int
* first 要排序的起始元素下标
* @param int
* last 要排序的结束元素下标
*/
private static <T extends Comparable<T>> void quickSort(T[] data, int first, int last) {
if ((last - first + 1) < LIMIT) // 排序的数组大小小于限定值的时候调用插入排序
insertionSort(data, first, last);
else { // 递归调用
int pivotIndex = partition(data, first, last);
quickSort(data, first, pivotIndex - 1);
quickSort(data, pivotIndex + 1, last);
}
}
/**
* 将data中由first至last的数据分割成两个部分 左边的元素都小于中点元素,右边都大于中点元素 最后返回中点元素的索引
* @param <T>
* 范型参数
* @param data
* 要调整的数组
* @param first
* 调整的开始元素下标
* @param last
* 调整的最后元素下标
* @return 调整后的中点元素的索引
*/
private static <T extends Comparable<T>> int partition(T[] data, int first, int last) {
int mid = (first + last) / 2; // 从3个元素中挑选一个最有可能的中点元素
sortFirstMiddleLast(data, first, mid, last);
swap(data, mid, last - 1); // 移动支点元素到数组中倒数第二个位置
int pivotIndex = last - 1;
T pivot = data[pivotIndex]; // 记录支点元素
int indexFromLeft = first + 1;
int indexFromRight = last - 2;
boolean done = false;
while (!done) { // 找到左边的大于支点的元素和右边小于支点的元素,交换它们的位置
while (data[indexFromLeft].compareTo(pivot) < 0)
indexFromLeft++;
while (data[indexFromRight].compareTo(pivot) > 0)
indexFromRight--;
if (indexFromLeft < indexFromRight) {
swap(data, indexFromLeft, indexFromRight);
indexFromLeft++;
indexFromRight--;
}
else done = true; // 如果元素下标交错,表示数据已经符合要求
}
swap(data, pivotIndex, indexFromLeft); // 将支点元素放置到数组中间位置
return indexFromLeft;
}
/**
* 快速排序的子方法,确保data[first]<data[mid]<data[last]
* @param <T>
* 范型参数
* @param data
* 提供数据的数组
* @param first
* 起始元素下标
* @param mid
* 中点元素下标
* @param last
* 最后元素下标
*/
private static <T extends Comparable<T>> void sortFirstMiddleLast(T[] data, int first, int mid, int last) {
if (data[first].compareTo(data[mid]) > 0) swap(data, first, mid);
if (data[mid].compareTo(data[last]) > 0) swap(data, mid, last);
if (data[first].compareTo(data[mid]) > 0) swap(data, first, mid);
}
/**
* 快速排序的子方法,如果交换data[one]和data[two]的位置
* @param <T>
* 范型参数
* @param data
* 提供数据的数组
* @param one
* 要交换的元素
* @param two
* 要交换的元素
*/
private static <T> void swap(T[] data, int one, int two) {
T temp = data[one];
data[one] = data[two];
data[two] = temp;
}
public static void main(String[] args) {
String[] test = new String[] { "c", "d", "e", "a", "g", "h", "b", "f" };
insertionSort(test);
for (int i = 0; i < test.length; i++) {
System.out.print(test[i] + " ");
}
System.out.println();
test = new String[] { "c", "d", "e", "a", "g", "h", "b", "f" };
insertionSort(test, 1, test.length - 2);
for (int i = 0; i < test.length; i++) {
System.out.print(test[i] + " ");
}
System.out.println();
test = new String[] { "c", "d", "e", "a", "g", "h", "b", "f", "c", "d", "e", "a", "g", "h", "b", "f" };
quickSort(test);
for (int i = 0; i < test.length; i++) {
System.out.print(test[i] + " ");
}
System.out.println();
}
}
最后的话,其实排序功能在java的api里面都已经提供,学习排序只是为了扩展自己的思维,希望对大家有点帮助。
1:代码可能有点多,而且使用了范型,你可以先运行看一下排序后的效果。
2:之所以将插入排序和快速排序放在一章里面说明,是因为在要排序的数据很少的时候,插入排序是最理想的排序方式,所以在快速排序到数据很少的时候就是调用插入排序来完成排序的任务。
3:插入排序提供了两种实现,一种是针对整个排序数据进行排序操作,一种只对排序数据的部分区间进行排序(方便快速排序调用)。
4:快速排序里面有一个sortFirstMiddleLast方法来挑选中间元素,这个是非常必要的!是用来尽量让中点元素是真正的中点,这样可以大大加快快速排序。
5:也可以使用上百万的数据对排序进行测试,就能看出快速排序确实很快。
请看代码,全部都有详细的注释,希望有助于大家理解这两种排序的实现。
public class SortDemo {
/**
* 当数组个数为LIMIT的时候使用插入排序
*/
public static int LIMIT = 5;
/**
* 插入排序,当数组有序的时候是比较快的排序方法 非稳定算法,最快(当数组有序时)为线性(O(n)) 最慢以及平均情况都是为(O(n^2))二次算法
* @param <T>
* 范型参数
* @param data
* 要排序的数组
*/
public static <T extends Comparable<T>> void insertionSort(T[] data) {
if (data == null) // 检查数组参数
throw new IllegalArgumentException("data is empty!");
int i, j;
T temp;
for (i = 1; i < data.length; i++) {
temp = data[i]; // 当检查数组中的元素不在正确的位置就和前面的元素交换,直到插入到正确的位置为止。
for (j = i; j > 0 && data[j - 1].compareTo(temp) > 0; j--) {
data[j] = data[j - 1];
}
data[j] = temp;
}
}
/**
* 插入排序,当数组有序的时候是比较快的排序方法 非稳定算法,最快(当数组有序时)为线性(O(n)) 最慢以及平均情况都是为(O(n^2))二次算法
* @param <T>
* 范型参数
* @param data
* 要排序的数组
* @param first
* 要排序的起始元素下标
* @param last
* 要排序的结束元素下标
*/
public static <T extends Comparable<T>> void insertionSort(T[] data, int first, int last) {
if (data == null) // 检查数组参数
throw new IllegalArgumentException("data is empty!");
int i, j;
T temp;
for (i = first + 1; i <= last; i++) {
temp = data[i]; // 当检查数组中的元素不在正确的位置就和前面的元素交换,直到插入到正确的位置为止。
for (j = i; j > first && data[j - 1].compareTo(temp) > 0; j--) {
data[j] = data[j - 1];
}
data[j] = temp;
}
}
/**
* 快速排序,非稳定的排序算法,确定为基数选择错误的时候,排序效率为二次 平均情况为(O(nlogn)),最坏情况为(O(n^2))
* @param <T>
* 范型参数
* @param data
* 要排序的数组
*/
public static <T extends Comparable<T>> void quickSort(T[] data) {
quickSort(data, 0, data.length - 1);
}
/**
* 快速排序,非稳定的排序算法,确定为基数选择错误的时候,排序效率为二次 平均情况为(O(nlogn)),最坏情况为(O(n^2))
* @param <T>
* 范型参数
* @param data
* 要排序的数组
* @param int
* first 要排序的起始元素下标
* @param int
* last 要排序的结束元素下标
*/
private static <T extends Comparable<T>> void quickSort(T[] data, int first, int last) {
if ((last - first + 1) < LIMIT) // 排序的数组大小小于限定值的时候调用插入排序
insertionSort(data, first, last);
else { // 递归调用
int pivotIndex = partition(data, first, last);
quickSort(data, first, pivotIndex - 1);
quickSort(data, pivotIndex + 1, last);
}
}
/**
* 将data中由first至last的数据分割成两个部分 左边的元素都小于中点元素,右边都大于中点元素 最后返回中点元素的索引
* @param <T>
* 范型参数
* @param data
* 要调整的数组
* @param first
* 调整的开始元素下标
* @param last
* 调整的最后元素下标
* @return 调整后的中点元素的索引
*/
private static <T extends Comparable<T>> int partition(T[] data, int first, int last) {
int mid = (first + last) / 2; // 从3个元素中挑选一个最有可能的中点元素
sortFirstMiddleLast(data, first, mid, last);
swap(data, mid, last - 1); // 移动支点元素到数组中倒数第二个位置
int pivotIndex = last - 1;
T pivot = data[pivotIndex]; // 记录支点元素
int indexFromLeft = first + 1;
int indexFromRight = last - 2;
boolean done = false;
while (!done) { // 找到左边的大于支点的元素和右边小于支点的元素,交换它们的位置
while (data[indexFromLeft].compareTo(pivot) < 0)
indexFromLeft++;
while (data[indexFromRight].compareTo(pivot) > 0)
indexFromRight--;
if (indexFromLeft < indexFromRight) {
swap(data, indexFromLeft, indexFromRight);
indexFromLeft++;
indexFromRight--;
}
else done = true; // 如果元素下标交错,表示数据已经符合要求
}
swap(data, pivotIndex, indexFromLeft); // 将支点元素放置到数组中间位置
return indexFromLeft;
}
/**
* 快速排序的子方法,确保data[first]<data[mid]<data[last]
* @param <T>
* 范型参数
* @param data
* 提供数据的数组
* @param first
* 起始元素下标
* @param mid
* 中点元素下标
* @param last
* 最后元素下标
*/
private static <T extends Comparable<T>> void sortFirstMiddleLast(T[] data, int first, int mid, int last) {
if (data[first].compareTo(data[mid]) > 0) swap(data, first, mid);
if (data[mid].compareTo(data[last]) > 0) swap(data, mid, last);
if (data[first].compareTo(data[mid]) > 0) swap(data, first, mid);
}
/**
* 快速排序的子方法,如果交换data[one]和data[two]的位置
* @param <T>
* 范型参数
* @param data
* 提供数据的数组
* @param one
* 要交换的元素
* @param two
* 要交换的元素
*/
private static <T> void swap(T[] data, int one, int two) {
T temp = data[one];
data[one] = data[two];
data[two] = temp;
}
public static void main(String[] args) {
String[] test = new String[] { "c", "d", "e", "a", "g", "h", "b", "f" };
insertionSort(test);
for (int i = 0; i < test.length; i++) {
System.out.print(test[i] + " ");
}
System.out.println();
test = new String[] { "c", "d", "e", "a", "g", "h", "b", "f" };
insertionSort(test, 1, test.length - 2);
for (int i = 0; i < test.length; i++) {
System.out.print(test[i] + " ");
}
System.out.println();
test = new String[] { "c", "d", "e", "a", "g", "h", "b", "f", "c", "d", "e", "a", "g", "h", "b", "f" };
quickSort(test);
for (int i = 0; i < test.length; i++) {
System.out.print(test[i] + " ");
}
System.out.println();
}
}
最后的话,其实排序功能在java的api里面都已经提供,学习排序只是为了扩展自己的思维,希望对大家有点帮助。