1、题目描述
2、解题思路
先统计所有字符出现的次数,存入一个 HashMap 中,接着按照重复次数 (char,count) 从大到小构建一个大顶堆,把 map 中的元素装入大顶堆中。
创建一个 StringBuilder 对象 sb。
创建一个普通队列变量 queue 同步记录已经加入到 sb 中的字符。
循环遍历大顶堆的字符:
1、取出一个字符 c ,把它加入到 sb 中;
2、把 c 的重复次数减一,加入到一个普通队列 queue 中;
3、判断 queue 中的元素个数是否为 k,如果是,说明 sb 中距离上一个字符 c 已经插入了 k 个字符了,现在字符 c 可以再出现一次了,把 queue 队首元素放入大顶堆。
当大顶堆没有元素了:
1、sb 的长度和 s 一样,说明重构完成,返回 sb.toString();
2、sb 的长度和 s 不一样,说明还有些字符挂在 queue 中,没有装入大顶堆,说明有些字符无法间隔 k 个距离。
3、解题代码
class Solution {
public String rearrangeString(String s, int k) {
if (k <= 1) {
return s;
}
HashMap<Character, Integer> map = new HashMap<>();
// 大顶堆
PriorityQueue<Map.Entry<Character, Integer>> maxHeap = new PriorityQueue<>((a, b) -> b.getValue() - a.getValue());
for (Character c : s.toCharArray()) {
// 遍历字符,统计字符的出现次数
map.put(c, map.getOrDefault(c, 0) + 1);
}
maxHeap.addAll(map.entrySet()); // 装入大顶堆,按照字符重复次数作为比较
StringBuilder sb = new StringBuilder(s.length());
Queue<Map.Entry<Character, Integer>> queue = new LinkedList<>();
while (!maxHeap.isEmpty()) {
Map.Entry<Character, Integer> currentEntry = maxHeap.poll(); // 从大顶堆取出重复次数最多的字符
sb.append(currentEntry.getKey());
currentEntry.setValue(currentEntry.getValue() - 1); // 用掉一个字符,次数减一
queue.offer(currentEntry); // 放入到queue中,因为k距离后还要用。
if (queue.size() == k) {
// queue的大小到达了k,也就是说我们已经越过了k个单位,在结果中应该要出现相同的字母了
Map.Entry<Character, Integer> entry = queue.poll();
if (entry.getValue() > 0) {
// 该字符的重复次数大于 0,则添加入大顶堆中,要是0那还加它干嘛
maxHeap.add(entry);
}
}
}
// 退出 while 循环就是大顶堆已经没有元素了,如果此时 sb 的长度没有还原,说明还有元素挂在 queue 中
// 即 queue.size() == k 这个条件没有完全满足,即存在一些字符无法间隔 k 个距离
return sb.length() == s.length() ? sb.toString() : "";
}
}