算法笔记——左神初级(3)排序稳定性、桶排序

排序的稳定性
即在排序过程中,相同的值,它们的相对位置排序后是否会被打乱

例如:
具有排序稳定性:冒泡排序、插入排序、归并排序
不具有排序稳定性:选择排序、快速排序、堆排序

排序稳定性的价值:可以保留原始排序中的有用信息,例如几个人先按身高排序,然后再按年龄排序,第二次排完序后同年龄的人也是按身高排序的。

工程中的综合排序方法
如果数据很长:
1.首先做判断,如果数据都是int、long、char、float等基础类型,它会采用快排;
因为基础类型相同值无差异,

2.如果类型是自己定义的,如student,则会使用归并排序;
因为相同值得个体可能是不一样的,是有差别的,需要排序稳定性

如果数据很短:(数组长度小于60)
不管什么类型都使用插入排序! 原因在于插排的常数项极低,虽然插排复杂度为O(N^2),但小样本情况下劣势体现不出来。

综上,工程中的综合排序是先用快排和归并分解,当分解的的区间小于60时,则用插排。


上面这个题可以理解为快排的类比。因为快排实际是做一个大小判断,奇偶也是做一个判断,而快排是无法保证排序稳定性的,除非去研究那个《01_stable_sort》论文 hhh

比较器
Java里对自定义类型的数据进行排序时,需要引入比较器
这里重写的方法需要返回值,如果return负数,则第一个数应在前面;如果正数,则第二个应在前面;return的是0,则两个一样大

public static class IdAscendingComparator implements Comparator<Student> {

        @Override
        public int compare(Student o1, Student o2) {
            return o1.id - o2.id;
        }

    }
 
Java里有专门的堆类型PriorityQueue——优先级队列 其实就是堆结构
在创建堆结构时,如果数据是自定义类型,需要添加一个比较器,也即是优先级队列的比较优先级的方法。
基本类型则按顺序。

        PriorityQueue<Student> heap = new PriorityQueue<>(new IdAscendingComparator());

        heap.add(student3);
        heap.add(student2);
        heap.add(student1);

        while (!heap.isEmpty()){
            Student student = heap.poll();
            System.out.println("Name : " + student.name + ", Id : " 
            + student.id + ", Age : " + student.age);
        }
    
    //基本类型
    PriorityQueue<Integer> heap = new PriorityQueue<>();

        heap.add(5);
        heap.add(7);
        heap.add(1);

        while (!heap.isEmpty()){
            System.out.println(heap.poll());
        }
 
桶排序
基础的桶排序算法过程
1.根据待排序集合中最大元素和最小元素的差值范围和映射规则,确定申请的桶个数;比如要对一组正整数排序,就可以申请最大值+1个桶。
2.遍历待排序集合,将每一个元素移动到对应的桶中;
3.更新桶的计数器或者别的相应记录。
4.从大到小或从小到大遍历桶,按计数或其他标志将桶内数据输出。

代码如下:(说实话这个方法感觉有点土啊,额外空间复杂度太大了!!!!)

// only for 0~200 value
public static void bucketSort(int[] arr) {
    //增加算法的完备性和效率
    if (arr == null || arr.length < 2) {
        return;
    }
    int max = Integer.MIN_VALUE; //比较前先设为最小值
    for (int i = 0; i < arr.length; i++) {  //遍历找到最大值
        max = Math.max(max, arr[i]);
    }
    int[] bucket = new int[max + 1]; //因为考虑正整数,所以需要max+1个桶
    
    for (int i = 0; i < arr.length; i++) { // 放数入桶的操作
        bucket[arr[i]]++;
    }

    int i = 0;
    for (int j = 0; j < bucket.length; j++) {  //按序遍历桶即可,桶的计数就是对应数字出现的次数
        
        while (bucket[j]-- > 0) { //此while循环输出的是第j个桶存放的数字个j
            arr[i++] = j;
        }
    }
}
 
例题

桶排序思路:
1.有N个数,则准备N+1个桶;
2.遍历数组得到最小值和最大值;
3.如果最小值与最大值相等,则返回0;
4.最小值放在0号桶,最大值放在N号桶,把最小值到最大值的范围等分为N+1份,一个数属于哪个范围就放进去。
5.中间必存在一个空桶,空桶需要记录是否写入数和写入的最大最小值,其作用是排除相邻两数最大差值来源于同一个桶的可能性;
6.然后再比较所有给空桶之间的相邻两数差值,求得最大值。

public static int maxGap(int[] nums) {
        if (nums == null || nums.length < 2) {
            return 0;
        }
        int len = nums.length;
        int min = Integer.MAX_VALUE;
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < len; i++) {  //遍历  找到最大值和最小值
            min = Math.min(min, nums[i]);
            max = Math.max(max, nums[i]);
        }
        if (min == max) {
            return 0;
        }
        boolean[] hasNum = new boolean[len + 1];  //是否有值
        int[] maxs = new int[len + 1];
        int[] mins = new int[len + 1];
        int bid = 0;
        for (int i = 0; i < len; i++) { // 更新桶的信息
            bid = bucket(nums[i], len, min, max);
            mins[bid] = hasNum[bid] ? Math.min(mins[bid], nums[i]) : nums[i];
            maxs[bid] = hasNum[bid] ? Math.max(maxs[bid], nums[i]) : nums[i];
            hasNum[bid] = true;
        }
        int res = 0;
        int lastMax = maxs[0];
        int i = 1;
        for (; i <= len; i++) { //全局查找最大差值
            if (hasNum[i]) {
                res = Math.max(res, mins[i] - lastMax);
                lastMax = maxs[i];
            }
        }
        return res;
    }

    public static int bucket(long num, long len, long min, long max) {
        return (int) ((num - min) * len / (max - min));
    } //判断这个数该去几号桶
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值