1.Shell排序算法
本人部分理论内容参考自这里,感兴趣者可以直接在此处查看。
Shell sort is a sorting algorithm that requires asymptotically fewer
than O(n²) comparisons and exchanges in the worst case. Although it is
easy to develop an intuitive sense of how this algorithm works, it is
very difficult to analyze its execution time, but estimates range from
O(nlog2 n) to O(n1.5) depending on implementation details.Shell sort is a generalization of insertion sort, with two
observations in mind:
- Insertion sort is efficient if the input is “almost sorted”.
- Insertion sort is inefficient, on average, because it moves values just one position at a time.
Shell sort improves insertion sort by comparing elements separated by
a gap of several positions. This lets an element take “bigger steps”
toward its expected position. Multiple passes over the data are taken
with smaller and smaller gap sizes. The last step of Shell sort is a
plain insertion sort, but by then, the array of data is guaranteed to
be almost sorted.简言之:希尔排序得名于其发明者Donald Shell,是一种运行复杂度在O(nlogn)~O(n^1.5)之间的一种算法,具体复杂度取决于实现细节,即:gap的选择。
作为一种特殊的插入排序算法,其独到之处在于2点:
1. 当待排序数组几乎已经有序时,插入排序效率较高。
2. 插入排序整体效率较低,因为它每次只移动一个值。
1.gap=arr.length/2:
2. 第一次粗排序后结果:
3. gap = gap/2;
4. 第二次粗排序后结果:
5. gap=1,最后进行一波插入排序示意图(可以看到,需要进行交换的元素数量较少,效率就从这里体现出来了):
2.插入排序算法&&Shell 排序
实质上,Shell排序就是加了一层gap的插入排序,但是带来了的性能的大幅提升,不可思议。
插入排序
/**
* @param arr
* @author fqyuan
* @see A little twisted among the 3 simple sort method:
* select/insert/bubble.
*/
public static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int key = arr[i];
int j = i - 1;
// Find the position to insert at: j.
for (; j >= 0 && arr[j] > key; j--)
arr[j + 1] = arr[j];
arr[j + 1] = key;
}
}
Shell排序
/**
* @param arr
* @author fqyuan
* @see Shell sort:
*/
public static void shellSort(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap = gap == 2 ? 1 : (int) (gap / 2.2)) {
for (int i = gap; i < arr.length; i++) {
int temp = arr[i];
int j = i - gap;
for (; j >= 0 && arr[j] > temp; j -= gap)
arr[j + gap] = arr[j];
arr[j + gap] = temp;
}
}
}
3.性能比较
由运行结果可知:这里选取了10,000,000个随机数排列,运行结果表现出quick sort快速排序表现最好,merge sort和 shell sort性能相近。
package com.fqyuan.sort;
import java.util.Random;
import org.junit.Test;
public class SortUtils {
/**
* @param arr
* @author fqyuan
* @see 这里的swap操作在第二层循环之外,减少了交换的次数,改善了部分性能。
*/
public static void selectSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < arr.length; j++) {
if (arr[minIndex] > arr[j])
minIndex = j;
}
swap(arr, minIndex, i);
}
}
/**
* @param arr
* @author fqyuan
* @see 加入了sorted flag之后,如果是已经排序好的数组,可以大幅减少排序swap次数。
*/
public static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
boolean sorted = true;
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
sorted = false;
}
}
if (sorted)
break;
}
}
/**
* @param arr
* @author fqyuan
* @see A little twisted among the 3 simple sort method:
* select/insert/bubble.
*/
public static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int key = arr[i];
int j = i - 1;
// Find the position to insert at: j.
for (; j >= 0 && arr[j] > key; j--)
arr[j + 1] = arr[j];
arr[j + 1] = key;
}
}
public static void mergeSort(int[] arr) {
if (arr.length < 2)
return;
int mid = arr.length / 2;
int[] left = new int[arr.length / 2];
int[] right = new int[arr.length - mid];
for (int i = 0; i < mid; i++)
left[i] = arr[i];
for (int i = mid; i < arr.length; i++)
right[i - mid] = arr[i];
mergeSort(left);
mergeSort(right);
merge(left, right, arr);
}
private static void merge(int[] left, int[] right, int[] arr) {
int nL = left.length;
int nR = right.length;
int i = 0, j = 0, k = 0;
while (i < nL && j < nR) {
if (left[i] <= right[j])
arr[k++] = left[i++];
else
arr[k++] = right[j++];
}
if (i < nL) {
while (i < nL)
arr[k++] = left[i++];
} else {
while (j < nR)
arr[k++] = right[j++];
}
}
public static void quickSort(int arr[]) {
quickSort(arr, 0, arr.length - 1);
}
private static void quickSort(int[] arr, int startIndex, int endIndex) {
if (startIndex < endIndex) {
// int pivot = randomizePartiton(arr, startIndex, endIndex);
int pivot = partition(arr, startIndex, endIndex);
quickSort(arr, startIndex, pivot - 1);
quickSort(arr, pivot + 1, endIndex);
}
}
private static int randomizePartiton(int[] arr, int startIndex, int endIndex) {
int randomIndex = (int) Math.floor((Math.random() * (endIndex - startIndex + 1) + startIndex));
swap(arr, randomIndex, endIndex);
return partition(arr, startIndex, endIndex);
}
private static int partition(int arr[], int startIndex, int endIndex) {
// Randomize the pivot.
int randomIndex = (int) Math.floor((Math.random() * (endIndex - startIndex + 1) + startIndex));
swap(arr, randomIndex, endIndex);
int pivotValue = arr[endIndex];
int pivotIndex = startIndex;
for (int i = startIndex; i < endIndex; i++) {
if (arr[i] < pivotValue) {
swap(arr, i, pivotIndex);
pivotIndex++;
}
}
swap(arr, pivotIndex, endIndex);
return pivotIndex;
}
/**
* @param arr
* @author fqyuan
* @see Shell sort:
*/
public static void shellSort(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap = gap == 2 ? 1 : (int) (gap / 2.2)) {
for (int i = gap; i < arr.length; i++) {
int temp = arr[i];
int j = i - gap;
for (; j >= 0 && arr[j] > temp; j -= gap)
arr[j + gap] = arr[j];
arr[j + gap] = temp;
}
}
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public void printArr(int arr[]) {
// for (int val : arr)
// System.out.print(val + " ");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
if ((i + 1) % 10 == 0)
System.out.println();
}
System.out.println();
}
@Test
public void test() {
// selectSort time test.
long start = System.currentTimeMillis();
Random random = new Random();
int[] arr = new int[100000];
for (int i = 0; i < arr.length; i++)
arr[i] = random.nextInt(100000);
// printArr(arr);
// selectSort(arr);
// printArr(arr);
long end = System.currentTimeMillis();
System.out.println("Select Sort -- time taken is: " + (end - start) + "ms");
// insertSort time test
start = System.currentTimeMillis();
arr = new int[100000];
for (int i = 0; i < arr.length; i++)
arr[i] = random.nextInt(100000);
// printArr(arr);
// insertSort(arr);
// printArr(arr);
end = System.currentTimeMillis();
System.out.println("Insert Sort -- time taken is: " + (end - start) + "ms");
// bubbleSort time test
start = System.currentTimeMillis();
arr = new int[100000];
for (int i = 0; i < arr.length; i++)
arr[i] = random.nextInt(100000);
// printArr(arr);
// bubbleSort(arr);
// printArr(arr);
end = System.currentTimeMillis();
System.out.println("Bubble Sort -- time taken is: " + (end - start) + "ms");
// mergeSort time test
start = System.currentTimeMillis();
arr = new int[10000000];
for (int i = 0; i < arr.length; i++)
arr[i] = random.nextInt(10000000);
// printArr(arr);
mergeSort(arr);
// printArr(arr);
end = System.currentTimeMillis();
System.out.println("Merge Sort -- time taken is: " + (end - start) + "ms");
// quickSort time test
start = System.currentTimeMillis();
arr = new int[10000000];
for (int i = 0; i < arr.length; i++)
arr[i] = random.nextInt(1000000);
// printArr(arr);
quickSort(arr);
// printArr(arr);
end = System.currentTimeMillis();
System.out.println("Quick Sort -- time taken is: " + (end - start) + "ms");
// quickSort time test
start = System.currentTimeMillis();
arr = new int[10000000];
for (int i = 0; i < arr.length; i++)
arr[i] = random.nextInt(10000000);
// printArr(arr);
shellSort(arr);
// printArr(arr);
end = System.currentTimeMillis();
System.out.println("Shell Sort -- time taken is: " + (end - start) + "ms");
}
@Test
public void test1() {
Random random = new Random();
int[] arr = new int[100];
for (int i = 0; i < 100; i++) {
arr[i] = random.nextInt(100);
}
printArr(arr);
shellSort(arr);
printArr(arr);
}
}
//Running result(单元测试test的结果):
Select Sort -- time taken is: 21ms
Insert Sort -- time taken is: 1ms
Bubble Sort -- time taken is: 0ms
Merge Sort -- time taken is: 2030ms
Quick Sort -- time taken is: 1231ms
Shell Sort -- time taken is: 1866ms