学习总结(数据结构:排序)

转载请标明出处,谢谢!
https://www.jianshu.com/p/176b0b892591

关联文章
排序 https://www.jianshu.com/p/176b0b892591
栈和队列 https://www.jianshu.com/p/8cb602ef4e21
顺序表、单双链表 https://www.jianshu.com/p/3aeb5998e79e
二叉树 https://www.jianshu.com/p/de829eab944c
图论 https://www.jianshu.com/p/cf03e51a3ca2

image.png

详细的各种排序
https://www.cnblogs.com/ll409546297/p/10956960.html

冒泡排序和选择排序,适合个位数的排序

冒泡排序(Bubble Sort)

一种计算机科学领域的较简单的排序算法。

它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素已经排序完成。

这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

冒泡排序简单的讲就是相邻的两个数字对比,大的数往后推。

private void bubbleSort(int[] array){
        for (int i = 0; i <array.length-1 ; i++) {
            if(array[i]>array[i+1]){
                int temp = array[i];
                array[i] = array[i+1];
                array[i+1] = temp;
            }
        }
        print(array);

    }

输出的结果是:


image.png

第一轮下来 已经把最大的数字9往后推到最后一位。 接下来就要比较1 3 5 2 ->1 3 2 5 然后是1 3 2 所以按照这个思路再最外面加一个for循环就可以将数组排序

 private void bubbleSort(int[] array){
        for (int j = 0; j <array.length - 1 ; j++) {
            for (int i = 0; i <array.length-1 - j ; i++) {
                if(array[i]>array[i+1]){
                    int temp = array[i];
                    array[i] = array[i+1];
                    array[i+1] = temp;
                }
            }
        }
        print(array);

    }

我们看下,这是最基本的冒泡排序写法。那么我们看下他的时间复杂度是多少?冒泡排序的时间复杂度是O(n²)。这是冒泡排序的最差时间复杂度。那么他有没有优化的方案呢?

有! 我们可以假设下 如果一开始的数组数据就是从小到大排序好的呢? 那么真正的有效代码时间复杂度是O(n),因为if语句根本就走不进去。 我们看下代码。

@Test
    public void test() {
        int[] array = new int[]{1, 2, 3, 4, 5};
        bubbleSort(array);
    }

    private void bubbleSort(int[] array) {
        for (int j = 0; j < array.length - 1; j++) {
            boolean flag = true;
            for (int i = 0; i < array.length - 1 - j; i++) {
                if (array[i] > array[i + 1]) {
                    int temp = array[i];
                    array[i] = array[i + 1];
                    array[i + 1] = temp;
                    flag = false;
                }
            }
            if (flag) {
                break;
            }
        }
        print(array);
    }

选择排序

一种简单直观的排序算法。它的工作原理很容易理解:初始时在序列中找到最小(大)元素,放到序列的起始位置作为已排序序列;然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

注意选择排序与冒泡排序的区别:冒泡排序通过依次交换相邻两个顺序不合法的元素位置,从而将当前最小(大)元素放到合适的位置;而选择排序每遍历一次都记住了当前最小(大)元素的位置,最后仅需一次交换操作即可将其放到合适的位置。

先排第一次,首先将第一个数字固定然后和后面一个数字3对比 如果4>3就将3的角标赋值给index,然后对换数字,以此类推,我们看下结果:

 private void selectionSort() {
        int[] array = new int[]{4, 3, 9, 5, 2};
        int index = 0;
        for (int i = 0 + 1; i < array.length; i++) {
            /*如果后者大于第一个数 index重新赋值*/
            if (array[index] > array[i]) {
                index = i;
            }
            /*替换两个数*/
            int temp = array[index];
            array[index] = array[0];
            array[0] = temp;
        }
        print(array);
    }

按照这样的思路,再最外层加上一个循环进行排序:

 private void selectionSort() {
        int[] array = new int[]{4, 3, 9, 5, 2};
        for (int j = 0; j < array.length - 1; j++) {
            int index = j;
            for (int i = j + 1; i < array.length; i++) {
                /*如果后者大于第一个数 index重新赋值*/
                if (array[index] > array[i]) {
                    index = i;
                }
                /*替换两个数*/
                int temp = array[index];
                array[index] = array[j];
                array[j] = temp;
            }
        }
        print(array);
    }

那么这个算法也有优化方案吗?有!

 private void selectionSort() {
        int[] array = new int[]{4, 3, 9, 5, 2};
        for (int j = 0; j < array.length - 1; j++) {
            int index = j;
            for (int i = j + 1; i < array.length; i++) {
                /*如果后者大于第一个数 index重新赋值*/
                if (array[index] > array[i]) {
                    index = i;
                }
                /*替换两个数*/
                if (index != j) {//如果已经是最小的,就不需要替换
                    int temp = array[index];
                    array[index] = array[j];
                    array[j] = temp;
                }
            }
        }
        print(array);
    }

插入排序

image.png

插入排序类似整理扑克牌,将每一张牌插到其他已经有序的牌中适当的位置。

插入排序由N-1趟排序组成,对于P=1到N-1趟,插入排序保证从位置0到位置P上的元素为已排序状态。

简单的说,就是插入排序总共需要排序N-1趟,从index为1开始,讲该位置上的元素与之前的元素比较,放入合适的位置,这样循环下来之后,即为有序数组。
为了看清原理,先模拟三个数据

 @Test
    public void test() {
        int[] array = new int[]{2, 4, 1};
        insertSort(array);
        print(array);
    }

    //直接插入排序
    public void insertSort(int[] array) {
        int j = 2;
        int target = array[j];
        while (j > 0 && target < array[j - 1]) {
            array[j] = array[j - 1];
            j--;
        }
        array[j] = target;

    }

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

分析: j=2, target = array[j] = 1;
如果1<4 则替换,然后j = 1, 再替换。几轮下来 1就跑到最前面 输出的结果是 1,2,4.

完整代码:

  //直接插入排序
    public void insertSort(int[] array) {
        for (int i = 0; i <array.length ; i++) {
            int j = i;
            int target = array[j];
            while (j > 0 && target < array[j - 1]) {
                array[j] = array[j - 1];
                j--;
            }
            array[j] = target;
        }


    }

希尔排序
希尔排序(Shell's Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。

该方法实质上是一种分组插入方法

比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。

一般的初次取序列的一半为增量,以后每次减半,直到增量为1。

给定实例的shell排序的排序过程

假设待排序文件有10个记录,其关键字分别是:

49,38,65,97,76,13,27,49,55,04。

增量序列的取值依次为:

5,2,1
代码:
改进刚刚的插入排序

 @Test
    public void test() {
        int[] array = new int[]{2, 4, 1, 44, 3, 22, 7, 8, 9, 444};
        //insertSort(array);
        shellSort(array, 5);
        shellSort(array, 2);
        shellSort(array, 1);
        print(array);
    }
    //希尔排序
    public void shellSort(int[] array, int step) {
        for (int k = 0; k < step; k++) {
            for (int i = k + step; i < array.length; i += step) {
                int j = i;
                int target = array[j];
                while (j > step - 1 && target < array[j - step]) {
                    array[j] = array[j - step];
                    j -= step;
                }
                array[j] = target;
            }
        }
    }

堆排序
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

堆排序的过程:
1。从最后一个非叶子节点开始,每三个节点做一次大小比较,最小的做根
如果移动过程中如果子树上的顺序被破坏了,子树上重新调整三个节点的位置
2。取走整个树的根节点,把最后一个叶子做为根节点
3。重复1和2,直到所有节点被取走了

image.png

举个例子说明下:有一个数组 3、6、9、1、2、4、5、7、8
用完全二叉树表示


image.png

根据堆排序过程
先从1开始


image.png

然后从9开始
不用操作。
然后操作6


image.png

操作完6 发现 做左边的三个节点需要调整:


image.png

操作3


image.png

右小角需要调整:


image.png

上图就是调整后的二叉树。我们发现9已经被推到了最上面。接着取出9,将最后一个叶子节点1作为根节点

image.png

然后重复上面的操作。

代码:

 @Test
    public void test(){
        int[] array=new int[]{2,3,4,5,6,7,1,8,9};
        heapSort(array,array.length);
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i]+" ");
        }

    }
    /**
     * 调整堆
     */
    void maxHeapify(int array[],int start,int end){
        //父亲的位置
        int dad=start;
        //儿子的位置
        int son=dad*2+1;
        while(son<=end){//如果子节点下标在可以调整的范围内就一直调整下去
            //如果没有右孩子就不用比,有的话,比较两个儿子,选择最大的出来
            if(son+1 <= end && array[son]<array[son+1]){
                son++;
            }
            //和父节点比大小
            if(array[dad]>array[son]){
                return;
            }else{//父亲比儿子小,就要对整个子树进行调整
                int temp=array[son];
                array[son]=array[dad];
                array[dad]=temp;
                //递归下一层
                dad=son;
                son=dad*2+1;
            }
        }
    }
    void heapSort(int array[],int len){
        //建堆  len/2-1最后一个非叶子节点
        for(int i=len/2-1;i>=0;i--){
            maxHeapify(array,i,len-1);
        }
        //排序,根节点和最后一个节点交换
        //换完以后,取走根,重新建堆
        //len-1 最后一个节点
        for(int i=len-1;i>0;i--){
            int temp=array[0];
            array[0]=array[i];
            array[i]=temp;
            maxHeapify(array,0,i-1);
        }
    }

看下八大排序的应用场景


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

推荐阅读更多精彩内容