LeetCode163--前k个高频元素(L347)、字符串解码(L394)

1、前k个高频元素

//给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
//
//
//
// 示例 1:
//
//
//输入: nums = [1,1,1,2,2,3], k = 2
//输出: [1,2]
//
//
// 示例 2:
//
//
//输入: nums = [1], k = 1
//输出: [1]
//
//
//
// 提示:
//
//
// 1 <= nums.length <= 105
// k 的取值范围是 [1, 数组中不相同的元素的个数]
// 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
//
//
//
//
// 进阶:你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。
// Related Topics 数组 哈希表 分治 桶排序 计数 快速选择 排序 堆(优先队列)

方法一:堆排序
采用Java内置的PriorityQueue进行堆排序,还是需要先用HashMap来记录值和值出现的次数,然后新建一个排序列表queue。
然后遍历HashMap,直到queue的大小为k,然后不断取queue中最小的和继续遍历的map中的元素比较,小了替换,这样就能保持queue总能保持遍历到当前时刻,前k个频率最高的元素。

public int[] topKFrequent(int[] nums, int k) {
        //方法一:堆排序
        /*Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0)+1);
        }
        //建立一个用于排序的优先队列,因为这个队列是通过小顶堆的形式实现的,所以可以降低算法的复杂度
        //而且我们可以在里面设置排序规则
        //queue中的数组int[0]代表这个数,int[1]代表出现的次数
        //从小到大进行排列
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[1]-o2[1];
            }
        });
        //Entry是Map中的一个内部类
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            int num = entry.getKey();
            int count = entry.getValue();
            //这样会永远保留前k个元素
            if(queue.size() == k){
                if(queue.peek()[1] < count){
                    queue.poll();
                    queue.offer(new int[]{num, count});
                }
            }else{
                queue.offer(new int[]{num, count});
            }
        }
        int[] ret = new int[k];
        for (int i = 0; i < k; ++i) {
            ret[i] = queue.poll()[0];
        }
        return ret;
        }

方法二:桶排序
构建一个列表数组,用索引来表示出现的次数,然后相同次数的元素添加到相同索引下的列表中。

public int[] topKFrequent(int[] nums, int k) {
        //方法二:桶排序
        //用来存结果
        int[] ret = new int[k];
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums) {
            map.put(num, map.getOrDefault(num, 0)+1);
        }
        //桶排序,将频率作为数组下标,对于出现频率不同的数字集合,存入对应的数组下标
//        List<Integer> list = new ArrayList<>(nums.length + 1);
        //建立一个集合数组,里面每个元素都是一个集合
        List<Integer>[] lists = new List[nums.length+1];
        for (Map.Entry<Integer, Integer> entry: map.entrySet()){
            int num = entry.getKey();
            int count = entry.getValue();
            if(lists[count] == null){
                lists[count] = new ArrayList<>();
            }
            lists[count].add(num);
        }
        //倒序遍历数组获取出现顺序从大到小进行排列
        int m = 0;
        for (int i = lists.length-1; i >= 0 && m<k; i--) {
            if(lists[i] == null)continue;
            for (int j = 0; j < lists[i].size(); j++) {
                ret[m++] = lists[i].get(j);
            }
        }
        return ret;
    }

2、字符串解码

//给定一个经过编码的字符串,返回它解码后的字符串。
//
// 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
//
// 你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
//
// 此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
//
//
//
// 示例 1:
//
// 输入:s = “3[a]2[bc]”
//输出:“aaabcbc”
//
//
// 示例 2:
//
// 输入:s = “3[a2[c]]”
//输出:“accaccacc”
//
//
// 示例 3:
//
// 输入:s = “2[abc]3[cd]ef”
//输出:“abcabccdcdcdef”
//
//
// 示例 4:
//
// 输入:s = “abc3[cd]xyz”
//输出:“abccdcdcdxyz”
//
// Related Topics 栈 递归 字符串

现在主要需要判断遇到每种字符应该如何处理,遇到数字字符记得一定要取整体,比如出现“23”不能拆成“2”,“3”,遇到字母或者’[‘直接进栈,遇到‘]’就去找最近的一个’[‘然后找’['之前的那个数字,重复相应的次数。

class Solution {
        int ptr;

        public String decodeString(String s) {
            LinkedList<String> stk = new LinkedList<String>();
            ptr = 0;

            while (ptr < s.length()) {
                char cur = s.charAt(ptr);
                if (Character.isDigit(cur)) {
                    // 获取一个数字并进栈
                    String digits = getDigits(s);
                    stk.addLast(digits);
                } else if (Character.isLetter(cur) || cur == '[') {
                    // 获取一个字母并进栈
                    stk.addLast(String.valueOf(s.charAt(ptr++)));
                } else {
                    ++ptr;
                    LinkedList<String> sub = new LinkedList<String>();
                    while (!"[".equals(stk.peekLast())) {
                        sub.addLast(stk.removeLast());
                    }
                    Collections.reverse(sub);
                    // 左括号出栈
                    stk.removeLast();
                    // 此时栈顶为当前 sub 对应的字符串应该出现的次数
                    int repTime = Integer.parseInt(stk.removeLast());
                    StringBuffer t = new StringBuffer();
                    String o = getString(sub);
                    // 构造字符串
                    while (repTime-- > 0) {
                        t.append(o);
                    }
                    // 将构造好的字符串入栈
                    stk.addLast(t.toString());
                }
            }

            return getString(stk);
        }

        public String getDigits(String s) {
            StringBuffer ret = new StringBuffer();
            while (Character.isDigit(s.charAt(ptr))) {
                ret.append(s.charAt(ptr++));
            }
            return ret.toString();
        }

        public String getString(LinkedList<String> v) {
            StringBuffer ret = new StringBuffer();
            for (String s : v) {
                ret.append(s);
            }
            return ret.toString();
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值