• 02-荷兰国旗,堆排序,各种排序


    1、荷兰国旗

    1、数组分开

    给定一个数组arr,和一个数num,请把小于等于num的数放在数
    组的左边,大于num的数放在数组的右边。
    要求额外空间复杂度O(1),时间复杂度O(N)

    2、荷兰国旗

      

    流程:

     = num ,cur跳下一个

    <num,cur交换小于区域的下一个,

              less++,cur++

    >num,cur交换大于区域的上一个

       more--,cur不变

     cur==more 停止

    java版本

    package basic_class_01;
    
    public class Code_08_NetherlandsFlag {
    
        public static int[] partition(int[] arr, int l, int r, int p) {
            int less = l - 1;
            int more = r + 1;
            while (l < more) {
                if (arr[l] < p) {
                    swap(arr, ++less, l++);
                } else if (arr[l] > p) {
                    swap(arr, --more, l);
                } else {   // == num
                    l++;
                }
            }
            return new int[] { less + 1, more - 1 };  // 等于区域的左右 边界
        }
    
        public static void swap(int[] arr, int i, int j) {
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }
        
        // for test
        public static int[] generateArray() {
            int[] arr = new int[10];
            for (int i = 0; i < arr.length; i++) {
                arr[i] = (int) (Math.random() * 3);
            }
            return arr;
        }
    
        // for test
        public static void printArray(int[] arr) {
            if (arr == null) {
                return;
            }
            for (int i = 0; i < arr.length; i++) {
                System.out.print(arr[i] + " ");
            }
            System.out.println();
        }
    
        public static void main(String[] args) {
            int[] test = generateArray();
    
            printArray(test);
            int[] res = partition(test, 0, test.length - 1, 1);
            printArray(test);
            System.out.println(res[0]);
            System.out.println(res[1]);
    
        }
    }

     

     python版本

     2、快速排序

    1、经典快排

    每次搞定一个x

    2、荷兰问题改进经典快排

    每次搞定一片X

    public static void quickSort(int[] arr) {
            if (arr == null || arr.length < 2) {
                return;
            }
            quickSort(arr, 0, arr.length - 1);
        }
    
        public static void quickSort(int[] arr, int l, int r) {
            if (l < r) {
                // swap(arr, l + (int) (Math.random() * (r - l + 1)), r); 随机快排
                int[] p = partition(arr, l, r);
                quickSort(arr, l, p[0] - 1);
                quickSort(arr, p[1] + 1, r);
            }
        }
    
        public static int[] partition(int[] arr, int l, int r) {
            int less = l - 1;
            int more = r;
            while (l < more) {
                if (arr[l] < arr[r]) {
                    swap(arr, ++less, l++);
                } else if (arr[l] > arr[r]) {
                    swap(arr, --more, l);
                } else {
                    l++;
                }
            }
            swap(arr, more, r);
            return new int[] { less + 1, more };   // 返回 ==num 的左右边界
        }
    
        public static void swap(int[] arr, int i, int j) {
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }

     

    3、经典快排的问题

    1、每次搞定一个数

    2、与数据状况有关系

    4、随机快排

    时间复杂度O(N*logN),额外空间复杂度O(logN)

    每次随机选择一个数

     

        public static void quickSort(int[] arr) {
            if (arr == null || arr.length < 2) {
                return;
            }
            quickSort(arr, 0, arr.length - 1);
        }
    
        public static void quickSort(int[] arr, int l, int r) {
            if (l < r) {
                swap(arr, l + (int) (Math.random() * (r - l + 1)), r);  //随机快排
                int[] p = partition(arr, l, r);
                quickSort(arr, l, p[0] - 1);
                quickSort(arr, p[1] + 1, r);
            }
        }
    
        public static int[] partition(int[] arr, int l, int r) {
            int less = l - 1;
            int more = r;
            while (l < more) {
                if (arr[l] < arr[r]) {
                    swap(arr, ++less, l++);
                } else if (arr[l] > arr[r]) {
                    swap(arr, --more, l);
                } else {
                    l++;
                }
            }
            swap(arr, more, r);
            return new int[] { less + 1, more };   // 返回 ==num 的左右边界
        }
    
        public static void swap(int[] arr, int i, int j) {
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }

    5、随机快排的优势

    1、常数项只while一次

    2、空间复杂度 O(logN)  保存划分点

    3、堆排序 :完全二叉树

    1、满二叉树、完全二叉树

    2、完全二叉树 与 数组 

    3、大根堆 与 小根堆

    大根堆:任何一个子树的最大值都是子树的头部

    小根堆:任何一个子树的最小值都是子树的头部

        

    4、建立大根堆算法

     

     1、流程:

    与父节点的数比较,父(i-1)/2

    if cur比父大,则交换,cur来到父位置,继续比较

    else 不交换

        public static void heapSort(int[] arr) {
            if (arr == null || arr.length < 2) {
                return;
            }
            for (int i = 0; i < arr.length; i++) {
                heapInsert(arr, i);   // 0~i  
            }
            int size = arr.length;
            swap(arr, 0, --size);
            while (size > 0) {
                heapify(arr, 0, size);
                swap(arr, 0, --size);
            }
        }
    
        public static void heapInsert(int[] arr, int index) {
            while (arr[index] > arr[(index - 1) / 2]) {
                swap(arr, index, (index - 1) / 2);
                index = (index - 1) / 2;   
            }
        }

    2、建立一个大根堆的复杂度

    每次一调整

    当有N个节点已经存在,进来一个数N+1,需要调整O(logN)次

     

    总共调整的复杂度

    5、heapInsert 与 heapify

    heapInsert:较大的值,往上升的操作

    heapify:较小的值,往下沉的操作,与左右孩子最大的交换 

    max(2*i+1, 2*i+2)

        

        public static void heapify(int[] arr, int index, int size) {
            int left = index * 2 + 1;
            while (left < size) {   // 不越界
                int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;  // 左右孩子最大值 与 我比较
                largest = arr[largest] > arr[index] ? largest : index;
                if (largest == index) {
                    break;
                }
                swap(arr, largest, index);   // lagest != index
                index = largest;
                left = index * 2 + 1;
            }
        }

     6、寻找流中的中位数

    1、笨办法

    temp = [] 收集数据O(1),  每次求值,都要排序O(logN)

    2、堆

        

    弹出堆顶

      

     BAT 起码5个堆的问题

    优先级队列

    堆什么时候都不能忘了

    被火车撞了都不能忘

    所有的贪心问题

    非常重要,一定要会

    堆头部与最后一个数交换,

    heapsize -1,

    调整堆

    堆排序的细节和复杂度分析
    时间复杂度O(N*logN),额外空间复杂度O(1)
    堆结构非常重要
    1,堆结构的heapInsert与heapify
    2,堆结构的增大和减少heapSize
    3,如果只是建立堆的过程,时间复杂度为O(N)
    4,优先级队列结构,就是堆结构

        public static void heapSort(int[] arr) {
            if (arr == null || arr.length < 2) {
                return;
            }
            for (int i = 0; i < arr.length; i++) {
                heapInsert(arr, i);   // 0~i  
            }
            int size = arr.length;
            swap(arr, 0, --size);
            while (size > 0) {
                heapify(arr, 0, size);
                swap(arr, 0, --size);
            }
        }
    
        public static void heapInsert(int[] arr, int index) {
            while (arr[index] > arr[(index - 1) / 2]) {
                swap(arr, index, (index - 1) / 2);
                index = (index - 1) / 2;   
            }
        }
    
        public static void heapify(int[] arr, int index, int size) {
            int left = index * 2 + 1;
            while (left < size) {   // 不越界
                int largest = left + 1 < size && arr[left + 1] > arr[left] ? left + 1 : left;  // 左右孩子最大值 与 我比较
                largest = arr[largest] > arr[index] ? largest : index;
                if (largest == index) {
                    break;
                }
                swap(arr, largest, index);   // lagest != index
                index = largest;
                left = index * 2 + 1;
            }
        }
    
        public static void swap(int[] arr, int i, int j) {
            int tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
        }

    3、排序算法的稳定性及其汇总

    相同的值,相对顺序是否被打乱

    为什么要追求稳定性?

     业务过程中,保留原始数据的次序

    先height排序,在age排序,需要保持height的原始次序。稳定性

    4、工程中的综合排序算法

    1、首先看数据类型

    从稳定性出发的

    基础类型:int,double,char,float,string

    选择快排序

    自己定义的类型 stu:name:age   

    选择归并排序

    2、在看,数组长度

    数组长度较短,使用插入排序,飞快

    样本量<60时,常数项很低,直接使用,插排

    sort(Left,Right)

    if (len<60)  {插入排序}  

    有关排序问题的补充:

    1,归并排序的额外空间复杂度可以变成O(1),但是非常难,不
    需要掌握,可以搜“归并排序 内部缓存法”


    2,快速排序可以做到稳定性问题,但是非常难,不需要掌握,
    可以搜“01 stable sort”


    3,有一道题目,是奇数放在数组左边,偶数放在数组右边,还
    要求原始的相对次序不变,碰到这个问题,可以怼面试官。面试
    官非良人。

    diss面试

    Question:

    维持相对次序

    快排一般情况实现不了,但是01 stable sort可以做到稳定性

    01标准

    奇偶标准,一类的

    “ 01 stable sort” 

    01稳定排序

     荷兰国旗做不到稳定性,只能分层

    5、认识比较器

    package basic_class_01;
    
    import java.util.Arrays;
    import java.util.Comparator;
    
    public class Code_09_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;
            }
        }
    
        public static class IdAscendingComparator implements Comparator<Student> {
    
            @Override
            public int compare(Student o1, Student o2) {
                return o1.id - o2.id;
            }
    
        }
    
        public static class IdDescendingComparator implements Comparator<Student> {
    
            @Override
            public int compare(Student o1, Student o2) {
                return o2.id - o1.id;
            }
    
        }
    
        public static class AgeAscendingComparator implements Comparator<Student> {
    
            @Override
            public int compare(Student o1, Student o2) {
                return o1.age - o2.age;
            }
    
        }
    
        public static class AgeDescendingComparator implements Comparator<Student> {
    
            @Override
            public int compare(Student o1, Student o2) {
                return o2.age - o1.age;
            }
    
        }
    
        public static void printStudents(Student[] students) {
            for (Student student : students) {
                System.out.println("Name : " + student.name + ", Id : " + student.id + ", Age : " + student.age);
            }
            System.out.println("===========================");
        }
    
        public static void main(String[] args) {
            Student student1 = new Student("A", 1, 23);
            Student student2 = new Student("B", 2, 21);
            Student student3 = new Student("C", 3, 22);
    
            Student[] students = new Student[] { student3, student2, student1 };
            printStudents(students);
    
            Arrays.sort(students, new IdAscendingComparator());
            printStudents(students);
    
            Arrays.sort(students, new IdDescendingComparator());
            printStudents(students);
    
            Arrays.sort(students, new AgeAscendingComparator());
            printStudents(students);
    
            Arrays.sort(students, new AgeDescendingComparator());
            printStudents(students);
    
        }
    
    }

    1、自定义比较器  (C语言 重载大于号,运算符)

    if (o1.id>o2.id)

        return -1

    elif o1.id<o2.id

        return 1

    else

        return 0

    等价于 

    id升序排列

    return o1.id-o2.id   

    2、堆==优先级队列

    堆也可以用比较器

    建堆方法<自定义比较器>

    3、TreeMap<>  红黑树  

     基于比较的排序是重点

    lambda表达式可以实现

     6、桶排序,计数排序,基数排序

    不基于比较的排序

    桶 ----  容器

    桶排序是基础

    计数排序,基数排序是桶排序的 具体实现。

    1,非基于比较的排序,与被排序的样本的实际数据状况很有关系,所
    以实际中并不经常使用
    2,时间复杂度O(N),额外空间复杂度O(N)
    3,稳定的排序

    数组的词频---计数排序

     

    必考问题:补充问题

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

    借用了桶排序的概念

    等分为

    0~99分成10份

    鸽笼原理

    中间必存在一个空桶

     

    否定  最大差值不来自同一个桶

    max最大差值一定来自与两个桶

    只存,

    这个桶是否进来过数

    这个桶的最小,最大值

     

    当前桶的最小值min,与它前一个桶的最大max

    最大差值 一定在 max-min 差值 中

    空桶只是:否定最大差值不来自同一个桶的相邻数

    package basic_class_01;
    
    import java.util.Arrays;
    
    public class Code_11_MaxGap {
    
        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;
            
            // 找到array的最小值,最大值
            for (int i = 0; i < len; i++) {
                min = Math.min(min, nums[i]);
                max = Math.max(max, nums[i]);
            }
            if (min == max) {
                return 0;
            }
            
            // 每个桶的 3个信息
            boolean[] hasNum = new boolean[len + 1];  
            int[] maxs = new int[len + 1];
            int[] mins = new int[len + 1];
            
            int bid = 0;
            
            // 当前数去哪个桶,桶的3个信息要更新
            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;
            
            // 当前桶min与它前一个桶的max的差值,最大差值就在其中
            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));
        }
    
        // for test
        public static int comparator(int[] nums) {
            if (nums == null || nums.length < 2) {
                return 0;
            }
            Arrays.sort(nums);
            int gap = Integer.MIN_VALUE;
            for (int i = 1; i < nums.length; i++) {
                gap = Math.max(nums[i] - nums[i - 1], gap);
            }
            return gap;
        }
    
        // for test
        public static int[] generateRandomArray(int maxSize, int maxValue) {
            int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
            for (int i = 0; i < arr.length; i++) {
                arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
            }
            return arr;
        }
    
        // for test
        public static int[] copyArray(int[] arr) {
            if (arr == null) {
                return null;
            }
            int[] res = new int[arr.length];
            for (int i = 0; i < arr.length; i++) {
                res[i] = arr[i];
            }
            return res;
        }
    
        // for test
        public static void main(String[] args) {
            int testTime = 500000;
            int maxSize = 100;
            int maxValue = 100;
            boolean succeed = true;
            for (int i = 0; i < testTime; i++) {
                int[] arr1 = generateRandomArray(maxSize, maxValue);
                int[] arr2 = copyArray(arr1);
                if (maxGap(arr1) != comparator(arr2)) {
                    succeed = false;
                    break;
                }
            }
            System.out.println(succeed ? "Nice!" : "Fucking fucked!");
        }
    
    }

     

  • 相关阅读:
    使用 Helm【转】
    部署 Helm【转】
    Helm 架构【转】
    Why Helm?【转】
    用 ConfigMap 管理配置【转】
    js argument实参集合与局部变量、参数关系
    js 定时器
    JVM的调优
    Java异常,以及finally不会执行的情况
    Java绑定
  • 原文地址:https://www.cnblogs.com/venicid/p/9864971.html
Copyright © 2020-2023  润新知