排序算法总结


前言

在这里插入图片描述

排序 : 就是将一串记录, 按照其中某个关键字的大小 , 递增或递减的操作
稳定性 : 排完序后, 原来相同的数据,顺序不变 - 这种算法被称为稳定的
内部排序:数据元素全部放在内存中的排序。
外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。

插入排序

思想 : 类似于打扑克 , 第一个数据有序, 后序的数据逐个与前面数据比较,从而插入到合适的位置中去

  /**
     * 直接插入排序-推荐使用
     * 时间复杂度O(N^2)
     *      最好的情况是O(N):对于直接插入排序来说,最好的情况就是数据有序的时候
     *      可以推导出结论:当数据越有序,时间效率越高
     * 空间复杂度O(1)
     * 稳定性:稳定的
     * 一个稳定的排序,可以实现为不稳定的排序,但是一个本身不稳定的排序,是不可以
     * 变成稳定的排序的。
     * @param array
     */
    public  void insertSort(int[] array){
//        思想:假定第一个元素有序,从第二个元素开始回退排序
        for (int i = 1; i < array.length; i++) {
            int tmp=array[i];
            int j = i-1;
            for (; j >=0 ; j--) {
                if (array[j]>array[j+1])array[j+1]=array[j];
//                当array[j]<tmp的时候,说明有序了
                else break;
//                为什么不在else中写 array[j+1]=tmp;因为当j为-1的时候进不来
            }
            //j回退到了小于0的地方
            array[j+1]=tmp;
        }
    }

希尔排序

思想 : 对插入排序进行优化 , 因为插入排序有个越有序效率越高的特性 ,所以希尔排序就是先将数据尽可能的小部分有序,从而达到整体有序

/**
     * 希尔排序,
     * 时间复杂度和增量有关系【n^1.3-n^1.5】
     * 空间复杂度O(1)
     * 稳定性:不稳定
     * @param array
     * @param gap
     */

    //待排序的序列
    private   void shell(int [] array,int gap){
        for (int i =gap; i < array.length; i++) {
            int tmp=array[i];
            int j = i-gap;
            for (;j >0 ; j-=gap) {
                if (array[j]>tmp)array[j+gap]=array[j];
                else break;
            }
            array[j+gap]=tmp;
        }
    }
    public  void shellSort(int[] array){
        int gap=array.length;
        while (gap>1){
            shell(array,gap);
            gap/=2;
        }
        shell(array,1);
    }

选择排序

思想 : 选择排序在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后以此类推,直到所有元素均排序完毕。

 /**
     * 选择排序-不推荐使用-很垃圾的
     * 时间复杂度O(N^2)
     * 稳定性:不稳定
     * @param array
     */
    public void choiceSort(int[] array){
        for (int i = 0; i < array.length; i++) {
            for (int j = i+1; j < array.length; j++) {
                if (array[j]<array[i]){
                    int tmp=array[i];
                    array[i]=array[j];
                    array[j]=tmp;
                }
            }
        }
    }

堆排序

思想: 如果是升序数组的话 , 建立一个大根堆 , 每次从大根堆弹出堆顶元素 与 数组最后一个元素进行交换, 交换完成后,再向下调整.重复这个过程从而达到数组有序的状态

 /**堆排序
     * 时间复杂度O(NlogN)
     * @param array
     */
    public void heatSort(int[] array){
        createHeap(array);
        int end=array.length-1;
        while (end>0){
            int tmp=array[0];
            array[0]=array[end];
            array[end]=tmp;
            shiftDown(array,0,end);
            end--;
        }
    }

    private static void createHeap(int[] array){
        for (int parent = (array.length-1-1)/2; parent >=0 ; parent--) {
            shiftDown(array,parent,array.length);
        }
    }
    private static void shiftDown(int[] array,int parent,int len){
        int child=2*parent+1;
        while (child<len){
            if (child+1<len&&array[child]<array[child+1]){
                child++;//child下标就是左右孩子最大值的下标
            }
            if (array[child]>array[parent]){
                int tmp=array[child];
                array[child]=array[parent];
                array[parent]=tmp;
                parent=child;
                child=2*parent+1;
            }else break;
        }
    }

冒泡排序

思想 :不断比较左右的值,然后将最大的值放到最右边

/**冒泡排序
     * 时间复杂度O(N^2)
     * 空间复杂度)O(1)
     * 稳定性:稳定
     * @param array
     */
    public void bubbleSort(int[] array){
        for (int i = 0; i <array.length ; i++) {
            //i是趟数
            boolean flg=false;
            for (int j = 0; j <array.length-1; j++) {
                if (array[j]>array[j+1]){
                    int tmp=array[j];
                    array[j]=array[j+1];
                    array[j+1]=tmp;
                    flg=true;
                }
            }
            if (flg==false)break;
        }
    }

快速排序

思想: 选择一个数, 将这个数组中 所有比这个基数大的数放到这个数的右边 , 所有比这个基数小的数放到这个数的左面, 然后 从这个数一分为二的数组中, 在重新寻找基数重术上述的过程
写法 : 推荐 - 挖坑法 , hore 法 , 前后指针法
优化: ①递归改成栈 , ②三数取中法(基数选择不在总是左右) ③对于小数列的数据用插入排序进行优化
话不多说 , 线上代码,下面具体解释三种具体写法

/**快速排序
     * 时间复杂度(最好)【每次可以均匀分割待排序序列】:O(Nlogn)
     * 最坏【数据有序,或者逆序的情况O(N^2);
     * 空间复杂度:(logn)
     * 最坏【单分支的一棵树】O(N)
     *    稳定性:不稳定
     * @param array
     */
    public void FquickSort(int[] array){
        //非递归实现
        Stack<Integer> stack=new Stack<>();
        int left=0;
        int right=array.length-1;
        int pivot=partition(array,left,right);
        if (pivot >left+1){
            stack.push(left);
            stack.push(pivot-1);
        }
        if (pivot<right-1){
            stack.push(pivot+1);
            stack.push(right);
        }
        while (!stack.isEmpty()){
            right=stack.pop();
            left= stack.pop();
            pivot=partition(array,left,right);
            if (pivot>left+1){
                stack.push(left);
                stack.push(pivot-1);
            }
            if (pivot<right-1){
                stack.push(pivot+1);
                stack.push(right);
            }
        }
    }
    private void quick(int[] array,int left,int right){
        if (left>=right)return;
//        找基准之前,找到中间大小的值
        int midValIndex=findMidValIndex(array,left,right);
        int tmp=array[left];
        array[left]=array[midValIndex];
        array[midValIndex]=tmp;

        int pivot=   partition(array,left,right);//基准

        quick(array,left,pivot-1);
        quick(array,pivot+1,right);
    }
    private int partition(int[] array,int start,int end){
        int tmp=array[start];
        while (start<end){
            while (start<end && array[end]>=tmp){//=号必须有,防止死循环
                end--;
            }
            //end下标遇到了小于tmp的值,然后将值放到start值
            array[start]=array[end];
            while (array[start]<=tmp&&start<end){
                start++;
            }
            //start下标遇到>tmp的值,将值放到end
            array[end]=array[start];
        }
        array[start]=tmp;
        return start;
    }
  
  

快速排序的递归实现

  public void quickSort(int[] array){
        quick(array,0,array.length-1);
    }
    private void quick(int[] array,int left,int right){
        if (left>=right)return;
//        找基准之前,找到中间大小的值
        int midValIndex=findMidValIndex(array,left,right);
        int tmp=array[left];
        array[left]=array[midValIndex];
        array[midValIndex]=tmp;

        int pivot=   partition(array,left,right);//基准

        quick(array,left,pivot-1);
        quick(array,pivot+1,right);
    }
    private int findMidValIndex(int[] array,int left,int right){
    //这个是用了三数取中法的具体实现,  若是不用三数取中法, 则可以直接将最左边的数作为基准
        int mid=left+((right-left)>>>1);
        if (array[left]<array[right]){
            if (array[mid]<array[left])return left;
            else if (array[mid]>array[right])return right;
            else return mid;
        }else {
            if (array[mid]>array[left])return left;
            else if (array[mid]<array[right])return right;
            else return mid;
        }

    }
      private int partition(int[] array,int start,int end){
        int tmp=array[start];
        while (start<end){
            while (start<end && array[end]>=tmp){//=号必须有,防止死循环
                end--;
            }
            //end下标遇到了小于tmp的值,然后将值放到start值
            array[start]=array[end];
            while (array[start]<=tmp&&start<end){
                start++;
            }
            //start下标遇到>tmp的值,将值放到end
            array[end]=array[start];
        }
        array[start]=tmp;
        return start;
    }

归并排序

思想 : 将数组不断二分, 直到分无可分. 然后每次将单个子序列安排到数组中使其有序,(有点类似于并查集,只不过并查集不是负责这方面的)

**归并排序
     * 时间复杂度ON*logN)
     * 空间复杂度ON* 稳定性:稳定
     * 如果:array[s1]<=array[s2]不取等号那么就是不稳定的
     * 学过的排序只有3个是稳定的:
     * 冒泡 插入 归并
     * @param array
     */
    public void FmergeSort(int[] array){
//        归并排序的非递归方式
        int gap=1;//每组的数据个数
        while (gap<array.length){
            //每次遍历需要确定归并的区间
            for (int i = 0; i < array.length; i+=gap*2) {
                int left=i;
                int mid=left+gap-1;
                if(mid>=array.length-1){
                    //防治越界
                    mid=array.length-1;
                }
                int right=mid+gap;
                if (right>=array.length){
                    //防治越界
                    right=array.length-1;
                }
                merge(array,left,mid,right);
            }
            gap*=2;
        }
    }
    public void mergeSort(int[] array){
        mergeSortInternal(array,0,array.length-1);
    }
    private void mergeSortInternal(int[] array,int low,int high){
        if (low>=high)return;

        int mid=low+((high-low)>>>1);

        mergeSortInternal(array,low,mid);

        mergeSortInternal(array,mid+1,high);

        merge(array,low,mid,high);//合并
    }
    private void merge(int[] array,int low,int mid,int high){
        int[] tmp=new int[high-low+1];
        int k=0;

        int s1=low;
        int e1=mid;
        int s2=mid+1;
        int e2=high;

        while (s1<=e1 && s2<=e2) {
            if (array[s1] <= array[s2]) {
                tmp[k++] = array[s1++];
            } else {
                tmp[k++] = array[s2++];
            }
        }
        while (s1<=e1){
            tmp[k++]=array[s1++];
        }
        while (s2<=e2){
            tmp[k++]=array[s2++];
        }
        //拷贝tmp数组的元素,放入原来的数组array当中
        for (int i = 0; i < k; i++) {
            array[i+low]=tmp[i];
        }

    }

计数排序

思想: 利用于哈希的思想 , 将数据直接定值在 ,数组中 , 然后从数组中依次取出即为有序

优点 : 快, 适用于范围集中的数据

时间复杂度 : O(N)

空间复杂度 :(最大值减最小值的范围)

稳定性 : 不稳定


    /**
     * 计数排序
     * 时间复杂度O(N)
     * 空间复杂度O(M)M:代表当前的数据的范围
     * 稳定性:当前不稳定,但是能改的稳定
     * @param array
     */

    public void countingSort(int[] array){
        int maxVal=array[0];
        int minVal=array[0];
        for (int i = 1; i <array.length ; i++) {
            if (array[i]<minVal){
                minVal=array[i];
            }
            if (array[i]>maxVal){
                maxVal=array[i];
            }
        }
        //已经找到最大值和最小值
        int[] count=new int[maxVal-minVal+1];
        for (int i = 0; i < array.length; i++) {
            int index=array[i];
            //不是i存放因为你的数组长度是最大值减最小值创建的
            count[index-minVal]++;
        }
        //将计数数组中array数组每个数字出现的次数统计好了
        int indexArray=0;
        for (int i = 0; i < count.length; i++) {

            while (count[i]>0){
//                因为不一定是i出现俩次
                array[indexArray]=i+minVal;
                count[i]--;//拷贝依次后数据就少一个
                indexArray++;//原数组往后走
            }
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值