排序


插入排序 :不断的将后面的数据插入到前面的有序数据中。
冒泡排序:通过从0交换相邻数据,保证第一轮n-1次交换后最大数据在倒数第一位;第二轮n-2次交换后,第二大数据在倒数第二位
选择排序:第一轮:从1~n-1中找最小的数据与第0为交换,保证最小数据在第一位;
冒泡和选择排序区别在于:在交换的方式上 冒泡算法,每次比较如果发现较小的元素在后面,就交换两个相邻的元素。 而选择排序算法的改进在于:先并不急于调换位置,先从A[1]开始逐个检查,看哪个数最小就记下该数所在的位置P,等一躺扫描完毕,再把A[P]和A[1]对调,这时A[1]到A[10]中最小的数据就换

快排代码
递归退出的边界条件:left >= right
操作中的边界条件 : left < right


排序算法:
原地排序:原地排序算法,就是特指空间复杂度是 O(1) 的排序算法
稳定的排序算法:经过某种排序算法排序之后,如果两个等值的数据的前后顺序没有改变


如何在 O(n) 的时间复杂度内查找一个无序数组中的第 K 大元素?
利用快排思想,每次用于比较的数据a,假设其最终index为p,那其为该无序数组中的第p+1大的元素,如果
p+1==k,则第k大元素就是a,否则在其中的一侧中。


归并和快排区别?
归并:先分区后处理,代码上就是先递归,后merge
快排:先处理后排序,代码上就是先sort 后递归

作业:
如何用递归代码来实现归并排序:merge(A,p,q) = merge(A,p,r)+merge(A,r,q) 边界r>=q
快速排序代码 再来一次
n个无序数组查找第k大元素

现在你有 10 个接口访问日志文件,每个日志文件大小约 300MB,每个文件里的日志都是按照时间戳从小到大排序的。你希望将这 10 个较小的日志文件,合并为 1 个日志文件,合并之后的日志仍然按照时间戳从小到大排列。如果处理上述排序任务的机器内存只有 1GB,你有什么好的解决思路,能“快速”地将这 10 个日志文件合并吗?

构建10个文件流,每次读取一条,比较获取最小的一条,放入新的空间,然后读取下一条


线性排序
本质上不再进行原值之间的比较,而是通过分类等巧妙的方案实现数据的排序,实例如:
桶、基数、计数排序

桶排序:比较适合用在外部排序中。所谓的外部排序就是数据存储在外部磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中。

订单金额的桶排序
1)扫描订单金额,1-10万
2)分成100桶,第一个范围 1-1000,第二个1001-2000...
3)桶内快排
有序将桶合并即可。

时间复杂度的计算:
n个订单,m个桶,每个桶数据n/m 每个桶内时间复杂度 n/mlogn/m
整体复杂度 m*n/mlogn/m = nlogn/m~~ n

上面如果某个桶数据特别多,再用桶的思想处理。
桶排序比较适合用在外部排序中。所谓的外部排序就是数据存储在外部磁盘中,数据量比较大,内存有限,无法将数据全部加载到内存中。

计数排序:
需要排序的n个数据,所处的范围不大,如最大值为k,我们把数据划分成k个桶。
如高考分数排名:50万考生,分数范围:0-900
实例,分数: 2 1 0 2 1 4
第一个数组 index:分数 value:分数个数 0-1 1-2 2-2 3-0 4-1
第二个数组 index:分数 value:个数的累加 0-1 1-3 2-5 3-5 4-6
第三个数组开始存放学生,index:index value:学生分数
注意:处理第二个数组获取第三个数组时,一定是先处理value相同的部分,上面的实例即为2-5, 其含义是 2放到第5个位置,即index为4,且同时2-5中对5减去1 2-4
4-2  -》 0-1 1-3 2-4 3-5 4-6
2-1,4-2  -》 0-1 1-2 2-4 3-5 4-6
0-0,2-1,4-2  -》 0-0 1-2 2-4 3-5 4-6
0-0,2-1,3-2,4-2  -》 0-0 1-2 2-3 3-5 4-6
0-0,1-1,2-1,3-2,4-2  -》 0-0 1-1 2-3 3-5 4-6
0-0,1-1,2-1,3-2,4-2,5-4  -》 0-0 1-1 2-3 3-5 4-5 
结束。
计数排序适用场景:
数据范围不大的场景,如果数据范围k比n大很多,不适合使用计数排序
针对非整数或负整数,需要转化为正数

基数排序:
多次使用稳定排序算法 。
稳定排序: 即 1 2 3 2,即排序前后两个2的位置没有发生变化
手机号排序:
先按照最后一位来排序手机号码,然后,再按照倒数第二位重新排序,以此类推,最后按照第一位重新排序。经过 11 次排序之后,手机号码就都有序了。
例如:桶排序即可

适用场景:
1) 可以分割成位来进行比较
2) 位之间有递进关系,如a 数据的高位比 b 数据大,那剩下的低位就不用比较了
3) 每位数据的范围不可太大,否则无法适用线性排序


问题:
根据年龄给100万用户排序:
1 150岁
计数排序即可,150个桶

对 D,a,F,B,c,A,z 这个字符串进行排序,要求将其中所有小写字母都排在大写字母的前面,但小写字母内部和大写字母内部不要求有序
计数排序:先统计length,大写字母个数,大写字母个数为大写字母开始村内方的位置,别的放到前面。
巧妙的逻辑:用两个指针a、b:a指针从头开始往后遍历,遇到大写字母就停下,b从后往前遍历,遇到小写字母就停下,交换a、b指针对应的元素;重复如上过程,直到a、b指针相交。


高性能排序选择:
线性排序需要特定要求,排除
快排和归并排序,归并耗内存,一般使用快排
快排O(n2)怎么避免,选择合适的分区点?
1) 三数取中发
2) 随机法

java中的sort算法:
归并排序: 分区 + 排序 + 归并  --已弃用,最坏O(n2)
时间轴排序: --推荐
合理分区 + 反转倒叙 +  合并

public class Sort {

    /**
     * desc:  选择排序:时间复杂度O(n2)
     */
    public static  void choseSort(int[] array){
        int length = array.length;
        int temp = 0;
        for(int i=0; i<length; i++){
            for(int j=i+1;j<length;j++){
                if(array[j]<array[i]){
                    temp = array[i];
                    array[i] =array[j];
                    array[j]=temp;
                }
            }
        }
    }

    /**
     * desc:  冒泡排序:时间复杂度O(n2)
     */
    public static  void puppleSort(int[] array){

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

    }

    /**
     * desc:  插入排序:时间复杂度O(n2)
     */
    public static  void insertSort(int[] array){
        int length = array.length;
        for(int i=1; i<length; i++){
            int temp = array[i];
            int index = i;
            for(int j=i-1;j>=0;j--){
                if(temp<array[j]){
                    array[j+1]=array[j];
                    index = j;
                }else{
                    break;
                }
            }
            array[index] = temp;
        }
    }

    /**
     * desc:  快排:时间复杂度O(nlogn)
     */
    public static  void quickSort(int[] arr, int left, int right){
        if(left >= right){
            return;
        }
        int l = left;
        int r = right;
        int tmp = arr[left];

        while( l < r){
            while(arr[r] >= tmp && l < r){
                r --;
            }
            if(l<r){
                arr[l] = arr[r];
                l++;
            }

            while(arr[l] < tmp && l < r){
                l ++;
            }
            if(l < r){
                arr[r] = arr[l];
                r--;
            }
        }

        arr[l] = tmp;
        quickSort(arr,left,l-1);
        quickSort(arr,l+1,right);
    }


    /**
     * desc:  借鉴快排的思想,从n个无序数组中查找第n大的数
     */
    public static  int findN(int[] arr, int  k, int left, int  right){
        if(k > arr.length){
            return -1;
        }

        int l = left;
        int r = right;
        int tmp = arr[left];
        while(l < r){
            while(l < r && arr[r] > tmp){
                r --;
            }
            if(l < r){
                arr[l] = arr[r];
                l++;
            }
            while(l<r && arr[l]<tmp){
                l++;
            }
            if(l<r){
                arr[r] = arr[l];
                r--;
            }
        }
        arr[l] = tmp;
        if(l+1 == k){
            return arr[l];
        }else if(l+1 < k ){
            return findN(arr,k,l+1,right);
        }else{
            return findN(arr,k,left,l-1);
        }
    }

    /**
     * desc:  归并排序,注意先归后并
     */
    public static  void mergeSort(int[] arr, int left, int right){
        if(left >= right){
            return ;
        }
        // 避免直接right导致的溢出问题
        int middle = left + ((right - left) >> 1);
        mergeSort(arr, left, middle);
        mergeSort(arr, middle+1, right);
        merge(arr, left, middle, right);
    }

    /**
     * desc: 有序数组的合并
     */
    public static  void merge(int[] arr, int left, int middle, int right){
        int[] temp = new int[arr.length];
        int k =left,l = left,m =middle+1;
        while(l<=middle && m <=right){
            if(arr[l] >= arr [m]){
                temp[k++]=arr[m++];
            }else{
                temp[k++]=arr[l++];
            }
        }
        while(l<=middle){
            temp[k++]=arr[l++];
        }
        while(m <=right){
            temp[k++]=arr[m++];
        }

        for(int i=left; i<=right; i++){
            arr[i]=temp[i];
        }
    }

    /**
     * desc: 遍历数组
     */
    public static  void ergodicArray(int[] array){
        for(int i=0; i<array.length; i++){
            System.out.print(array[i]+",");
        }
        System.out.println();
    }

    /**
     * desc: 计数排序,如通过统计值出现的次数,定位其所在的位置
     * 适用场景:50万考生,分数范围0-800的排序
     * bound:最大值
     */
    public static void countSort(int[]  arr, int bound){
        // index为传入arr的值,value为arr某值出现的次数
        int[] valueTime =  new int[bound];
        int length = arr.length;
        for(int i=0;i<length;i++){
            valueTime[arr[i]] = valueTime[arr[i]] +1;
        }
        // index为传入arr的值,value为arr某值及比起小的值出现的次数的累加,该累加值决定实际值所在的下标
        int[] vlueAccum = new int[bound];
        vlueAccum[0]=valueTime[0];
        for(int i=1 ;  i<bound; i++){
            vlueAccum[i] = vlueAccum[i-1] + valueTime[i];
        }
        // 临时数组
        int[] temp = new int[length];
        for(int i = 0;i<length; i++){
            temp[vlueAccum[arr[i]]-1] = arr[i];
            vlueAccum[arr[i]] = vlueAccum[arr[i]]-1;
        }
        for(int i=0; i<length; i++){
            arr[i] = temp[i];
        }
    }

    public static void main(String[] args) {
        Random rm = new Random();
        rm.nextInt(9);
        int[] arr = new int[50];
        for(int i=0; i<50; i++){
            arr[i]=rm.nextInt(9);
        }
        ergodicArray(arr);
        countSort(arr,9);
        ergodicArray(arr);

        char c = 'A';
//
//        int[] array = {11, 9, 14, 13, 8, 8, 19};
        System.out.println(findN(array,2,0,array.length-1));
//        ergodicArray(array);
//        mergeSort(array,0, array.length-1);
        insertSort(array);
        insertSort(array);
//        ergodicArray(array);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值