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();
}
}