十大经典排序(java实现)与横向对比

一、排序算法实现

1).冒泡排序

1.1算法描述:

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

1.2代码实现(Java)
    public static int[] bubbleSort(int[] array) {
        if (array.length == 0)
            return array;
        for (int i = 0; i < array.length; i++)
            for (int j = 0; j < array.length - 1 - i; j++)
                if (array[j + 1] < array[j]) {
                    int temp = array[j + 1];
                    array[j + 1] = array[j];
                    array[j] = temp;
                }
        return array;
    }

2) 选择排序

2.1算法描述

在n个数据的序列中,需要经过n-1趟的排序。选择排序的原理就是每次在序列中找到最小(大)值,首先每一次找最值需要定一个标准,把未排序列的第一个当作最小(大)值这么一标准,然后在剩下的序列中再找比这个最值小或者大的值来代替它,最终和未排序列的第一个交换(如果找不到那就是自己和自己交换),重复以上步骤,直到排序完成

2.2代码实现(Java)
    public static int[] selectionSort(int[] array) {
        if (array.length == 0)
            return array;
        for (int i = 0; i < array.length; i++) {
            int minIndex = i;
            for (int j = i; j < array.length; j++) {
                if (array[j] < array[minIndex]) //找到最小的数
                    minIndex = j; //将最小数的索引保存
            }
            int temp = array[minIndex];
            array[minIndex] = array[i];
            array[i] = temp;
        }
        return array;
    }

3)插入排序

3.1算法描述

插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

3.2代码实现(Java)
   public static int[] insertionSort(int[] array) {
        if (array.length == 0)
            return array;
        int current;
        for (int i = 0; i < array.length - 1; i++) {
            current = array[i + 1];
            int preIndex = i;
            while (preIndex >= 0 && current < array[preIndex]) {
                array[preIndex + 1] = array[preIndex];
                preIndex--;
            }
            array[preIndex + 1] = current;
        }
        return array;
    }

4) 希尔排序

4.1算法描述

希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序,同时该算法是冲破O(n2)的第一批算法之一。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序

希尔排序是把记录按下表的一定增量分组,对每组使用直接插入排序算法排序;******随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止

4.2代码实现(Java)
public static int[] ShellSort(int[] array) {
        int len = array.length;
        int temp, gap = len / 2;
        while (gap > 0) {
            for (int i = gap; i < len; i++) {
                temp = array[i];
                int preIndex = i - gap;
                while (preIndex >= 0 && array[preIndex] > temp) {
                    array[preIndex + gap] = array[preIndex];
                    preIndex -= gap;
                }
                array[preIndex + gap] = temp;
            }
            gap /= 2;
        }
        return array;
    }

5)归并排序

5.1算法描述

归并排序是将一个数组分成左右两部分,然后进行比较排序,最终再合并成一个完全有序的一种算法。归并排序在进行分组的过程中,需把序列分到不能再分的情况下(也就是说只剩1个元素的时候),依次进行左右两部分元素比较并合并(使子序列变得有序),最终得到整个序列有序。

5.2代码实现(Java)
public static int[] MergeSort(int[] array) {
        if (array.length < 2) return array;
        int mid = array.length / 2;
        int[] left = Arrays.copyOfRange(array, 0, mid);
        int[] right = Arrays.copyOfRange(array, mid, array.length);
        return merge(MergeSort(left), MergeSort(right));
    }
    public static int[] merge(int[] left, int[] right) {
        int[] result = new int[left.length + right.length];
        for (int index = 0, i = 0, j = 0; index < result.length; index++) {
            if (i >= left.length)
                result[index] = right[j++];
            else if (j >= right.length)
                result[index] = left[i++];
            else if (left[i] > right[j])
                result[index] = right[j++];
            else
                result[index] = left[i++];
        }
        return result;
    }

6) 快速排序

6.1算法描述

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序

6.2代码实现(Java)
public static void quicksort(int[] a, int start, int end) {
        int i, j;   
        if(start > end) {
            return;
        }
        i = start;
        j = end;
        int jz = a[start];   //先设定序列第一个元素为基准
        while(i < j) {
            while(jz <= a[j] && i < j) { //这里必须先从右向左找小于基准的第一个元素
                j--;                     //否则会报索引越界异常
            }
            while(jz >= a[i] && i < j) {
                i++;
            }
            if(i < j) {     //把小于基准的和大于基准的元素交换
                int tem = a[i];
                a[i] = a[j];
                a[j] = tem;
            }
        }
        a[start]=a[j];  //到这里证明这一趟排完
        a[j]=jz;        //更换基准的位置,使得左边都是比基准小的,右边都是比基准大的
        quicksort(a, start, j-1);   //然后再对这左右两部分进行快速排序(不包含基准)
        quicksort(a, j+1, end);
    }

7)堆排序

7.1算法描述

堆排序就是把未排序列看成是一个二叉堆,每次通过堆顶和最后一个元素进行交换,再进行堆顶的调整操作,重复操作来达到有序数列

7.2代码实现(Java)
 public static void heapsort(int[] a) {
        //构建二叉堆(大顶堆)
        for(int i = (a.length - 2) / 2 ; i >= 0 ; i--) {
            downAdjust(a, i, a.length);
        }
        //堆排序
        for(int i = a.length - 1; i >= 1; i--) {
            //把堆顶的元素与最后一个元素交换
            int tem = a[i];
            a[i] = a[0];
            a[0] = tem;
            //为了保持有序,交换后还行进行下沉操作
            downAdjust(a, 0, i);
        }
    }
    //调整操作(父节点下沉,子节点上浮)
    public static void downAdjust(int[] a, int parent, int n) {
        //临时保存要下沉的元素
        int tem = a[parent];
        //定位左孩子节点的位置
        int child = 2 * parent + 1;
        //开始下沉
        while(child < n) {
            //如果右孩子大于左孩子,则定位到右孩子
            if(child + 1 < n && a[child + 1] > a[child]) {
                child = child + 1;
            }
            //如果孩子节点小于或等于父节点,则下沉结束
            if(a[child] <= tem) {
                break;
            }
            //父节点进行下沉
            a[parent] = a[child];
            parent = child;
            child = 2 * parent + 1;
        }
        a[parent] = tem;
    }

8) 计数排序

8.1算法描述

计数排序的核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

计数排序(Counting sort)是一种稳定的排序算法。计数排序使用一个额外的数组C,其中第i个元素是待排序数组A中值等于i的元素的个数。然后根据数组C来将A中的元素排到正确的位置。它只能对整数进行排序。

8.2代码实现(Java)
public static void countingsort(int[] a) {
        if(a == null || a.length < 2) 
            return;
        int max = a[0];
        int min = a[0];
        //寻找数组中的最大值和最小值
        for(int i = 0 ; i < a.length ; i++) {
            if(max < a[i]) {
                max = a[i];
            }
            if(min > a[i]) {
                min = a[i];
            }
        }
        //创建一个临时的数组,为了防止创建多余的空间,
        //需要定义一个区间的大小,这个区间大小由最大和最小值的差值决定
        int[] tem = new int[max - min + 1];
        //统计元素出现的次数,元素的值当做tem数组的下标
        for(int i = 0 ; i < a.length; i++) {
            tem[a[i] - min]++;
        }
        int index = 0;
        //把统计好的数据还原到原来数组中
        for(int i = 0 ; i < max - min + 1; i++) {
            for(int j = tem[i] ; j > 0; j--) {
                a[index++] = i + min;
            }
        }
    }

9)桶排序

9.1算法描述

桶排序是计数排序的一个更高级版本,桶排序和计数排序的不同就是桶排序把最大值和最小值之间的这些数据进行瓜分,放到不同的桶中,对每个桶进行排序(这里可以使用其他排序,比如快速排序、插入排序等看情况而定),最终再把每个桶里面的序列拼接起来使整个序列有序

9.2代码实现(Java)
 public static void bucketsort(int[] a) {
        if(a == null || a.length < 2) 
            return;
        int max = a[0];
        int min = a[0];
        //寻找数组中的最大值和最小值
        for(int i = 0 ; i < a.length ; i++) {
            if(max < a[i]) {
                max = a[i];
            }
            if(min > a[i]) {
                min = a[i];
            }
        }
        //计算差值
        int d = max - min;
        //设置桶存放元素的范围,这里5就是0~4,5~9,10~14...
        int bucketSize = 5;
        //桶的数量
        int bucketNum = d/5 + 1;
        //创建桶
        ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>();
        //初始化桶
        for(int i = 0 ; i < bucketNum; i++) {
            bucketList.add(new ArrayList<Integer>());
        }
        //遍历原数组,将每个元素放入桶中
        for(int i = 0 ; i < a.length ; i++) {
            //计算应该存放在哪个桶
            Integer I = (a[i] - min) / bucketSize;
            bucketList.get(I).add(a[i] - min);
        }
        //对每个桶进行排序,这里使用插入排序
        for(int i = 0 ; i < bucketNum ; i++) {
            insertionsort(bucketList.get(i));
        }
        //把每个排序好的数据进行合并汇总放回原数组
        int index = 0;
        for(int i = 0 ; i < bucketNum; i++) {
            for(Integer j : bucketList.get(i)) {
                a[index++] = j + min;
            }
        }
    }
    public static void insertionsort(ArrayList<Integer> a) {
        for(int i = 1 ; i < a.size(); i++ ) {
            int insert = a.get(i);  //取出要插入的元素
            int ageIndex = i-1;     
            for(; ageIndex >= 0 && a.get(ageIndex) > insert ; ageIndex--) {//这里需把ageIndex >= 0写在前面,不然会报错
                a.set(ageIndex + 1, a.get(ageIndex));   //把大于或小于insert的元素向后移
            }
            a.set(ageIndex+1, insert);  //插入ageIndex+1个位置上,因为上面的循环中最后多ageIndex多减了一次
        }
    }
}

10) 基数排序

10.1算法描述

基数排序是把元素先按个位数(低位)放进十个桶里(0-9),把具有相同位数的放在同一个桶里,按顺序0~9取出;接着按十位数又放进十个桶里,按顺序取出;接着按百位数来.....以此类推。最终达到整个序列有序。反之,也可以从高位进行

10.2代码实现(Java)
public static void rediosort(int[] a) {
        if(a == null || a.length < 2) 
            return;
        int max = a[0];
        //找出最大值
        for(int i = 1 ; i < a.length; i++) {
            if(max < a[i]) {
                max = a[i];
            }
        }
        //计算最大值是几位数
        int num = 1;
        while(max / 10 > 0) {
            num++;
            max = max / 10;
        }
        //创建10个桶
        ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>();
        //初始化桶
        for(int i = 0 ; i < 10 ; i++) {
            bucketList.add(new ArrayList<Integer>());
        }
        //进行排序,从个位数开始排序
        for(int i = 1 ; i <= num; i++) {
            for(int j = 0 ; j < a.length ; j++) {
                //获取每个数第i个位数的数字
                int I = (a[j] / (int)Math.pow(10, i-1)) % 10;
                //放入对应的桶里
                bucketList.get(I).add(a[j]);
            }   
            //合并放回原数组
            int index = 0;
            for(int j = 0 ; j < 10; j++) {
                for(Integer k : bucketList.get(j)) {
                    a[index++] = k;
                }
                //放回原数组后把桶里数据清空,因为每排完一个位后还需要用到这10个桶
                bucketList.get(j).clear();
            }
        }
    }

二、排序算法横向总结

图片.png
关键词解析

稳定性

   (1) 稳定排序:在序列中如果a在b的前面,并且a==b,经过排序交换后,a还在b的前面,则称为稳定排序。
   (2) 非稳定排序:在序列中如果a在b的前面,并且a==b,经过排序交换后,a有可能不在b前面,则称为非稳定排序。

时间复杂度

   一个算法执行需要消耗的时间。

空间复杂度

   运行完一个程序所需内存的大小。

排序方式

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

推荐阅读更多精彩内容