• leetcode 215 & 347 排序:堆、快排、桶排序


    1⃣️普通库函数排序:

    class Solution {
        public int findKthLargest(int[] nums, int k) {
            Arrays.sort(nums);
            return nums[nums.length-k];
        }
    }

    2⃣️堆排序:

    时间复杂度 O(nlogk),空间复杂度 O(k)

    三种函数poll,peek,element,add

    共同点:

    • 都是返回队列中的首个元素

    不同点:

    • poll:将首个元素从队列中弹出,如果队列是空的,就返回null
    • peek:查看首个元素,不会移除首个元素,如果队列是空的就返回null
    • element:查看首个元素,不会移除首个元素,如果队列是空的就抛出异常NoSuchElementException
    • add:添加新元素在堆内
    • size:表示求堆的大小
    1. 首先先整理一下本题的思路,要求的是数组中第K个最大元素,也就是说要求排序之后的倒数第K个;
    2. 再考虑应该是用大顶堆还是小顶堆呢?只有使用小顶堆才能每次都poll(更新)出去不是候选的数值,也就是每次都poll出去最小的第K+1个,来始终保持堆顶的元素是K个最大的元素中最小的一个;
    3. 注意大顶堆和小顶堆两者的建立,之前我的博文中有提到:

    Java 使用 PriorityQueue<>((x, y) -> (y - x)) 可方便实现大顶堆。

    Java 使用 PriorityQueue<>() 可方便实现小顶堆。

        实际上小顶堆内部默认的完整格式是:PriorityQueue<>((x, y) -> (x - y)),尤其注意全部参数的设置都是括号里面的写法;

    class Solution {
        public int findKthLargest(int[] nums, int k) {
            PriorityQueue<Integer> q=new PriorityQueue<>();
            for(int n:nums){
                q.add(n);
                if(q.size()>k){
                    q.poll();
                }
            }
            return q.peek();
        }      
    }

     3⃣️快速选择

    一般用于求解  Kth Element 问题,可以在 O(n) 时间复杂度,O(1) 空间复杂度完成求解工作。与快速排序一样,快速选择一般需要先打乱数组,否则最坏情况下时间复杂度为 O(n2)。也就是说对于每一个选定的对比数都要比较N次。

    快速排序较为详细介绍:https://xiaozhuanlan.com/topic/6854379012

    import java.lang.*;
    class Solution {
        //为什么要用this指针呢?如果不用this指针函数参数就要一直传递nums数组。
        int[] nums;
        int size;
        public int findKthLargest(int[] nums, int k){
            this.nums = nums;
            size = nums.length;
            // kth largest is (N - k)th smallest
            return quickselect(0, size - 1, size - k);
        }
        public int quickselect(int l,int r,int index){
            //注意这里既可以用递归的方法也可以用循环的方法
            //递归的方法
            if(l==r) return this.nums[l];
            Random rd=new Random();
            int pivot=l+rd.nextInt(r-l);//产生一定范围的随机数
            int j=partition(l,r,pivot);
            if(j==index) return this.nums[j];
            if(j>index){
                return quickselect(l,j-1,index);//注意不是j
            }
            else {
                return quickselect(j+1,r,index);
            }
            //循环的方法
            // k = nums.length - k;
            // int lo = 0;
            // int hi = nums.length - 1;
            // while (lo < hi) {
            //     final int j = partition(nums, lo, hi);
            //     if(j < k) {
            //         lo = j + 1;
            //     } else if (j > k) {
            //         hi = j - 1;
            //     } else {
            //         break;
            //     }
            // }
            // return nums[k];
        }
        public int partition(int l,int r,int pivot){
            //这里写的比较晦涩,把最右边的数值选作为pivot对比数
            int cp=this.nums[pivot];
            swap(pivot,r);
            int zl=l;
            //从头开始遍历,用一个滞留指针指向最靠前的大于对比数的值,也就是把小于对比数的值移动到前面
            for(int i=l;i<=r;i++){
                if(this.nums[i]<cp){
                    swap(i,zl);
                    zl++;
                }
            }
            swap(zl,r);
            return zl;
        }
        public void swap(int l,int r){
            int tmp=this.nums[l];
            this.nums[l]=this.nums[r];
            this.nums[r]=tmp;
        }
    }

     leetcode 347

    //基于桶排序求解「前 K 个高频元素」
    class Solution {
        public List<Integer> topKFrequent(int[] nums, int k) {
            List<Integer> res = new ArrayList();
            // 使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值
            HashMap<Integer,Integer> map = new HashMap();
            for(int num : nums){
                if (map.containsKey(num)) {
                   map.put(num, map.get(num) + 1);
                 } else {
                    map.put(num, 1);
                 }
            }
            
            //桶排序
            //将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标
            List<Integer>[] list = new List[nums.length+1];
            for(int key : map.keySet()){
                // 获取出现的次数作为下标
                int i = map.get(key);
                if(list[i] == null){
                   list[i] = new ArrayList();
                } 
                list[i].add(key);
            }
            
            // 倒序遍历数组获取出现顺序从大到小的排列
            for(int i = list.length - 1;i >= 0 && res.size() < k;i--){
                if(list[i] == null) continue;
                res.addAll(list[i]);
            }
            return res;
        }
    }
    public List<Integer> topKFrequent(int[] nums, int k) {
        List<Integer> ret = new ArrayList<>();
        Map<Integer, Integer> map = new HashMap<>();
        for(int num : nums) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        List<Integer>[] bucket = new List[nums.length + 1];
        for(int key : map.keySet()) {
            int frequency = map.get(key);
            if(bucket[frequency] == null) {
                bucket[frequency] = new ArrayList<>();
            }
            bucket[frequency].add(key);
        }
    
        for(int i = bucket.length - 1; i >= 0 && ret.size() < k; i--) {
            if(bucket[i] != null) {
                ret.addAll(bucket[i]);
            }
        }
        return ret;
    }
  • 相关阅读:
    C++编译器详解(二)常见precompiling 指令介绍
    C++编译器详解(一)
    Music
    jQuery语法
    Freedom DownTime
    A
    Map类
    伤不起:File.toPath() & Paths.get()
    在不同浏览器中空格显示的效果不一致的问题(主要是宽度不一致)
    关于xmlhttp会使用ie的缓存的问题及解决
  • 原文地址:https://www.cnblogs.com/sjh-dora/p/12898701.html
Copyright © 2020-2023  润新知