zoukankan      html  css  js  c++  java
  • 基本排序算法之桶排序(力扣451题、347题)

    桶排序是对整数进行排序的高效算法,在进行桶排序的时候我们需要先确定key,即key代表得含义,以及key的取值范围,key的取值范围决定了桶的数量。假设键值的范围是从0到t,那么需要t+1个桶,标记分别为0、1、……、t 。如果元素的键值是i,那么就将该元素放入桶i中,每个桶放的都是键值相同的元素。

    一般使用一个ArraysList数组作为一组不同标记的桶。桶中存储的正是键值相同的一系列元素。伪代码如下:

        public void bucketsort(E[] list){
            
            ArrayList[] bucket = new ArrayList[t+1];
    
            for (int i = 0; i < list.length; i++) {
                int key = list[i].getKey();
                
                if (bucket[key] == null){
                    bucket[key] = new ArrayList<>();
                }
                bucket[key].add(list[i]);
            }
            
            int k = 0;
            for (int i = 0; i < bucket.length; i++) {
                if (bucket[i]!=null){
                    for (int j = 0; j < bucket[i].size(); j++) {
                        list[k++] = bucket[i].get(j);
                    }
                }
            }
            
        }

    桶排序的时间复杂度O(n+t),空间复杂度为O(n+t),n为元素列表的大小,t为桶的大小

    练习:

    1、力扣第451题

      给定一个字符串,请将字符串里的字符按照出现的频率降序排列。

    分析思路:

      将字符串中出现频率最高的字符放在最前边,字符出现的频率的大小为1-n,所以以字符出现的频数作为桶的标记,将频数出现相同的放在一个桶内,有一个细节就是如果同一个字符,如果出现的频数为i,那么就将它放入桶中i个,这样方便后面求出最终的排列结果。最后,倒序遍历桶,然后依次拼接字符串。

    具体的代码如下:

    public String frequencySort(String s) {
            if (s.length() <=2){
                return s;
            }
            HashMap<Character, Integer> map = new HashMap<>();
            for (char c : s.toCharArray()) {
                map.put(c,map.getOrDefault(c,0)+1);
            }
    
            ArrayList[] bucket = new ArrayList[s.length() + 1];
    
            for (Character c : map.keySet()) {
                int count = map.get(c);
                if (bucket[count] == null){
                    bucket[count] = new ArrayList<>();
                }
                if (count>1){
                    for (int i = 0; i < count; i++) {
                        bucket[count].add(c);
                    }
                }else {
                    bucket[count].add(c);
                }
    
            }
    
            char[] res = new char[s.length()];
            int k = 0;
            for (int i = bucket.length-1; i >= 0; i--) {
                if (bucket[i] == null){
                    continue;
                }else {
                    for (int j = 0; j < bucket[i].size(); j++) {
                        res[k++] = (Character)bucket[i].get(j);
                    }
                }
            }
    
            return new String(res);
    
        }

    2、力扣第347题

      给定一个非空的整数数组,返回其中出现频率前 高的元素。

        你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
        你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
        题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
        你可以按任意顺序返回答案。

    分析思路:

      要求求出出现频率最高的前K个元素,那么说明数组中元素的频数至少为1,至多为n(假设数组一共n个元素),那么我们可以以元素出现的频数作为键值进行桶排序,设t为数组不重复元素的个数,那么需要的桶的标记数量就为t+1,每个桶中存储的元素就是频度相同的元素。最后我们从后向前遍历桶,求出k个不同元素即可。

    参考自:https://github.com/CyC2018/CS-Notes/      (膜拜大神)

    实现代码如下:

        public int[] topKFrequent(int[] nums, int k) {
            if (nums == null){
                return null;
            }
            HashMap<Integer, Integer> map = new HashMap<>();
            int n = nums.length;
            for (int num : nums) {
                if (map.get(num) == null){
                    map.put(num,1);
                }else {
                    int count = map.get(num);
                    count ++;
                    map.put(num,count);
                }
            }
            // 用元素出现的频数作为key,key的范围是0-t,所以需要t+1个桶,标记从0-t
            ArrayList[] bucket = new ArrayList[n + 1];
            // 将频度相同的元素添加到相同的水桶中
            for (Integer key : map.keySet()) {
                int count = map.get(key);
                if (bucket[count] == null){
                    bucket[count] = new ArrayList<>();
                }
                bucket[count].add(key);
            }
            // 进行寻找频率前k高的元素
            ArrayList<Integer> res = new ArrayList<>();
            for (int i = bucket.length-1; i >=0; i--) {
                if (bucket[i] == null){
                    continue;
                }else {
                    if (bucket[i].size() <= (k-res.size())){
                        res.addAll(bucket[i]);
                    }else {
                        res.addAll(bucket[i].subList(0,k-res.size()));
                    }
                }
            }
            return res.stream().mapToInt(Integer::valueOf).toArray();
        }
  • 相关阅读:
    对于想用OS但又觉得单片机资源太过紧张,状态机是个不错的选择分享一种状态机设计方法
    状态机实践入门
    Codewarrior 调试错误ILLEGAL_BP
    坑爹的AVR编译器中文路径问题
    跨入AVR
    atmega8 例程:USART串口通信
    2011总结
    atmega8 默认内部RC振荡 + 解锁
    关于AVR I/O 的驱动能力的介绍
    atmega8 例程:系统库函数的延迟
  • 原文地址:https://www.cnblogs.com/yxym2016/p/12952987.html
Copyright © 2011-2022 走看看