基本排序算法之桶排序(力扣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();
    }
posted @ 2020-05-24 22:04  有心有梦  阅读(879)  评论(0编辑  收藏  举报