常用排序算法及比较

import java.util.Stack;

public class ArraySort {
    /*
        冒泡排序:
        时间复杂度: 最好,最差,平均都是:O(n^2)
        空间复杂度: 由于是in-place,故O(1)
        稳定
     */
    public static void bubbleSort(int[] a) {
        if (a == null || a.length == 0) {
            return;
        }
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < a.length - i - 1; j++) {
                if (a[j] > a[j + 1]) {
                    swap(a, j, j + 1);
                }
            }
        }
    }

    /*
        插入排序:
        时间复杂度: 最好:O(n)  最差:O(n^2)  平均:O(n^2)
                   最好情况为已经排序,每个元素只需比较一次即可
                   最差情况为倒序
        空间复杂度: O(1)
        稳定
     */
    public static void insertSort(int[] a) {
        if (a.length <= 1) return;
        int len = a.length;
        for (int i = 1; i < len; i++) {
            // 下一个for循环中j > 0的判断条件是可以的,因为
            for (int j = i;  j > 0 && a[j-1] > a[j]; j--) {
                swap(a, j-1, j);
            }
        }
    }

    /*
        希尔排序:
        时间复杂度: 平均:O(n^1.3)
        空间复杂度: O(1)
        不稳定。例如[7, 5, 5, 8],第一次时比较第二个5和7交换
     */
    public static void shellSort(int[] a) {
        if (a.length <= 1) return;
        int len = a.length;
        int h = 1;
        while (h < len/3) {
            h = h*3+1;
        }

        // 注意:这里h >= 1,有等号。因为当h=1时,退化为直接插入排序
        while (h >= 1) {
            for (int i = h; i < len; i++) {
                for (int j = i; j-h >= 0 && a[j-h] > a[j]; j = j-h) {
                    swap(a, j, j-h);
                }
            }
            h = h/3;
        }
    }


    /*
        选择排序:
        时间复杂度: 最好,最差,平均都是O(n^2)
        空间复杂度: O(1)
        不稳定。例如[5, 4, 5, 2, 9],第一次选择最小的2和前面的5进行交换
     */
    public static void selectSort(int[] a) {
        int len = a.length;
        // 每次找到最小的放在前面,共需a.length次
        for (int i = 0; i < len; i++) {
            int min = i;
            // 每轮比较时比上一轮次数少1
            for (int j = i+1; j < len; j++) {
                if (a[min] > a[j]) {
                    min = j;
                }
            }
            // 由于最小的和前面的进行了交换,所以不稳定
            swap(a, min, i);
        }
    }

    /*
        堆排序:
        时间复杂度: 最好,最差,平均都是O(nlogn)
        空间复杂度: O(1)
        不稳定。例如[36, 27, 27(2), 3].已经是大根堆,如下:
                36
               /  \
              27  27(2)
             /
            3
        第一次交换3和36,输出36。此时3在堆顶,不符合大根堆定义,将3下沉,
        与27交换得到新的大根堆:
                27
               /  \
              3   27(2)

           36
        第二次交换27(2)和27,输出27,此时27(2)在堆顶,符合大根堆,无需调整:
               27(2)
               /
              3    27

            36
        第三次交换27(2)和3,输出27(2),此时只剩3一个节点。最终输出如下:
                3

            27(2)    27

          36
        即最终排序结果是[3, 27(2), 27, 36]。可见两个27与排序之前的位置不同。
        故堆排序不稳定。
     */
    public static void heapSort(int[] a) {
        int len = a.length;
        // len/2-1是最后一个非叶子节点的位置,注意这里的len指的是节点个数
        // 而非最后一个元素的下标
        for (int i = len/2-1; i >= 0; i--) {
            adjustHeap(a, i, len); // 每次要从0~len-1进行调整
        }

        for (int i = len-1; i > 0; i--) {
            swap(a, 0, i);
            adjustHeap(a, 0, i);  // 第一次调整的范围是0~len-2,因为len-1已经和0交换过了
        }
    }

    /**
     *
     * @param a 数组
     * @param i 要从第i个节点下沉调整
     * @param len 调整范围右界,不包括len
     */
    public static void adjustHeap(int[] a, int i, int len) {
        int tmp = a[i];
        for (int k = 2*i+1; k < len; k = k*2+1) {
            if (k+1 < len && a[k] < a[k+1]) { // 左子节点小于右子节点,k指向右子节点
                k++;
            }

            if (a[k] > tmp) { // 如果子节点比父节点大
                // 其实下面应该交换a[i]与a[k],即a[i] = a[k], a[k] = tmp
                // 如果这样的话,将if从句中的判断条件改为a[k] > a[i]更容易理解
                // 但如果下一次又需要交换的话,这次的a[k] = tmp相当于白执行了
                // i在这里每次调整,相当于往下走,每次的i为交换后的子节点下标
                // 因此用tmp来记录父节点的值,直到不需要交换时,再将tmp赋值给a[i]
                a[i] = a[k];  // 将子的节点的值赋给根节点,之后继续看子节点的子节点是否符合要求
                i = k; // 从这里可以看出调整大顶堆是从上至下的一个过程
            } else {
                break; // 如果该节点没有任何问题,则退出循环。即堆调整的前提是子堆都是正确的
            }
        }
        a[i] = tmp; // 调整结束后,将tmp放回结束为止
    }

    /*
        归并排序:
        时间复杂度: 最好,最差,平均都是O(nlogn)
        空间复杂度: O(n) 因为要用到数组来暂存归并元素
     */
    public static void mergeSort(int[] a) {
        mergeSort(a, 0, a.length-1);
    }

    public static void mergeSort(int[] a, int l, int r) {
        if (l < r) {
            int mid = (l+r)/2;
            mergeSort(a, l, mid);
            mergeSort(a, mid+1, r);
            merge(a, l, mid, r);
        }
    }

    public static void merge(int[] a, int l, int mid, int r) {
        int[] tmp = new int[r-l+1];
        int i = l;
        int j = mid+1;
        int t = 0;
        while (i <= mid && j <= r) {
            tmp[t++] = a[i] <= a[j] ? a[i++] : a[j++];
        }

        while (i <= mid) {
            tmp[t++] = a[i++];
        }

        while (j <= r) {
            tmp[t++] = a[j++];
        }

        t = 0;
        while (l <= r) {
            a[l++] = tmp[t++];
        }
    }

    public static void mergeSortIterative(int[] a) {
        if (a == null || a.length <= 1) {
            return;
        }
        int len = a.length;
        for (int size = 1; size < len; size = size+size) {
            for (int low = 0; low < len-size; low = low+2*size) {
                merge(a, low, low+size-1, Math.min(low+2*size-1, len-1));
            }
        }
    }


    public static void swap(int[] a, int i, int j) {
        int tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }

    /*
        快速排序
        时间复杂度:最好:O(nlogn)   最差:O(n^2)   平均:  O(nlogn)
                  最差情况对应于正序和逆序
                  如果数组所有元素都相同,则在findPivot中进行比较时,
                  遇到和a[left]相等的元素也交换,可以避免最差情况发生
        空间复杂度:O(logN)
     */
    public static void quickSortIterative(int[] a) {
        if (a == null || a.length == 0) {
            return;
        }
        Stack stack = new Stack<>();
        stack.push(a.length-1);
        stack.push(0);
        while (!stack.isEmpty()) {
            int left = stack.pop();
            int right = stack.pop();
            int pivot = findPivot(a, left, right);
            if (pivot+1 < right) {
                stack.push(right);
                stack.push(pivot+1);
            }
            if (left < pivot-1) {
                stack.push(pivot-1);
                stack.push(left);
            }
        }
    }

    public static void quickSort(int[] a) {
        if (a == null || a.length == 0) {
            return;
        }
        quickSort(a, 0, a.length-1);
    }

    public static void quickSort(int[] a, int left, int right) {
        if (left < right) {
            int pivot = findPivot(a, left, right);
            quickSort(a, left, pivot-1);
            quickSort(a, pivot+1, right);
        }
    }

    public static int findPivot(int[] a, int left, int right) {
        int i = left;
        int j = right;
        int tmp = a[left];
        while (i < j) {
            while (i < j && a[j] > tmp) {
                j--;
            }
            if (i < j) {
                a[i++] = a[j];
            }
            while (i < j && a[i] < tmp) {
                i++;
            }
            if (i < j) {
                a[j--] = a[i];
            }
        }
        a[i] = tmp;
        return i;
    }

    public static void showArray(int[] a) {
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int[] arr = new int[]{6, 4, 7, 3, 9, 1, 0, 2};
        bubbleSort(arr);
        showArray(arr);
        arr = new int[]{6, 4, 7, 3, 9, 1, 0, 2};
        insertSort(arr);
        showArray(arr);
        arr = new int[]{6, 4, 7, 3, 9, 1, 0, 2};
        shellSort(arr);
        showArray(arr);
        arr = new int[]{6, 4, 7, 3, 9, 1, 0, 2};
        selectSort(arr);
        showArray(arr);
        arr = new int[]{6, 4, 7, 3, 9, 1, 4, 0, 2, 11, 5};
        heapSort(arr);
        System.out.println("Heap sort:");
        showArray(arr);
        arr = new int[]{6, 4, 7, 3, 9, 1, 0, 2};
        mergeSortIterative(arr);
        showArray(arr);
        arr = new int[]{6, 4, 7, 3, 9, 1, 4, 0, 2, 11, 5};
        quickSort(arr);
        showArray(arr);
    }
}

你可能感兴趣的:(常用排序算法及比较)