排序(Java实现)

排序分类.png

性能比较

对比图.png

O(nlogn)效率优于O(n^2)
简单算法:冒泡、选择、直接插入
改进算法:希尔、堆、快速、归并
不稳定的排序----快些选队(快速排序、希尔排序、选择排序、堆排序)


冒泡排序

    public static void BubbleSort(int[] num) {
        int size  =num.length;
        boolean flag = false;
        for(int i = 0; i< size-1; i++) {
            for(int j = 0; j< size-1-i; j++) {
                if (num[j] > num[j+1]) {
                    int temp = num[j];
                    num[j] = num[j+1];
                    num[j+1] = temp;
                    flag = true;
                }
            }
            if (flag == false) {
                break;          //遍历一遍数组后发现无元素交换时说明此时数组已有序
            }
        }
    }

快速排序(冒泡排序的升级,同属于交换排序类)

通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
适用于数据量较大的情况

原始算法

    public static void quickSort(int[] num,int low, int high) {
        if (low < high) {
            int middle = getMiddle(num, low, high);     //将数组一分为二
            quickSort(num, low, middle-1);      //对低字段进行递归排序
            quickSort(num, middle+1, high);     //对高字段进行递归排序
        }
    }
    
    public static int getMiddle(int[] num, int low, int high) {
        int temp = num[low];        //优化前:选取第一个字段作为基准值
        while(low < high) {
            while(low < high && num[high] > temp) {     //从后往前找到一个比基准值小的数
                high--;
            }
            swap(num, low, high);
            while(low <high && num[low] < temp) {       //从前往后找一个比基准值大的数
                low++;
            }
            swap(num, low, high);
        }
        return low;     //返回中轴所在位置
    }
    
    
    public static void swap(int[] num, int low, int high) {
        int temp = num[low];
        num[low] = num[high];
        num[high] = temp;
    }

优化算法

采用三数取中方式选取基准值
采用替换方式而不是交换方式进行操作

    public static int getMiddle(int[] num, int low, int high) {
        //快速排序优化:三数取中方式选取基准值
        int m = low + (high - low) /2;      //获取数组中间值下标
        if (num[low] > num[high]) {
            swap(num, low, high);       //保证左端小于右端
        }
        if (num[m] > num[high]) {
            swap(num, m, high);     //保证中间小于右端
        }
        if (num[m] > num[low]) {
            swap(num, m, low);          //保证左端小于中间
        }
        int temp = num[low];            //优化后:选取数组第一个数,中间的数以及最后一个数中的中间数最为基准值
        while(low < high) {             //循环结束后low和high指向同一元素,该元素的位置就是基准元素的位置
            while(low < high && num[high] > temp) {     //从后往前找到一个比基准值小的数
                high--;
            }
            num[low] = num[high];           //采用替换而不是交换方式进行操作
            while(low <high && num[low] < temp) {       //从前往后找一个比基准值大的数
                low++;
            }
            num[high] = num[low];
        }
        num[low] = temp;        //将保存的基准值替换到相应位置
        return low;     //返回中轴所在位置
    }

选择排序

思想:在待排序的数中,选出一个最小的数与第一个位置的数交换,然后在剩下的数中找到第二小的数与第二个位置的数交换,如此反复。

    public static void selectSort(int[] num) {
        for(int i = 0; i < num.length; i++) {
            int min = i;
            for(int j = i+1; j < num.length; j++){
                if (num[j] < num[min]) {
                    min = j;
                }
            }
            if (i != min) {
                swap(num,i,min);                
            }
        }
    }

优化思路:每次遍历都找出一个最大值以及最小值


堆排序(选择排序的升级)

思路:构造一个最大堆,将根结点的值与最后一个叶节点交换,然后排除该叶节点后再次构造最大堆。

        for(int i=0; i<num.length-1; i++) {
            //循环建立最大堆
            buildMaxHeap(num, num.length-1-i);
            //交换根节点和最大堆最后一个叶节点
            swap(num, 0, num.length-1-i);
        }

    public static void buildMaxHeap(int[] num, int lastIndex) {
        //从lastIndex节点的父节点开始循环
        for(int i = (lastIndex-1) /2; i>=0; i--) {
            //保存正在判断的节点
            int temp = i;
            //当前节点的子节点存在时
            while( temp*2+1 <= lastIndex) {
                //当前节点的左节点
                int biggerIndex = 2*temp+1;
                if (biggerIndex < lastIndex) {
                    //biggerIndex指向当前节点左右孩子节点中较大值的索引
                    if (num[biggerIndex] < num[biggerIndex+1]) {
                        biggerIndex++;
                    }
                }
                //当前节点小于子女节点中较大值时
                if (num[temp] < num[biggerIndex]) {
                    //交换位置
                    swap(num,temp,biggerIndex);
                    //开启while的下一次循环,保证temp节点的值大于左右孩子节点的值
                    temp = biggerIndex;
                }else {
                    break;
                }
            }
        }
    }

直接插入排序

思路:将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数加1的有序表。

    public static void insertSort(int[] num) {
        int i,j;
        for( i = 0; i< num.length; i++) {
            int temp = num[i];
            for( j = i; j > 0 && temp < num[j-1]; j-- ) {
                //temp小于前面的值,则前面的数右移
                num[j] = num[j-1];
            }
            num[j] = temp;
        }
    }

希尔排序(直接插入排序升级版)

思路:将待排序的数组按一定步长进行分组,并在每个分组中应用直接插入排序算法,然后缩短步长再次进行分组,直到步长为0为止。

    public static void shellSort(int[] num) {
        //设置步长
        int step = num.length/2;
        while(step >= 1) {
            for(int i = step; i< num.length; i+=step) {
                int temp = num[i];
                int j;
                //直接插入排序算法
                for(j=i; j>0 && num[j-step] > temp; j-=step) {
                    num[j] = num[j-step];
                }
                num[j]  = temp;
            }
            //缩小步长直到step为0
            step = step/2;
        }
    }

归并排序

思路:假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2(向上取整)个长度为2或1的有序序列,再两两归并......,如此反复,直至得到一个长度为n的有序序列为止,这种排序称为2路归并排序。

    /**
     * @param num 待排序数组
     * @param n 排序元素个数
     */
    public static void mergerSort(int[] num, int n) {
        sort(num, 0, n-1);
    }
    
    public static void sort(int[] num, int left, int right) {
        if (left < right) {
            int middle = (right + left)/2;
            sort(num, left, middle);
            sort(num, middle+1, right);
            merge(num, left, middle, right);
        }
    }
    
    public static void merge(int[] num, int left, int mid, int right) {
        //计算数组大小
        int n = right-left+1;
        //创建临时数组保存数据
        int[] tempArr = new int[n];
        //临时数组下标
        int t = 0;
        int r = mid+1;
        int l = left;
        while(l <= mid && r <= right) {
            if (num[l] < num[r]) {
                tempArr[t++] = num[l++];
            }else {
                tempArr[t++] = num[r++];
            }
        }
        //剩余元素加入临时数组
        while(l <= mid) {
            tempArr[t++] = num[l++];
        }
        while(r <= right) {
            tempArr[t++] = num[r++];
        }
        
        //将临时数组的值复制到原数组中
        for(int i = 0; i < t ; i++) {
            num[left+i] = tempArr[i];
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 本文总结十大经典排序算法及变形,并提供Java实现。参考文章:十大经典排序算法总结(Java语言实现)快速排序算法...
    Steven1997阅读 17,598评论 3 186
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,170评论 0 52
  • 概述排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的...
    Luc_阅读 2,259评论 0 35
  • 认识大冰该是缘之所致,才有所遇。 我是那种规规矩矩,平时所读亦为规规矩矩的人。可想而知,初始大冰,于我很是一种震惊...
    霜小清阅读 575评论 0 0
  • 有一种孤独,是阴阳相隔。有一种孤独,是咫尺天涯。 有些人,爱得很深,一方却早早离去。有些人,明明在一个屋檐下,心却...
    思源_沈先生阅读 415评论 1 3