【Leetcode】Java:排序

这篇博客记录了使用Java解决LeetCode中关于排序的题目,包括找到数组中的第k个最大元素、前k个高频元素、字符频率排序和颜色分类问题。主要解题方法涉及大顶堆、小顶堆、快速排序和桶排序等数据结构和算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


参考了很多大佬的题解,仅作为自己学习笔记用。


215. 数组中的第 k 个最大元素(medium)

题意:

在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

在这里插入图片描述

题解1:

求解 TopK 问题,也就是 K 个最小元素的问题,可以使用大顶堆来实现 TopK 问题。
大顶堆的堆顶元素为当前堆的最大元素。实现过程:不断地往大顶堆中插入新元素,当堆中元素的数量大于 k 时,移除堆顶元素,也就是当前堆中最大的元素,剩下的元素都是当前添加过的元素中最小的 K 个元素。插入和移除堆顶元素的时间复杂度都为 log2N。

堆也可以用于求解 Kth 问题,得到了大小为 K 的小顶堆之后,堆顶元素就是第 K 大的元素。本题就可以使用小顶堆来实现。

    public int findKthLargest(int[] nums, int k) {
        Queue<Integer> queue = new PriorityQueue<>();   // 小顶堆

        for(int n : nums){
            queue.add(n);
            if(queue.size() > k)    // 如果个数多于k,九八堆顶弹出,也就是最小的元素弹出
                queue.poll();
        }
        return queue.poll();
    }

题解2:

用于求解 Kth Element 问题,也就是第 K 个元素的问题。
可以使用快速排序的 partition() 进行实现。需要先打乱数组,否则最坏情况下时间复杂度为 O(N2)。

class Solution {
    public int findKthLargest(int[] nums, int k) {    
        k = nums.length - k;    // 要找的元素排序后的下标
        int l = 0, h = nums.length - 1;
        while(l < h){
            int j = partition(nums, l, h);
            if(j == k){
                break;
            }else if(j < k){
                l = j + 1;
            }else{
                h = j - 1;
            }
        }        
        return nums[k];
    }

    int partition(int[] a, int lo, int hi) {
        int i = lo, j = hi + 1;               // 左右扫描指针,右指针先加1
        int v = a[lo];                        // 第一个为基准元素,切分元素

        while(true){
            while(++i <= hi && a[i] < v);     // 扫描左右,检查扫描是否结束,并交换元素
            while(--j >= lo && a[j] > v);
            if(i >= j)
                break;
            swap(a, i, j);
        }
        swap(a, lo, j);                  // 指针相遇时,交换v和a[j],将v=a[j] 放入正确位置
        return j;                        // a[lo...j-1] <= a[j] <= a[j+1...hi] 达成
    }

    void swap(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}

347. 前 k 个高频元素(medium)

题意:

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

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

在这里插入图片描述

题解1:

桶排序,直接上思路,看图应该好理解一点。
在这里插入图片描述

    public int[] topKFrequent(int[] nums, int k) {
        // 创建 数组长度+1 个桶,第一个桶是用不到的,每一个桶都是一个list
        List<Integer>[] buckets = new ArrayList[nums.length + 1]; 
        Map<Integer, Integer> cntMap = new HashMap<>();     // 统计每个字符出现次数
        int[] res = new int[k];

        for(int n : nums){
            cntMap.put(n, cntMap.getOrDefault(n, 0) + 1);
        }
        for(int key : cntMap.keySet()){   // 出现了cnt 次,就把 key 放到 bucket[cnt] 的list里面
            int cnt = cntMap.get(key);
            if(buckets[cnt] == null)
                buckets[cnt] = new ArrayList<>();
            buckets[cnt].add(key);
        }
        int index = 0;
        for(int i = buckets.length - 1; i >= 0 && index < k; i--){
            if(buckets[i] != null){
                for(int n : buckets[i]){
                    if(index == k)  break;
                    res[index++] = n;
                }              
            }           
        }
        return res;
    }

题解2:

小顶堆。先用 map 存放出现次数,然后放入堆中。
如果堆的元素个数小于 k,直接入堆。如果堆的元素个数等于 k,则检查堆顶与当前出现次数的大小。如果堆顶次数更小,就弹出堆顶,并将当前值插入堆中。

    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> cntMap = new HashMap<>();
        for (int n : nums) {
            cntMap.put(n, cntMap.getOrDefault(n, 0) + 1);
        }

        // int[] 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {// 小顶堆
            public int compare(int[] m, int[] n) {
                return m[1] - n[1];
            }
        });
        for (Map.Entry<Integer, Integer> entry : cntMap.entrySet()) {
            int num = entry.getKey(), cnt = entry.getValue();
            if (queue.size() == k) {
                if (queue.peek()[1] < cnt) {
                    queue.poll();
                    queue.offer(new int[]{num, cnt});
                }
            } else {
                queue.offer(new int[]{num, cnt});
            }
        }
        int[] res = new int[k];
        for (int i = 0; i < k; ++i) {
            res[i] = queue.poll()[0];
        }
        return res;
    }

451. 字符出现频率排序(medium)

题意:

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

在这里插入图片描述

题解:

桶排序。

    public String frequencySort(String s) {
        if(s == null || s.length() == 1)
            return s;

        Map<Character, Integer> map = new HashMap<>();
        for(char c : s.toCharArray())           // 将字符放入map,key位字符,value为次数
            map.put(c, map.getOrDefault(c, 0) + 1);

        List<Character>[] buckets = new List[s.length() + 1];   // 定义一个list数组,每一个list都是一个桶
        for(char key : map.keySet()){
            int value = map.get(key);
            if(buckets[value] == null)
                buckets[value] = new ArrayList<Character>();
            buckets[value].add(key);        // 将key添加到value对应的list(桶)中
        }

        StringBuilder sb = new StringBuilder();
        for(int i = buckets.length - 1; i > 0; i--){        // 遍历每个桶
            if(buckets[i] != null){
                for(char c : buckets[i]){                   // 拿出桶里面的每个字符
                    for(int j = i; j > 0; j--){             // 根据下标决定添加次数
                        sb.append(c);
                    }
                }
                
            }
        }
        return sb.toString();
    }

075. 颜色分类(medium)

题意:

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
在这里插入图片描述
题解:

遍历指针为 i,当遍历到的元素等于 0 时,放到最左边,遍历到的元素等于 2 时,放到最右边。

class Solution {
    public void sortColors(int[] nums) {
        int zero = -1, i = 0, two = nums.length;
        while(i < two){
            if(nums[i] == 0){
                swap(nums, ++zero, i++);
            }else if(nums[i] == 2){
                swap(nums, --two, i);
            }else{
                i++;
            }
        }
    }

    void swap(int[] a, int i, int j){
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值