一文通关八大排序算法

一、选择排序

思路:从数组中选择最小元素,将它与数组的第一个元素交换位置。再从剩下的元素中选择出最小的元素,将它与第二个元素交换位置。不断进行这样的操作,直到遍历完整个数组。

时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性(两个相等元素的绝对位置交换前后是否发生改变):不稳定

public static void SelectSort(int[] arr){

        int minIndex;

        //依此遍历找剩下元素的最小
        for (int i=0;i<arr.length;i++){
            minIndex=i;
            for (int j=i+1;j<arr.length;j++){
                if (arr[j]<arr[minIndex]){
                    minIndex=j;
                }
            }

          
            //交换元素
            if (minIndex!=i){
                int tmp=arr[minIndex];
                arr[minIndex]=arr[i];
                arr[i]=tmp;

            }

        }

    }

二、冒泡排序

思路:从左到右不断交换相邻逆序的元素,在一轮的循环之后,可以让未排序的最大元素上浮到右侧。在一轮循环中,如果没有发生交换,那么说明数组已经是有序的,此时可以直接退出。
时间复杂度:O(n^2)
空间复杂度:O(1)
稳定性:稳定

    public static void bubbleSort(int[] arr) {

        //该轮是否排序了
        boolean isSort = true;
        
        //如果上一轮没有发生交换,直接跳出循环
        for (int i = 0; i < arr.length && isSort; i++) {
            isSort = false;
            for (int j = 1; j < arr.length - i; j++) {
                if (arr[j - 1] > arr[j]) {
                    isSort = true;
                    //交换元素
                    int tmp = arr[j - 1];
                    arr[j - 1] = arr[j];
                    arr[j] = tmp;
                }
            }

        }

    }

三、插入排序

思路:每次都将当前元素插入到左侧已经排序的数组中,使得插入之后左侧数组依然有序。
时间复杂度:O(N)~O(N^2)和初始顺序有关
空间复杂度:O(1)
稳定性:稳定

    public static void insertSort(int[] arr) {


        for (int i = 1; i < arr.length; i++) {
            
            //寻找适合的位置插入
            if (arr[i] < arr[i - 1]) {
                int tmp = arr[i];
                int j;
                for (j = i - 1; j >= 0 && arr[j] > tmp; j--) {
                    arr[j + 1] = arr[j];
                }

                arr[j + 1] = tmp;
            }

        }

    }

四、希尔排序

思路:插入排序只能交换相邻的元素,对于大规模的数组,其插入排序很慢,希尔排序使用插入排序对时间间隔h的序列进行排序,通过不断减小h,最后令h=1。
时间复杂度:依赖于增量序列,优于插入排序
空间复杂度:O(1)
稳定性:不稳定

    public static void shellSort(int[] arr) {

        //初始间隔d,一直到1
        for (int d = arr.length / 2; d > 0; d = d / 2) {
            //从d开始
            for (int i = d; i < arr.length; i++) {
                
                for (int j = i - d; j >= 0; j -= d) {
                    if (arr[j + d] < arr[j]) {
                        int tmp = arr[j + d];
                        arr[j + d] = arr[j];
                        arr[j] = tmp;
                    }

                }

            }

        }

    }

五、快速排序

思路:快速排序通过一个切分元素将数组分为两个子数组,左子数组小于等于切分元素,右子数组大于等于切分元素,将这两个子数组排序也就将整个数组排序了。

快速排序最好的情况下是每次都正好将数组对半分,这样递归调用次数才是最少的。这种情况下比较次数为 CN=2CN/2+N,复杂度为 O(NlogN)。
最坏的情况下,第一次从最小的元素切分,第二次从第二小的元素切分,如此这般。因此最坏的情况下需要比较 N2/2。为了防止数组最开始就是有序的,在进行快速排序时需要随机打乱数组。
时间复杂度:O(NlogN)
空间复杂度:O(logN)
稳定性:不稳定

    public static void quickSort(int[] arr, int start, int end) {

        if (start < end) {
            //确定切分元素
            int stard = arr[start];
            int low = start, high = end;

            while (low < high) {

                while (low < high && arr[high] >= stard) {
                    high--;
                }

                arr[low] = arr[high];

                while (low < high && arr[low] < stard) {
                    low++;
                }

                arr[high] = arr[low];

            }

            arr[low] = stard;

            //处理前面的
            quickSort(arr, start, low - 1);

            //后面
            quickSort(arr, low + 1, end);

        }

    }

六、归并排序

思路:归并排序的思想是将数组分成两部分,分别进行排序,然后归并起来。
时间复杂度:O(NlogN)
空间复杂度:O(N)
稳定性:稳定

    public static void mergeSort(int[] arr,int low,int high){

        int middle=(low+high)/2;
        if (low<high){

            //处理左边
            mergeSort(arr,low,middle);

            //处理右边
            mergeSort(arr,low+1,high);

            //排序
            merge(arr,low,middle,high);

        }


    }

    /**
     *@description
     * 将两个已经排序的数组并成一个
     *@author lugton
     *@date 2020/3/23
     *@return
     */
    public static void merge(int[] arr,int low,int middle,int high){

        int i=low,j=middle+1;
        int[] tmp=new int[arr.length];

        //复制辅助数组
        for (int k=low;k<=high;k++){
            tmp[k]=arr[k];
        }

        for (int k=low;k<=high;k++){

            //前面数组已经排完
            if (i>middle){
                arr[k]=tmp[j++];
            }else if (j>high){
                //后面数组已经排完
                arr[k]=tmp[i++];
            }else if (tmp[i]<=tmp[j]){
                arr[k]=tmp[i++];
            }else {
                arr[k]=tmp[j++];
            }

        }

    }

七、堆排序

思路:构建大顶堆,然后依此将堆顶放入数组,从后往前放。
时间复杂度:O(NlogN)
空间复杂度:O(1)
稳定性:不稳定

    public static void heapSort(int[] arr){

        //开始位置是最后一个非叶子节点,也就是最后一个节点的父节点
        int start=(arr.length-1)/2-1;

        //调整为大顶堆
        for (int i=start;i>=0;i--){
            maxHeap(arr,arr.length,i);
        }

        //把数组第0个和堆中最后一个交换位置
        for (int i=arr.length-1;i>0;i--){

            int tmp=arr[0];
            arr[0]=arr[i];
            arr[i]=tmp;

            maxHeap(arr,i,0);

        }



    }


    public static void maxHeap(int[] arr,int size,int index){

        //左子节点
        int left=2*index+1;
        int right=2*index+2;
        int max=index;

        //找出最大的节点

        if (left<size && arr[left]>arr[max]){
            max=left;
        }
        if (right<size && arr[right] > arr[max]){
            max=right;
        }

        //交换位置
        if (max!=index){

            int tmp=arr[index];
            arr[index]=arr[max];
            arr[max]=tmp;

            //交换位置以后,可能会破坏之前的堆,所以要重新进行排序
            maxHeap(arr,size,max);

        }

    }

也可以利用优先队列构建小顶堆

 public static void heap(int[] arr){

        //小顶堆
        PriorityQueue<Integer> priorityQueue=new PriorityQueue<>();

        for (int num:arr
             ) {
            priorityQueue.add(num);
        }

        for (int i=0;i<arr.length;i++){
            arr[i]=priorityQueue.poll();
        }


    }

八、基数排序

思路:数字都是由0-9组成的,所以可以用10个桶来进行排序。先依据序列的个为分配到指定的桶中,然后再对十位...百位...做相应排序。
时间复杂度:O(n)
空间复杂度:O(n+k)k为桶的数量
稳定性:稳定

参考自:https://www.cnblogs.com/yadiel-cc/p/11829398.html

    /**
     * @return
     * @description digit 最多有多少位数字
     * @author lugton
     * @date 2020/6/26
     */
    public void randix(int[] arr, int digit) {

        //10个桶
        int radix = 10;

        int begin = 0, end = arr.length - 1;

        int i = 0, j = 0;
        //存放每个桶的数据个数
        int[] count = new int[radix];

        int[] bucket = new int[end - begin + 1];

        //按照从低位到高位的顺序进行排序 也就是从个位开始
        for (int d = 1; d <= digit; d++) {

            //初始化
            for (i = 0; i < radix; i++) {
                count[i] = 0;
            }

            //统计每个桶要装入数据的个数
            for (i = begin; i <= end; i++) {

                j = getDigit(arr[i], d);
                count[j]++;
            }

            //每个桶的右边界索引
            for (i = 1; i < radix; i++) {
                count[i] = count[i] + count[i - 1];
            }

            //数字依次入桶
            for (i = end; i >= begin; i--) {

                j = getDigit(arr[i], d);
                //放入桶的对应位置
                bucket[count[j] - 1] = arr[i];
                //索引对应的也要减一位
                count[j]--;
            }

            //返回按个位排序的数组
            for (i = begin, j = 0; i <= end; i++, j++) {
                arr[i] = bucket[j];
            }

        }


    }

    /**
     * @return
     * @description 获取数字x第d位上的数字
     * @author lugton
     * @date 2020/6/26
     */
    public int getDigit(int x, int d) {

        while (--d > 0) {
            x = x / 10;
        }
        return x % 10;
    }

九、二分查找

    public static void BinarySearch(int[] arr,int target){

        int begin=0;
        int end=arr.length-1;

        int index=-1;

        int mid=(begin+end)/2;

        while (true){

            if (target==arr[mid]){
                index=mid;
                break;
            }else {

                if (target<arr[mid]){
                    end=mid-1;
                }else {
                    begin=mid+1;
                }
            }

            mid=(begin+end)/2;

        }

        System.out.println("index:"+index);

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