0、关于集合知识的温故知新
TreeSet add源码分析:
package collection;
import java.util.Comparator;
import java.util.TreeSet;
/*
* @author lzy
* @version 1.0
* */
public class TreeSet_ {
public static void main(String[] args) {
//无参构造treeset方法,打印出的元素依旧是无序的。
//添加的元素,按照字符串大小来排序
//3. 使用treeSet提供的构造器可以传入一个比较器(匿名内部类 并指定排序规则
/*public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>( comparator));
} */
/*
public interface Comparator<T> {
int compare(T o1, T o2);
* */
TreeSet treeSet=new TreeSet();
/*TreeSet treeSet=new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String)o1).compareTo((String)o2);
// return ((String)o1).length()-((String)o2).length(); 长度
}
});*/
//1. 构造器:将传入的比较器,赋给了TreeMap底层的comparator属性
/* public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}*/
//2.底层源码:cpr这就是传入的底层的匿名内部类比较器 底层思想就是 比较然后选择应该找树的左边还是右边,然后更新parent,然后循环遍历整棵树,最后将key-value-parent传入构造新的树节点
/*
* Comparator<? super K> cpr = comparator;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);//动态绑定道匿名内部类(对象)所实现的接口方法。
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);//如果相等则key加不进去
} while (t != null);
}
else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;//这里是将Key进行一个向上转型,方便调用父类接口的方法
* //原因就是这里key是一个String类型,但是String类型源码中是实现了Comparable接口的所以可以转型,不行你去看String源码
* //https://blog.csdn.net/weixin_43971252/article/details/119396327 从如果传入不实现Comparable的类,我们也有解决方法
* //1. 实现Comparable接口 2. 传入Comparable匿名内部类
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
* */
/*
* public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
* */
// treeSet.add("hbm");
// treeSet.add("hlm");
// treeSet.add("xbm");
treeSet.add(1231231312);
treeSet.add(12312321);
System.out.println(treeSet);
//
}
}
一、2335. 装满杯子需要的最短总时长
题目描述:
现有一台饮水机,可以制备冷水、温水和热水。每秒钟,可以装满 2 杯 不同 类型的水或者 1 杯任意类型的水。
给你一个下标从 0 开始、长度为 3 的整数数组 amount ,其中 amount[0]、amount[1] 和 amount[2] 分别表示需要装满冷水、温水和热水的杯子数量。返回装满所有杯子所需的 最少 秒数。
输入:amount = [5,4,4]
输出:7
解释:下面给出一种方案:
第 1 秒:装满一杯冷水和一杯热水。
第 2 秒:装满一杯冷水和一杯温水。
第 3 秒:装满一杯冷水和一杯温水。
第 4 秒:装满一杯温水和一杯热水。
第 5 秒:装满一杯冷水和一杯热水。
第 6 秒:装满一杯冷水和一杯温水。
第 7 秒:装满一杯热水。
代码思路:
贪心,每次贪心的选取两杯水来喝,如果发现杯数最多的水,比其他两杯总数加起来还多,那么返回杯数最多的水的数量。
代码题解:
class Solution {
//数字数量少了,用冒泡速度很快。
private int[] bubbleSort(int[] amount) {
for(int i = 0; i < amount.length - 1; i++) {
for(int j = i + 1; j < amount.length; j++) {
if(amount[j - 1] > amount[j]) {
int temp = amount[j];
amount[j] = amount[j - 1];
amount[j - 1] = temp;
}
}
}
return amount;
}
public int fillCups(int[] amount) {
//4 4 3 ,3 3 3,2 2 3, 2 1 2,1 1 1,0 0 1,1
if(amount.length == 0) return 0;
int res = 0;
bubbleSort(amount);
while(amount[2] > 0){
if(amount[0] + amount[1] <= amount[2]) {
return res + amount[2];
}else {
amount[2]--;
amount[1]--;
res++;
}
bubbleSort(amount);
}
return res;
}
}
二、2336. 无限集中的最小数字
现有一个包含所有正整数的集合 [1, 2, 3, 4, 5, …] 。
实现 SmallestInfiniteSet 类:
SmallestInfiniteSet() 初始化 SmallestInfiniteSet 对象以包含 所有 正整数。
int popSmallest() 移除 并返回该无限集中的最小整数。
void addBack(int num) 如果正整数 num 不 存在于无限集中,则将一个 num 添加 到该无限集中。
输入
[“SmallestInfiniteSet”, “addBack”, “popSmallest”, “popSmallest”, “popSmallest”, “addBack”, “popSmallest”, “popSmallest”, “popSmallest”]
[[], [2], [], [], [], [1], [], [], []]
输出
[null, null, 1, 2, 3, null, 1, 4, 5]
解释
SmallestInfiniteSet smallestInfiniteSet = new SmallestInfiniteSet();
smallestInfiniteSet.addBack(2); // 2 已经在集合中,所以不做任何变更。
smallestInfiniteSet.popSmallest(); // 返回 1 ,因为 1 是最小的整数,并将其从集合中移除。
smallestInfiniteSet.popSmallest(); // 返回 2 ,并将其从集合中移除。
smallestInfiniteSet.popSmallest(); // 返回 3 ,并将其从集合中移除。
smallestInfiniteSet.addBack(1); // 将 1 添加到该集合中。
smallestInfiniteSet.popSmallest(); // 返回 1 ,因为 1 在上一步中被添加到集合中,
// 且 1 是最小的整数,并将其从集合中移除。
smallestInfiniteSet.popSmallest(); // 返回 4 ,并将其从集合中移除。
smallestInfiniteSet.popSmallest(); // 返回 5 ,并将其从集合中移除。
代码思路:
- 由于题目数据量只有1000个,所以手动添加1000个数模拟无限集合。
- 根据题目的要求:
- 需要返回最小整数(表示我的集合得有序)。
- 同时进行添加操作,添加数字之后,也要保证有序。
- 无需添加null值(TreeSet 底层代码不让加null值)
- 根据上述题目的要求,TreeSet比较符合,但是如果使用HashMap的话,由于是按照递增数字来添加的,所以,通过hashCode加入指定位置元素,碰巧也是有序的,所以用HashMap也可以。
代码题解:
package leetcode;
import java.util.TreeSet;
/*
* @author lzy
* @version 1.0
* */
public class SmallestInfiniteSet {
public static void main(String[] args) {
new SmallestInfiniteSet();
}
private TreeSet<Integer> treeSet;
public SmallestInfiniteSet() {
this.treeSet = new TreeSet();
for (int i = 1; i < 1001; i++) {
treeSet.add(i);
}
}
public int popSmallest() {
int res = treeSet.first();
treeSet.remove(treeSet.first());
return res;
}
public void addBack(int num) {
if (treeSet.contains(num)) {
return;
} else {
treeSet.add(num);
}
}
}
/**
* Your SmallestInfiniteSet object will be instantiated and called as such:
* SmallestInfiniteSet obj = new SmallestInfiniteSet();
* int param_1 = obj.popSmallest();
* obj.addBack(num);
*/
时间复杂度: O(NLOGN)
主要时间开销来自于对二叉树的排序,以及底层红黑树的维护。
三、2337. 移动片段得到字符串
没做 (饿的一批,有时间看大佬题解吧)
四、2338. 统计理想数组的数目
hard难度,劝退 (饿的一批,有时间看大佬题解吧)