排序算法

快速排序

步骤

  1. 从列表选出一个基准值 pivot
  2. 重新排列数组,所有比基准值小的放在基准值左边,所有比基准值大的放在基准值右边。
  3. 递归把基准值左右两边的数列排序。

时间复杂度O(nlogn)

最坏的情况:序列已经是升序排序,需要比较n(n-1)/2次,为O(n^2)

最好的情况:每次划分都是正好两半 T(n) <= 2T(n/2)+O(n) —> T(n) = O(nlogn)

空间复杂度O(nlogn):由于是递归调用,空间复杂度为O(nlogn)

快速排序是不稳定的,时间复杂度O(nlogn),不稳定发生在中枢元素和a[j],中枢元素左边的元素可能和右边的元素相等,当中枢元素和其交换时,破坏了稳定性

public static void quickSort(int[] arr, int left, int right) {
        if (left >= right) {
            return;
        }
        int pivot = partition(arr, left, right);
        quickSort(arr, left, pivot - 1);
        quickSort(arr, pivot + 1, right);
    }

    public static int partition(int[] arr, int low, int high) {
        int pivot = arr[low];
        while (low < high) {
            while (low < high && arr[high] >= pivot)
                high--;
            arr[low] = arr[high];
            while (low < high && arr[low] <= pivot)
                low++;
            arr[high] = arr[low];
        }
        arr[low] = pivot;
        return low;
    }

冒泡排序

相邻两个元素进行交换,把大的元素浮到最上面

时间复杂度O(n^2):最外层for执行n次,内层循环j从0~i-1,i从n-1~1,复杂度为n*(n-1)/2,

空间复杂度O(1):只需要一个temp位置交换

稳定:比较两个相邻元素并进行交换,如果相等,则不会交换,稳定性没有被破坏。

public void bubbleSort(int[] arr) {
        if(arr == null || arr.length == 0)
            return ;
        for(int i=0;i<arr.length-1;i++) {
            for(int j=0;j<arr.length-i-1;j++) {
                if (arr[j]>arr[j+1]) {
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
}

选择排序

一次排序后把最小的元素放在最前面

时间复杂度O(n^2):比较次数和初始状态无关,为n(n-1)/2,最好,最坏和平均情况均为O(n2)

空间复杂度O(1):

不稳定,不稳定发生在最小的元素当前元素交换时,如果当前元素在最小元素前面,并且比和当前元素相等时,交换之后稳定性就被破坏

public static void selectSort(int[] arr) {
        if(arr == null || arr.length == 0)
            return ;
        int minIndex = 0;
        for(int i=0;i<arr.length-1;i++) {//比较n-1次
            minIndex = I;
            for (int j = i+1; j < arr.length; j++) {//从i+1开始比较,minIndex默认为I
                if (arr[j]<arr[minIndex]) {
                    minIndex = j;
                }
            }
            if (minIndex!=i) {
                swap(arr, i, minIndex);//minIndex不为i,说明找到更小的,交换
            }
        }
}

插入排序

在一个已经排好序的序列中,为下一个找到一个合适的插入位置

时间复杂度O(n^2):外层for共执行arr.length-2次,内层循环最少执行1次,最多执行index次,算法复杂度n*(n-1)/2

空间复杂度O(1):只需要一个位置key用于交换

稳定:插入排序是在已经有序的小序列中进行插入一个元素,即使遇到相等的元素,也是插入在其后面,稳定性没有被破坏

public static void insertSort(int[] arr) {
        if (arr == null || arr.length == 0)
            return;
        for (int i = 1; i < arr.length; i++) {// 假设第一个位置正确,要往后移,必须假设第一个
            int j = I;
            int target = arr[i];// 待插入
            while (j > 0 && target < arr[j - 1]) {// 往后移
                arr[j] = arr[j - 1];
                j--;
            }
            arr[j] = target;
        }
}

希尔排序

思想:分组插入排序,将整个待排序序列分割成若干个子序列,并分别进行插入排序,然后再缩小增量继续排序,当增量足够小时,再对全体元素进行直接插入排序。

时间复杂度O(nlogn):

时间复杂度依赖于增量序列

最好的情况:序列是升序序列,需要比较n次,后移赋值操作为0次,O(n)

最坏的情况:序列的降序序列,O(nlogn)

空间复杂度O(1):只需要一个位置置换

public static void shellSort(int[] arr) {
        int i, j, gap;
        for (gap = arr.length / 2; gap > 0; gap /= 2) {
            for (i = gap; i < arr.length; i++) {
                for (j = i - gap; j >= 0 && arr[j] > arr[j + gap]; j -= gap)
                    swap(arr, j, j + gap);
            }
        }
    }

归并排序

合并两个有序的子数组

思想:递归分治+归并

把待排序列看成两个有序序列,然后合并两个子序列。倒着看,两两合并,四四合并。。。最终形成有序序列。

时间复杂度O(nlogn):归并排序是一种非“就地”排序,需要和待排序序列一样多的辅助空间,故归并排序的缺点是所需额外空间多,对长度为n的数组,需要进行logn趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论在最好还是最坏情况下都是O(nlogn)

空间复杂度O(n):排序时需要和待排序序列一样的空间,空间复杂度为O(n)

稳定:归并排序是把序列递归的分成短序列,递归出口是只有一个元素或者2个元素短有序序列,然后把各个有序序列合并成有序的长序列,不断合并知道原序列全部排好序。在短序列中,即使两个元素相等也不会交换,因此稳定性没有被破坏。

使用场合

public static void mergeArray(int[] arr, int left, int mid, int right) {
        if (arr == null || arr.length == 0)
            return;
        int[] temp = new int[right-left+1];
        int i = left, j = mid + 1;
        int k = 0;
        // 二路归并
        while (i < mid && j <= right) {
            if (arr[i] <= arr[j]) {
                temp[k++] = arr[i++];
            } else {
                temp[k++] = arr[j++];
            }
        }
        // 处理子数组中剩余元素
        while (i <= mid) {
            temp[k++] = arr[i++];
        }
        while (j <= right) {
            temp[k++] = arr[j++];
        }
        // 从临时数组中拷贝到目标数组
        for (i = 0; i < temp.length; i++) {
            arr[left + i] = temp[I];
        }
    }

    public static void mergeSort(int[] arr, int left, int right) {
        if (left < right) {
            int mid = (left + right) / 2;
            // 归并排序使得左边序列有序
            mergeSort(arr, left, mid);
            // 归并排序使得右边序列有序
            mergeSort(arr, mid + 1, right);
            // 合并两个有序序列
            mergeArray(arr, left, mid, right);
        }
    }

堆排序

时间复杂度O(nlogn):最好,最坏,平均均为O(nlogn)

空间复杂度O(1)

思想:

堆(二叉堆):一棵完全二叉树

  1. 最大堆调整:将堆末端子节点作调整,使得子节点永远小于父节点
  2. 创建最大堆:将堆所有数据重新排序,使其成为最大堆
  3. 堆排序:移除位于第一个数据的根节点,并作最大堆调整堆递归运算,将最大的元素从堆中分离

parent(i) = floor((i-1)/2),i的父节点下标

left(i) = 2i+1,i的左子节点下标

right(i) = 2(i+1),i的右子节点下标

不稳定:堆和其子节点(堆,左节点,右节点)交换不会破坏稳定性,堆顶元素移除时,父节点和某个元素交换,刚好该元素后面有相同的元素,就会破坏稳定性。

    public static void heapSort(int[] arr) {
        int len = arr.length - 1;
        int beginIndex = (len - 1) / 2;// 第一个非叶子节点
        // 将数组堆化
        for (int i = beginIndex; i >= 0; i--) {
            maxHeapify(arr, i, len);
        }
        // 对堆化数组排序,每次都移出最顶层节点arr[0],与尾部节点位置调换,同时遍历长度-1,
        // 然后从新调整被换到根节点末尾都元素,使其符合堆堆特性,直至未排序堆堆长度未0
        for (int j = len; j >= 0; j--) {
            swap(arr, 0, j);
            maxHeapify(arr, 0, j - 1);
        }
    }

    private static void maxHeapify(int[] arr, int index, int len) {
        int li = (index * 2) + 1;// 左子节点索引
        int ri = 2 * (index + 1);// 右子节点索引
        int cMax = li;// 子节点值最大索引,默认左子节点
        if (li > len)
            return;// 左子节点索引超出计算范围,直接返回
        if (ri <= len && arr[ri] > arr[li])
            cMax = ri;// 先判断左右子节点哪个大
        if (arr[cMax] > arr[index]) {
            swap(arr, cMax, index);// 如果父节点被子节点调换
            maxHeapify(arr, cMax, len);// 则需要继续判断换下后堆父节点是否符合堆堆性质
        }
    }
算法时空复杂度对比

不稳定排序:选择排序,快速排序,希尔排序,堆排序

稳定排序:冒泡排序,插入排序,归并排序,基数排序

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

推荐阅读更多精彩内容

  • 欢迎访问我的博客原文⚠️⚠️⚠️郑重声明:转载请务必注明出处! 排序算法是程序员必备的基础知识,弄明白它们的原理和...
    FiTeen阅读 883评论 0 0
  • 一、 选择排序 选择排序的基本思想是每次从待排序子表中挑选出最小的元素放在已经排好序子表的最后位置,直至全部元素排...
    Y_Stone阅读 431评论 0 1
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    zwb_jianshu阅读 1,103评论 0 0
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    闲云清烟阅读 756评论 0 6
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    printf200阅读 760评论 0 0