常用排序算法及比较

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<Integer> 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);
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容