1. 程式人生 > >Shell排序和插入排序

Shell排序和插入排序

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:

  1. Insertion sort is efficient if the input is “almost sorted”.
  2. 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