题目链接
- 牛客:字符统计
- LeetCode:373. 查找和最小的 K 对数字
字符统计
- 输入
输入一个只包含小写英文字母和数字的字符串
- 输出
一个字符串,为不同字母出现次数的降序表示。
若出现次数相同,则按ASCII码的升序输出。
- 测试用例
输入:
aaddccdc
输出:
cda
解释
c 和 d 出现 3 次,a 出现两次,而 c 的 ascii 排序在 d 前面,所以输出 cda;
查找和最小的 k 对数字
- 输入
给定两个以升序排列的整形数组 nums1 和 nums2, 以及一个整数 k。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2。
- 输出
找到和最小的 k 对数字 (u1,v1), (u2,v2) … (uk,vk)。
- 测试用例
输入:
nums1 = [1,7,11], nums2 = [2,4,6], k = 3
输出:
[1,2],[1,4],[1,6]
解释:
返回序列中的前 3 对数:
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]
基本思路与代码展示
字符统计
思路
常见的排序都不陌生,但是我们平时的排序都只是 针对一个元素 进行比较,而这两个题都是需要 对两个元素进行比较,单纯的使用常见的排序来完成会比较麻烦。
- 字符集中存在重复元素,使用
HashMap
来提取出字符串中不重复的元素与该元素出现的次数; - 定义 节点 来存储元素的值与出现的次数;
- 使用
List
来存储HashMap
中得到的结果; - 排序;
- 输出结果。
代码
使用普通排序
- 这个是我一开始刷题的时候写的代码,没有使用到
Comparator
,而是使用了 优化的冒泡排序。
// 2、 定义节点
static class Node {
char key;
int val;
public Node(char key,int val) {
this.key = key;
this.val = val;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String s = sc.next();
ArrayList<Node> list = new ArrayList<>();
Map<Character,Integer> map = new HashMap<>();
// 1、使用 map 提取出字符串中字符与出现的次数
for (int i = 0; i < s.length(); i++) {
map.put(s.charAt(i),map.getOrDefault(s.charAt(i),0)+1);
}
// 3、遍历 map, 将该结果存储在 list 中
for (Map.Entry<Character,Integer> entry : map.entrySet()) {
list.add(new Node(entry.getKey(),entry.getValue()));
}
// 4、根据 先根据node.val 排序 如果一样,根据 key 排序
for (int i = 0; i < list.size(); i++) {
boolean isSorted = true;
for (int j = 0; j < list.size()-1-i; j++) {
if (list.get(j).val > list.get(j+1).val) {
Node node1 = list.get(j);
Node node2 = list.get(j+1);
list.set(j,node2);
list.set(j+1,node1);
isSorted = false;
}else if (list.get(j).val == list.get(j+1).val) {
// 如果前面 ascii 码值小于后面 ascii 码值
if (list.get(j).key < list.get(j+1).key) {
Node node1 = list.get(j);
Node node2 = list.get(j+1);
list.set(j,node2);
list.set(j+1,node1);
isSorted = false;
}
}
}
// 优化
if (isSorted) {
break;
}
}
// 5、得到结果
String res = "";
for (int i = list.size()-1; i >= 0; i--) {
res += list.get(i).key;
}
System.out.println(res);
}
}
使用 Comparator
import java.util.*;
public class Main {
// 2、 定义节点
static class Node {
char key;
int val;
public Node(char key,int val) {
this.key = key;
this.val = val;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
String s = sc.next();
ArrayList<Node> list = new ArrayList<>();
Map<Character,Integer> map = new HashMap<>();
// 1、使用 map 提取出字符串中字符与出现的次数
for (int i = 0; i < s.length(); i++) {
map.put(s.charAt(i),map.getOrDefault(s.charAt(i),0)+1);
}
// 3、遍历 map, 将该结果存储在 list 中
for (Map.Entry<Character,Integer> entry : map.entrySet()) {
list.add(new Node(entry.getKey(),entry.getValue()));
}
// 4、 使用 Comparator 排序
// o1 代表当前 o2 代表下一个
Collections.sort(list, new Comparator<Node>() {
@Override
public int compare(Node o1, Node o2) {
// 当前次数小于下一个 次数是降序排序 说明要交换
if (o1.val < o2.val) {
return 1;
}
// 次数相等
else if (o1.val == o2.val) {
// ascii 是升序 如果当前 ascii 大于下一个 交换
if (o1.key > o2.key) {
return 1;
}
}
// 次数大于 不交换
return -1;
}
});
// 5、得到结果
String res = "";
for (int i = 0; i < list.size(); i++) {
res += list.get(i).key;
}
System.out.println(res);
}
}
}
查找和最小的 k 对数字
思路
- 利用 TopK 思路来解决这个问题
- 建立一个 容量为 k 的大堆(查找前 k 个最小建大堆,前 k 个最大建小堆);
- 先添加 k 个元素进入堆中,确保堆顶最大;
- 第 k+1 个元素开始就需要与堆顶元素进行比较,如果比堆顶小,则交换,反之不交换。
- 关键问题
- 主要问题就出现在如何建立这个 容量为K 的大堆,毕竟是两个数组之和;
- 结合上面使用 节点 来保存两个值,我们可以使用 二维的数据结构 来解决。
代码
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
PriorityQueue<List<Integer>> pr = new PriorityQueue<>(k,new Comparator<List<Integer>>() {
@Override
public int compare(List<Integer> o1, List<Integer> o2) {
// 如果下一对的和大于当前这一对,那么交换
return (o2.get(0) + o2.get(1)) - (o1.get(0) + o1.get(1));
}
});
for (int i = 0; i < Math.min(nums1.length,k); i++) {
for (int j = 0; j < Math.min(nums2.length,k); j++) {
if (pr.size() < k) {
List<Integer> list = new ArrayList<>();
// 添加第一个元素的 i 下标的值
list.add(nums1[i]);
// 添加第二个元素的 j 下表的值
list.add(nums2[j]);
// 堆添加 list
pr.offer(list);
}else {
// 得到堆顶和最大值
int max = pr.peek().get(0) + pr.peek().get(1);
// 判断是否需要交换
if (max > nums1[i] + nums2[j]) {
pr.poll();
List<Integer> list = new ArrayList<>();
list.add(nums1[i]);
list.add(nums2[j]);
pr.offer(list);
}
}
}
}
// 现在堆中的值是前 k 个最小的 遍历堆即可
List<List<Integer>> res = new ArrayList<>();
while (!pr.isEmpty()) {
res.add(pr.poll());
}
return res;
}