左神算法初级班三 笔记

排序的稳定性:在排序过程中,如果任意两个元素的用于排序的基准值相同,排完序之后,如果他们的相对顺序不变,则称排序稳定,否则不稳定。

实际业务过程中我们有时候可能希望保留原始的一些信息(和相对次序有关)

不能做到稳定性的排序算法:堆排序、快速排序、二分插入排序

可以做到稳定性的排序算法:冒泡排序、朴素插入排序、归并排序

Java中的sort具有稳定性,当针对类型是基础数值类型如int、double、float、long时,规模很大的情况下采用快排的方式,因为对于基础数值类型来说,相同的值就是相同的,不含有任何附加信息;如果是复杂类型,使用比较器定义大小关系的时候,大规模情况下使用归并排序保证稳定性。当任务的样本量小于60时,采用插入排序,是因为尽管插入排序的复杂度有O(n^2),但是样本量很少的情况下,相比其他算法而言,插入排序的常数项要低得多。

 

Java的比较器使用(主要用于一些自定义复杂类型的比较):

需要补充的是,Java的Sort函数默认对基础数据类型使用值排序,对引用类型是对指向的地址值排序。

比较器的含义解释:

比较器需要重写compare方法,根据返回值的情况判定大小关系。如果返回值<0,则认为o1<o2,如果返回值=0,认为o1=o2,否则认为o1>o2,而Sort是按照升序排序的,所以小值在前大值在后。

public class IdComparator implements java.util.Comparator<Student> {
        @Override
        public int compare(Student o1,Student o2){
            return o1.id-o2.id;
        }
}

比较器的使用

import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.TreeSet;

public class Comparator {
    public static class Student{
        public String name;
        public int id;
        public int age;

        public Student(String name, int id, int age) {
            this.name = name;
            this.id = id;
            this.age = age;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", id=" + id +
                    ", age=" + age +
                    '}';
        }
    }

    public static class  IdAscComparator implements java.util.Comparator<Student> {//升序
        @Override
        public int compare(Student o1,Student o2){
            return o1.id-o2.id;
        }
    }

    public static class  IdDescComparator implements java.util.Comparator<Student> {//降序
        @Override
        public int compare(Student o1,Student o2){
            return o2.id-o1.id;
        }
    }

    public static void main(String[] args) {
        Student student1=new Student("w",6,2);
        Student student2=new Student("x",2,2);
        Student student3=new Student("c",7,2);

        System.out.println("升序结果:");
        Student[] students=new Student[]{student1,student2,student3};
        Arrays.sort(students,new IdAscComparator());
        for (Student student : students) {
            System.out.println(student);
        }
        System.out.println("======================================\n降序结果:");

        Arrays.sort(students,new IdDescComparator());
        for (Student student : students) {
            System.out.println(student);
        }
        System.out.println("======================================\n优先级队列-升序:");
        //优先级队列使用
        PriorityQueue<Student> heap=new PriorityQueue<>(new IdAscComparator());
        heap.add(student1);
        heap.add(student2);
        heap.add(student3);
        while(!heap.isEmpty()){
            Student student=heap.poll();
            System.out.println(student);
        }
        System.out.println("======================================\nTreeSet降序");

        TreeSet<Student> treeSet=new TreeSet<>(new IdAscComparator());
        treeSet.add(student1);
        treeSet.add(student2);
        treeSet.add(student3);
        for (Student student : treeSet) {
            System.out.println(student);
        }
    }
}

排序算法补充:

1.归并排序的额外空间复杂度可以变成O(1),称为“归并排序 内部缓存法”

2.快速排序有一篇论文“01 stable sort”,可以做到稳定性,但是我们日常所写的快排是做不到的

3.奇偶面试题:有一个数组,要求排序后奇数在左偶数在右,并保证数据的相对次序不变(即具有稳定性)要求时间复杂度O(n),额外空间复杂度O(1),这其实就是变着法考察是否可以让快排变为稳定(一般没有面试官这么变态问这个->->,左神给你撑腰,碰到这种面试官给他说一下题目的来龙去脉让他自己回答,他回答不上来就【默默】举报他┑( ̄Д  ̄)┍)。因为快排也是一个01标准问题(<=bias放左边,>bias放右边)。但是如果给了O(n)的额外空间复杂度,可以用辅助数组+双指针的解法。

public class Study {
    public static int[] AddAndOdd(int[] arr){
        int[] help=new int[arr.length];
        int add=0,odd=arr.length-1,L=0,R=arr.length-1;
        while(L<=R){
            if(arr[add]%2==1)help[L++]=arr[add];
            if(arr[odd]%2==0)help[R--]=arr[odd];
            ++add;
            --odd;
        }
        return help;
    }

    public static void main(String[] args) {
        int[] arr=new int[]{1,3,2,56,3436,6768,23,67};
        int[] ans=AddAndOdd(arr);
        for (int i : ans) {
            System.out.println(i+" ");
        }
    }
}

非基于比较的排序方法:桶排序、计数排序、基数排序

桶排序(是一个抽象的思想):由于桶本身就具有大小关系,所以可以不直接比较A、B,而是通过桶间接比较A、B。

计数排序(桶排序的一种实现思路):设计一个数组Count[i],Count[i]表示原数组中用于Count[i]个i。显然这种排序的瓶颈与数据范围有关,如果数据的跨度很大且分布不均匀,是非常不适合计数排序的。【如果说用基于比较的排序搞个离散化再进桶的话,为啥要这么多此一举呢23333】

面试题:给定一个数组,求如果排序之后,相邻两数的最大差值,要求时间复杂度O(N),且不能使用非基于比较的排序。

思路:对N个数,建立N+1个桶【0~N】。首先扫描一遍数组得到max和min,如果max==min,则证明数组的元素全部相同。否则,将min放入1号桶,将max放入N+1号桶,除了最后一个桶之外,我们让所有桶的数据跨度为d=(max-min+1)/(N+1),最后一个桶可能会因为除不尽而不足这个值。此时,把所有元素归位到属于的桶里index=(Arr[i]-min)/d。由于1和N+1号桶都有数,且总共有N个数、N+1个桶,根据鸽巢原理,中间至少存在一个空桶。

        那么存在一个空桶,就可以保证,答案一定是>d的,而来自一个桶内的元素插值一定是<d的,所以答案不可能来自于同一个桶内部元素的差值。我们针对每个桶维护一个Max和Min,从左到右扫描数组,第i号桶贡献的答案为i号桶的最小值减掉i往左第一个非空桶的最大值。但是呢!!!必须要注意的是,答案并不一定是来自于被空桶所隔开的两个桶。空桶可以保证差值>d,但是两个相邻桶的最大差值可以达到2d-1【左边桶min=max=桶下届,右边桶min=max=桶上界】。

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[] hashNum=new boolean[len+1];
        int[] maxs=new int[len+1];
        int[] mins=new int[len+1];
        int bid;
        for(int i=0;i<len;++i){
            bid=getIndex(nums[i],len,min,max);
            mins[bid]=hashNum[bid]?Math.min(mins[bid],nums[i]):nums[i];
            maxs[bid]=hashNum[bid]?Math.max(maxs[bid],nums[i]):nums[i];
            hashNum[bid]=true;
        }
        int res=0;
        int lastMax=maxs[0];
        for(int i=1;i<=len;++i){
            if(hashNum[i]){
                res=Math.max(res,mins[i]-lastMax);
                lastMax=maxs[i];
            }
        }
        return res;
    }
    public static int getIndex(int num,int len,int min,int max){
        return (num-min)*len/(max-min);
    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值