文章目录
零、 快查
一、 HashMap和HashSet
HashMap
映射(Map接口,将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值)性质:使用健值对的方式存储数据 <Key,Value>
Hashtable,此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
HashMap,基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)
LinkedHashMap,Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序,为插入顺序。
TreeMap,基于红黑树,该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
初始化
<Key, Value> key和value是任何Collection或任何Object
Map<Characters, Integer> map = new HashMap<>();
方法
map中是否包含key:boolean isContain = map.containsKey(key);
map中是否存在value:
map.containsValue(V value); // 在Map中若存在value,则返回true,否则返回false --- O(1)
map的put()将键值对<K,V>放入map, map.putAll() :map.put(key,value);
map的get()获得指定key值对应键值:Object value = map.get(key);
map的getOrDefault(): map.getOrDefault(key,default);//没找到就return default
# hashmap的getOrDefault方法
map.getOrDefault(num,0) //判断是否有key = num的值,有的话返回对应的value,没有的话返回0
map.keySet返回一个Set:
map.keySet(); // 返回一个Set,这个Set中包含Map中所有的Key --- O(1)
map.values返回一个Collection<v>,里面全是对应的每一个value:
map.values(); // 返回一个Collection<v>,里面全是对应的每一个value --- O(1)
map的遍历:for(Object key : map.keySet()){ //... }
// For example:
// We want to get all values in Map
// Map<Character, Integer> map = new HashMap<>();
for (Integer value : map.values()) {
// Operate with each values
}
map删除指定键值:map.remove(Object key)
map.isEmpty判断是不是空:
map.isEmpty() // 若Map为空返回true, 否则返回false --- O(1)
map.size返回Map中中键值对<K, V>的个数:
map.size() // 返回Map中中键值对<K, V>的个数 --- O(1)
1 void clear( )
从此映射中移除所有映射关系(可选操作)。
2 boolean containsKey(Object k)
如果此映射包含指定键的映射关系,则返回 true。
3 boolean containsValue(Object v)
如果此映射将一个或多个键映射到指定值,则返回 true。
4 Set entrySet( )
返回此映射中包含的映射关系的 Set 视图。
5 boolean equals(Object obj)
比较指定的对象与此映射是否相等。
6 Object get(Object k)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
7 int hashCode( )
返回此映射的哈希码值。
8 boolean isEmpty( )
如果此映射未包含键-值映射关系,则返回 true。
9 Set keySet( )
返回此映射中包含的键的 Set 视图。
10 Object put(Object k, Object v)
将指定的值与此映射中的指定键关联(可选操作)。
11 void putAll(Map m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
12 Object remove(Object k)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
13 int size( )
返回此映射中的键-值映射关系数。
14 Collection values( )
返回此映射中包含的值的 Collection 视图。
遍历map
方法一 在for-each循环中使用entries来遍历
这是最常见的并且在大多数情况下也是最可取的遍历方式。在键值都需要时使用。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
}
注意:for-each循环在java 5中被引入所以该方法只能应用于java 5或更高的版本中。如果你遍历的是一个空的map对象,for-each循环将抛出NullPointerException,因此在遍历前你总是应该检查空引用。
方法二 在for-each循环中遍历keys或values。
如果只需要map中的键或者值,你可以通过keySet或values来实现遍历,而不是用entrySet。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//遍历map中的键
for (Integer key : map.keySet()) {
System.out.println("Key = " + key);
}
//遍历map中的值
for (Integer value : map.values()) {
System.out.println("Value = " + value);
}
该方法比entrySet遍历在性能上稍好(快了10%),而且代码更加干净。
方法三、通过键找值遍历(效率低)
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer key : map.keySet()) {
Integer value = map.get(key);
System.out.println("Key = " + key + ", Value = " + value);
}
作为方法一的替代,这个代码看上去更加干净;但实际上它相当慢且无效率。因为从键取值是耗时的操作(与方法一相比,在不同的Map实现中该方法慢了20%~200%)。如果你安装了FindBugs,它会做出检查并警告你关于哪些是低效率的遍历。所以尽量避免使用。(但是刷题记不住第一个可以用)
所有的map对key和value进行排序
HashSet
集合(Set接口是一个不包含重复元素的集合)性质:Set中没有重复元素,重复添加的元素抛弃
HashSet,此类实现 Set 接口,由哈希表支持,元素存储迭代没有固定顺序
LinkedHashSet,具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现,顺序为插入顺序
TreeSet,元素是内部排序的,使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
导入库java.util.HashSet
import java.util.HashSet;
若失败则再导入java.util.*
HashSet声明并创建对象
第一个<>里添加集合元素的类型
可以省略<>,有时可能报错但能运行
setname可以更改变量名
初始化
HashSet<> setname = new HashSet<>();
方法
.add()
增加元素
成功添加返回true
.contains()
判断是否存在一个元素
返回true或者false
.remove()
删除一个制定元素
若存在,并删除成功则返回true
.isEmpty()
判断集合是否为空
.clear()
清空集合
但没有删除对象
.size() // 返回集合中中元素个数 --- O(1)
二、Stack s = new Stack<>()
性质: 先进后出
初始化 (唯一初始化方式)
Stack<Integer> stack = new Stack<>();
方法
入栈:stack.push(Object o)// 入栈元素o, 返回值为元素o --- O(1)
出栈:stack.pop()// 出栈一个元素,返回出栈元素o --- O(1)
返回栈顶:stack.peek()// 查看栈顶元素, 返回值为栈顶元素o --- O(1)
栈空:stack.isEmpty()// 若栈空返回true, 否则返回false --- O(1)
元素个数:stack.size() // 返回栈中元素个数 --- O(1)
Stack栈的遍历
在遍历集合时候,优先考虑使用foreach语句来做,这样代码更简洁些。
public class TestStack {
public static void main(String[] args) {
Stack<Integer> s = new Stack<Integer>();
for (int i = 0; i < 10; i++) {
s.push(i);
}
//集合遍历方式
for (Integer x : s) {
System.out.println(x);
}
System.out.println("------1-----");
// 栈弹出遍历方式
// while (s.peek()!=null) { //不健壮的判断方式,容易抛异常,正确写法是下面的
while (!s.empty()) {
System.out.println(s.pop());
}
System.out.println("------2-----");
// 错误的遍历方式
// for (Integer x : s) {
// System.out.println(s.pop());
// }
}
}
三、 Queue queue = new LinkedList();
性质:先进先出
通过实现实现队列接口的LinkedList<>();
初始化
使用LinkedList实现Queue接口初始化
Queue<Integer> q = new LinkedList<>();
方法
当栈用:
1 Object peek( )
查看堆栈顶部的对象,但不从堆栈中移除它。
2 Object pop( )
移除堆栈顶部的对象,并作为此函数的值返回该对象。
3 Object push(Object element)
把项压入堆栈顶部。
当队列用:
//add()和remove()方法在失败的时候会抛出异常(不推荐)
queue.add(E e)和queue.offer(E e):在队尾添加元素,当容量已满时,add() 方法会抛出IllegalStateException异常,offer() 方法只会返回 false 。
queue.remove()和queue.poll():都是都是返回队头元素,并在队列中删除返回的对象。不同点:如果没有元素remove()会直接抛出 NoSuchElementException 异常,而poll()会返回 null。
queue.offer(E e); // 队尾加入元素e。 若成功入队返回值true,否则返回false --- O(1)
queue.poll(); //返回第一个元素,并在队列中删除--- O(1)
queue.element(); //返回第一个元素
queue.peek(); //返回第一个元素, 查看队首元素----O(1)
queue.isEmpty() // 若队空返回true, 否则返回false --- O(1)
queue.size() // 返回队中元素个数 --- O(1)
从头部插入:
addFirst()//将指定的元素插入此双端队列的前面 ,空间不足抛异常
offerFirst()//空间不足插入失败返回回false
push()//空间不足抛异常
从尾部插入
add()//将指定的元素插入此双端队列的后面 ,空间不足抛异常
offer()//空间不足返回false
addLast()//同add()
offerLast()//同offer()
从头部删除:
E removeFirst()//检索并删除第一个元素,为空时抛出异常
E remove()//同removeFirst
E pop()//同removeFirst
E poll()//检索并删除第一个元素 ,为空时返回null
E pollFirst()//同poll
从尾部删除
E removeLast()//检索并删除最后一个元素,为空时抛出异常
E pollLast()//检索并删除最后一个元素 ,为空时返回null
检索但不删除
E getFirst()//检索但不删除第一个元素,为空就抛异常
E getLast()//检索不删除最后一个元素,为空就抛异常
E peek() peekFirst()//检索但不删除第一个元素,为空返回null
E peekLast()//检索但不删除最后一个元素,为空返回null
迭代器
Iterator<> iterator()
Queue队列的遍历
public class TestQueue {
public static void main(String[] args) {
Queue<Integer> q = new LinkedBlockingQueue<Integer>();
//初始化队列
for (int i = 0; i < 5; i++) {
q.offer(i);
}
System.out.println("-------1-----");
//集合方式遍历,元素不会被移除
for (Integer x : q) {
System.out.println(x);
}
System.out.println("-------2-----");
//队列方式遍历,元素逐个被移除
while (q.peek() != null) {
System.out.println(q.poll());
}
}
}
优先队列
PriorityQueue,一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序。元素小的优先级高,输出时先输出。实现Queue接口
实现大小根堆
// 默认是小根堆,实现大根堆需要重写一下比较器。
//实现大根堆
Queue<Integer> pq = new PriorityQueue<>((v1, v2) -> v2 - v1);
PriorityQueue小根堆与大根堆
优先队列 PriorityQueue (Heap)
性质:底层是一颗数, 以小根堆为例。对于任意结点来说,该节点的值比其左右孩子的值都要小。 (就是最上面的结点最小)。 大根堆类似,最上面结点最大
初始化
小根堆
Queue<Integer> minH = new PriorityQueue<>(); // 小根堆,默认大小为11 相当于 new PriorityQueue<>(11)
Queue<Integer> minH = new PriorityQueue<>(100); // 定义一个默认容量有100的小根堆。在当中增加元素会扩容,只是开始指定大小。不是size,是capacity
大根堆
Queue<Integer> maxH = new PriorityQueue<>((i1, i2) -> i2 - i1); // 大根堆,默认大小为11 相当于 new PriorityQueue<>(11, (i1, i2) -> i2 - i1)
Queue<Integer> maxH = new PriorityQueue<>(100, (i1, i2) -> i2 - i1); // 定义一个默认容量有100的大根堆。在当中增加元素会扩容,只是开始指定大小
比较器
Comparator<Object> cmp = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
//升序
return o1-o2;
//降序
return o2-o1;
}
};
一句话,返回负数,第一个参数放前面
comparator反应两个参数的权重。1是前者权重大,-1是后者。最后按照权重由小到大排序。 o1=4, o2=6, 返回1表示4权重大,权重默认升序最后4排后面即数值降序。
比较器
方法
优先队列
Queue<Integer> q = new PriorityQueue<>();默认 小根堆
peek()//不弹 返回队首元素
poll()//弹出 返回队首元素
add() offer() //添加元素,前者(add)在插入失败时抛出异常,后者(offer)则会返回false。
size()//返回队列元素个数
isEmpty()//判断队列是否为空,为空返回true,不空返回false
PriorityQueue<Integer> queue = new PriorityQueue<Integer>(new Comparator<Integer>() {
@Override
public int compare(Integer num1, Integer num2) {
return num1 - num2;//升序 小顶堆
return num2 - num1;//降序 大顶堆
}
});
技巧
从小到大(或从大到小弹出元素)
while (!pq.isEmpty()) {}
四、List<> l = new ArrayList<>();(动态数组)
性质: 可以动态扩容的数组
线性表(List接口,有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。与 set 不同,列表通常允许重复的元素。)
Stack,堆栈
Vector, 动态数组
ArrayList, 实现了List接口的动态数组
LinkedList, List 接口的链接列表实现,包含队列、双端队列的API,同时实现Queue接口
初始化
常规 - ArrayList更常用
List<Integer> array = new ArrayList<>(); // 数组
List<Integer> list = new LinkedList<>(); // 链表
方法(List接口方法)
方法:get, size, add, remove, subList
get
.get(int index) // 返回元素位置在index的元素e --- array O(1), list O(n)
size
.size() // 返回数组长度 --- O(1)
add
.add(E e) // 在尾部添加一个元素e --- O(1)
.add(int index, E e) // 在index位置插一个元素e --- O(n)
remove
.remove(int index) // 删除位于index的元素,并返回删除元素e --- 删除最后元素为O(1), 其余为O(n)
//删除最后元素 list.remove(list.size() - 1);
subList
.subList(int from, int to) // 相当于返回原数组的一个片段,但不要对其进行改动,改动会影响原数组 --- O(1)
// List<Integer> list, 对原来的list和返回的list做的“非结构性修改”(non-structural changes),
//都会影响到彼此对方. 如果你在调用了sublist返回了子list之后,如果修改了原list的大小,那么之前产生的子list将会失效,变得不可使用
技巧
Collections.sort(list); 从小到大排序
Collections.sort(list, (o1, o2) -> o2 - o1); 从大到小排序, 第二个参数为一个比较器
Collections的reverse方法()、sort()方法
// reverse(list) 反转
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(3);
list.add(1);
list.add(2);
Collections.reverse(list); // 使用Collections的reverse方法,直接将list反转
// ---> list = {2, 1, 3}
// sort() 升序排序
Collections.sort(list); // ---> list = {1, 2, 3}
// 升序在反转就相当于降序
.List<int[ ]> list = new ArrayList<>();
//将 list 返回为二维数组
return list.toArray(new int[0][]);
//进行段位截取
list.subList(int start,int end);
//集合转数组
list.toArray(new String[list.size()])
//返回ArrayList的实际大小
public int size()
//判断ArrayList是否为空
public boolean isEmpty()
//判断ArrayList是否包含元素o
public boolean contains(Object o)
//正向查找,返回元素的索引值
public int indexOf(Object o)
//反向查找,返回元素的索引值
public int lastIndexOf(Object o)
//获取index位置的元素
public E get(int index)
//将e添加到ArrayList末尾
public boolean add(E e)
//删除ArrayList指定位置的元素
public E remove(int index)
//删除ArrayList中指定的元素
public boolean remove(Object o)
//清空ArrayList,将全部元素置为null
public void clear()
//将集合C中的所有元素添加到ArrayList中
public boolean addAll(Collection<? extends E> c)
五、 Arrays(静态数组)
初始化
一维
// Type[] names = new Type[capacity];
int[] a = new int[10];
String[] s = new String[3];
// Type[] names = new Type[]{Tpye ...a};
int[] a = new int[]{1, 2, 3, 4}; // int[] a = {1, 2, 3, 4};
char[] b = new char[]{'a', 'b'}; // char[] b = {'a', 'b'};
String[] s = new String[]{"hello", "world"};
// 创建
Set<String> set = new Set[105]; // 每一个指向一个null
for (int i = 0; i < 105; i++) jud[i] = new HashSet<>(); // 每个Set现在才被创建出来
二维
// 二维
int[][] c = new int[10][10];
方法
1、填充数组:fill
eg1:
int []arr = new int[5];
Arrays.fill(arr, 2);
output(arr);
结果是:2 2 2 2 2
eg2:
int []arr = new int[5];
Arrays.fill(arr, 1,3,8);
output(arr);
结果是:0 8 8 0 0
给第1位(0开始)到第3位(不包括)赋值8
2、数组元素排序:sort
eg1:
int []arr = {3,2,1,5,4};
Arrays.sort(arr);
output(arr);
结果是:1 2 3 4 5
eg2:
int []arr = {3,2,1,5,4};
Arrays.sort(arr,1,3);
output(arr);
结果是:3 1 2 5 4
分析:给第1位(0开始)到第3位(不包括)排序
3、比较数组元素是否相等:equals
int []arr1 = {1,2,3};
int []arr2 = {1,2,3};
System.out.println(Arrays.equals(arr1,arr2));
结果是:true
如果是arr1.equals(arr2),则返回false,因为equals比较的是两个对象的地址,不是里面的数,而Arrays.equals重写了equals,所以,这里能比较元素是否相等。
4、二分查找法找指定元素的索引值(下标):binarySearch
eg1:
int []arr = {10,20,30,40,50};
System.out.println(Arrays.binarySearch(arr, 20));
结果是:1
分析:能找到该元素,返回下标为1(0开始)
eg2:
int []arr = {10,20,30,40,50};
System.out.println(Arrays.binarySearch(arr, 35));
结果是:-4
分析:找不到元素,返回-x,从-1开始数,如题,返回-4
eg3:
int []arr = {10,20,30,40,50};
System.out.println(Arrays.binarySearch(arr, 0,3,30));
结果是:2
分析:从0到3位(不包括)找30,找到了,在第2位,返回2
eg4:
int []arr = {10,20,30,40,50};
System.out.println(Arrays.binarySearch(arr, 0,3,40));
结果是:-4
分析:从0到3位(不包括)找40,找不到,从-1开始数,返回-4
5、截取数组:copeOf和copeOfRange
eg1: copy
int []arr = {10,20,30,40,50};
int []arr1 = Arrays.copyOf(arr, 3);
output(arr1);
结果:10 20 30
分析:截取arr数组的3个元素赋值给姓数组arr1
eg2: copyOfRange
int []arr = {10,20,30,40,50};
int []arr1 = Arrays.copyOfRange(arr,1,3);
output(arr1);
结果:20 30
分析:从第1位(0开始)截取到第3位(不包括)
Arrays.copyOf / arr.clone()复制一个数组(二维数组也可以)
int[] a = new int[5];
int[] newA = Array.copyOf(a, 5);
// or
int[][] a = {{1}, {1,2}, {1,2,3}, {1,2,3,4}, {1,2,3,4,5}}; // 不是5*5,第一维1 2 3 4 5
int[][] newa = a.clone(); // 不是5*5矩阵
Array.Sort() 快排
Arrays.sort():排序基本对象类型(不可以是基本数据类型,int要转化为Integer),采用双轴快排实现,即两个基准元素。
默认是升序,可重写Comparator()匿名内部类的compare()方法,重新定义排序规则。
//定义规则:对字符串合并后的结果排序,升序。
Arrays.sort(str, new Comparator<String>(){
public int compare(String s1, String s2){
String a = s1 + s2;
String b = s2 + s1;
return a.compareTo(b); //a>b,返回大于0;a<b,返回小于0;相等返回0
}
});
Arrays.sort(int[] arr) Arrays.sort(int[] arr, int fromIndex, int toIndex) 数组类型只要实现了Comparable接口就行(基本数据类型int也可以)
Arrays.sort(int[] arr, int fromIndex, int toIndex, 比较器); //一定是需要泛型
Arrays.sort(arr, (o1, o2) -> o2 - o1); //数组全部 从大到小排序 跟Collections.sort()一样
Arrays.sort(arr, 0, 3, (o1, o2) -> o2 - o1); //从大到小排序,只排序[0, 3)
Arrays.sort();//n*log(n);//重载了四个方法
sort(T[] a);
sort(T[] a,int fromIndex,int toIndex);//按升序排列数组的指定范围。
sort(T[] a,Comparator<? super T> c);//根据指定比较器产生的顺序对指定对象数组进行排序。
sort(T[] a,int fromIndex,int toIndex,Comparator<? super T> c);//根据指定比较器产生的顺序对指定对象数组的指定对象数组进行排序。
一个比较复杂的例子,先按cpu升序,再按内存升序,最后按id升序。
Arrays.sort(ha,(o1,o2)->o1.cpu!=o2.cpu?(o1.cpu-o2.cpu):(o1.neicun != o2.neicun?(o1.neicun-o2.neicun):(o1.id - o2.id)));
二维数组的行和列表示
int row = grid.length; // 代表行
int col = grid[0].length; // 代表列
.length 记得是属性而不是方法 arr.length 没有()
二维数组判断为空
if (array == null|| array.length == 0 || array[0].length == 0) return false;
Java数组拷贝及遍历总结
public class ArrayCopy {
public static void main(String[] args) {
int[] source={1,5,6,8,4,5,2,565,56};
int[] target=new int[source.length];
//数组的拷贝
//1. 通过遍历赋值拷贝数组
for(int i=0;i<source.length;i++){
target[i]=source[i];
}
//2. 通过System类的arraycopy()方法
System.arraycopy(source,0,target,0,source.length);
//3. 通过Arrays类的copyOf()方法和copyOfRange()方法
target=Arrays.copyOf(source,source.length);
target=Arrays.copyOfRange(source,0,source.length);
/**
* 4. 通过Arrays类的clone方法
* 一维数组:深克隆;(重新分配空间,并将元素复制过去)
* 二维数组:浅克隆。(只传递引用)
*/
target=source.clone();
//数组的遍历
//1. 通过数组的toString方法实现数组的遍历
System.out.println(Arrays.toString(target));
//2. 通过for循环实现数组的遍历
for (int i=0;i<target.length;i++){
System.out.print(target[i]+" ");
}
System.out.println();
//3. 通过增强for实现数组的遍历
for (int i:target){
System.out.print(i+" ");
}
}
}
六、数组字符串类型转化
Integer 类
1.String -> int:int value = Integer.parseInt(str);
2.将str转换成二进制的int: int binary = Integer.parseInt(str,2);
3.十进制转二进制:String binaryN=Integer.toBinaryString(N);
进制转换:Integer.toString(int n, int radix)十进制转radix进制,Integer.parseInt(String n, int radix)radix进制转十进制(返回基本类型int)。
Integer.toOctalString()/toHexString()8/16进制。
Integer.valueOf(int n):返回包装类Integer。Integer.parseInt()返回基本类型。
String 类
性质:不可变量(相当于只读final修饰)
每个位置元素是个char
初始化
字符串复制初始化
String s = "abc";
基于另外一个字符串
// s = "abc"
String s2 = new String(s);
方法
Object -> String:String str = String.valueOf(o);
char[ ] -> String:String str = String.valueOf(charArray);
int -> String:String str = String.valueOf(N);
String -> char[ ] char[] :charStr = str.toCharArray();
str.replaceAll("a","b"):字符串替换
str.split(","):字符串分割
str.charAt(index):String -> char
str.substring(start,end):字符串子串,包含start,不包含end
str.substring(int beginIndex); : 返回字符片段[beginIndex, end_of_String) 就是从beginIndex开始后面的 ---- O(n)
str.indexOf(int ch):返回字符ch第一次出现的索引。
str.length(); :返回字符串长度 --- O(1)
indexOf 是(暴力查找字符串,不是KMP)
.indexOf(String s) // 返回str第一个出现的位置(int),没找到则返回-1。 --- O(m * n) m为原串长度, n为str长度
// (假如要找一个字符char c,str可以表示成String.valueOf(c),然后作为参数传进去.
str.indexOf(String s, int fromIndex); // 同上,但从fromIndex开始找 --- O(m * n)
lastIndexOf
.lastIndexOf(String s); // 返回str最后出现的位置(int),没找到则返回-1。 --- O(m * n) m为原串长度, n为str长度
// (假如要找一个字符char c,str可以表示成String.valueOf(c),然后作为参数传进去.
.lastIndexOf(String s, int fromIndex); // 同上,
//但从fromIndex开始从后往前找 [0 <- fromIndex] --- O(m * n)
replace 只能换char
.replace(char oldChar, char newChar); // 返回一个新字符串String,其oldChar全部变成newChar --- O(n)
toCharArray
.toCharArray(); // 返回char[] 数组。 把String编程字符数组 --- O(n)
trim 去除前后空格
.trim(); // 返回去除前后空格的新字符串 --- O(n)
split 以什么分开
.split(String regex); // 返回 String[],以regex(正则表达式)分隔好的字符换数组。 ---- O(n)
// For example
// 从非"/"算起 若"/a/c" -> 会变成"" "a" "c"
String[] date = str.split("/"); // date[0]:1995 date[1]:12 date[2]:18 --- O(n)
toLowerCase, toUpperCase 转换大小写
s = s.toLowerCase(); // 返回一个新的字符串全部转成小写 --- O(n)
s = s.toUpperCase(); // 返回一个新的字符串全部转成大写 --- O(n)
char[] —> String
通过char.toString()
char[] chr = new char[]{'a','b','c'};
chr.toString();
通过String.valueOf()
char[] chr = new char[]{'a','b','c'};
String.valueOf(chr);
其他类型转String
直接调用String.valueOf()进行转换
也使用可以使用int+“”;转成字符串
通过String.chatAt()获取下标元素
通过String.substring()截取i之后的元素
通过String.split(“”)分隔字符串得到数组
将数字转为字符串
//从速度由快到慢
Integer.toString(int i)
String.valueOf(int i )
i+""
String—>int
String a = Integer.valueOf(b);
String a = Integer.ParseInt(b);
String转int、long等
public void test() {
String sInt = "123";
int s = Integer.parseInt(sInt);
long ls = Long.parseLong(sInt);
System.out.println(s + " " + ls);
}
char–>String
String str = String.valueOf(char)
返回子字符串
S.substring( int begin ,int end)//包含begin,不包含end
字符串转换大小写,去空格
String s = s.toLowerCase();//将字符串转换为小写
String s = s.toUpperCase();//将字符串转换为大写
String s = s.trim();
字符串转字符串数组(String—>String[](或者char[])
利用String.toCharArray()实现
String str = new String("abcedfghig");
char[] chr = str.toCharArray();
或者
字符串转数组
使用Java split() 方法
split() 方法根据匹配给定的正则表达式来拆分字符串。
注意: . 、 | 和 * 等转义字符,必须得加 \。多个分隔符,可以用 | 作为连字符。
利用String.split()实现
注:在使用String.split 方法分隔字符串时,分隔符如果用到一些特殊字符,可能会得不到我们预期的结果,例如“|”,“”,“+”等,需要变为“\|”,“\”,“\+”才可以。
// 字符串转数组 java.lang.String
String str = "0,1,2,3,4,5";
String[] arr = str.split(","); // 用,分割
System.out.println(Arrays.toString(arr)); // [0, 1, 2, 3, 4, 5]
String str = new String("abcedfg");
String[] arr = str.split("");
数组转字符串String[] —> String
方法一: 遍历
借助StringBuffer类的append(),String本身不具备此方法
String[] str = new String[]{"abc","123","78"};
StringBuffer sb = new StringBuffer();
for(int i = 0;i<str.length;i++){
sb.append(str[i]);
}
String[] arr = { "0", "1", "2", "3", "4", "5" };
// 遍历
StringBuffer str5 = new StringBuffer();
for (String s : arr) {
str5.append(s);
}
System.out.println(str5.toString()); // 012345
方法二: 使用StringUtils的join方法
//数组转字符串 org.apache.commons.lang3.StringUtils
String str3 = StringUtils.join(arr); // 数组转字符串,其实使用的也是遍历
System.out.println(str3); // 012345
String str4 = StringUtils.join(arr, ","); // 数组转字符串(逗号分隔)(推荐)
System.out.println(str4); // 0,1,2,3,4,5
方法三: 使用ArrayUtils的toString方法
// 数组转字符串 org.apache.commons.lang3.ArrayUtils
String str2 = ArrayUtils.toString(arr, ","); // 数组转字符串(逗号分隔,首尾加大括号)
System.out.println(str2); // {0,1,2,3,4,5}
技巧
通过+连接其他字符串, 但是是两个组成一个新的字符串,有开销。最好用StringBuilder
StringBuffer
StringBuffer类和String一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和String不同,所以StringBuffer在进行字符串处理时,不生成新的对象,在内存使用上要优于String类。 所以在实际使用时,如果经常需要对一个字符串进行修改,例如插入、删除等操作,使用StringBuffer要更加适合一些。 在StringBuffer类中存在很多和String类一样的方法,这些方法在功能上和String类中的功能是完全一样的。 但是有一个最显著的区别在于,对于StringBuffer对象的每次修改都会改变对象自身,这点是和String类最大的区别。 另外由于StringBuffer是线程安全的,所以在多线程程序中也可以很方便的进行使用,但是程序的执行效率相对来说就要稍微慢一些。
初始化
StringBuilder sb = new StringBuilder();
方法
1 String 转 StringBuffer
StringBuffer s = new StringBuffer("abc");
2 append() 方法,将内容追加到StringBuffer末尾
StringBuffer s = new StringBuffer("abc");
s.append("efg");
System.out.println(s.toString());
3 deleteCharAt(int index) 方法,作用是删除指定位置的字符,然后将剩余的内容形成新的字符串,第一位为0。
s.delete(int start,int end) 该方法的作用是删除指定区间以内的所有字符,包含start,不包含end索引值的区间。
StringBuffer s = new StringBuffer("abc");
s.append("efg");
s.deleteCharAt(2);
s.delete(0,2);
4 insert(int offset, String str) 方法,作用是在StringBuffer对象中插入内容,然后形成新的字符串。例如:
StringBuffer sb = new StringBuffer("TestString");
sb.insert(4,"hello");
System.out.println(sb.toString());
5 reverse() 方法,作用是将StringBuffer对象中的内容反转,然后形成新的字符串。例如:
StringBuffer sb = new StringBuffer("TestString");
sb.reverse();
System.out.println(sb.toString()); // ---> gnirtStseT
6 setCharAt(int index, char ch) 方法,作用是修改对象中索引值为index位置的字符为新的字符ch。例如:
StringBuffer sb = new StringBuffer("bc");
sb.setCharAt(1,'D'); // ---> sb = ”aDc”
7 charAt:返回index位置的char — O(1)
.charAt(int index); // 返回index位置的char --- O(1)
8 length:返回缓冲字符串长度 — O(1)
.length(); // 返回缓冲字符串长度 --- O(1)
9 toString: // 返回一个与构建起或缓冲器内容相同的字符串 — O(n)
.toString(); // 返回一个与构建起或缓冲器内容相同的字符串 --- O(n)
数组与list转换
数组转list,调用Arrays.asList()方法:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
String[] strings = str.split(" ");
List<String> list = Arrays.asList(strings);//12345
2 list转数组
List list = new ArrayList();
list.add("1");
list.add("2");
final int size = list.size();
String[] arr = (String[])list.toArray(new String[size]);//12345
七、其他
数字比大小
Math.max(int a, int b); Math.max(float a, float b); Math.min(int a, int b);
public void testMain() throws Exception {
int a = 100;
int b = 200;
System.out.println(Math.max(a,b));
System.out.println(Math.min(a,b));
}
大数阶乘
import java.math.BigInteger;
import java.util.Scanner;
public class Main{
public static void main(String[] args) {
Scanner inputScanner=new Scanner(System.in);
while(inputScanner.hasNext())
{
int n=inputScanner.nextInt();
BigInteger m;
m=BigInteger.valueOf(1);//将m定义成大数的1
for(int i=1;i<=n;i++)
{
m=m.multiply(BigInteger.valueOf(i));//大数乘法
}
System.out.println(m);
}
}
}
大数加、减、乘、除、取余
//定义:
BigDemical bg = new BigDemical(int)
BigDemical bg = new BigDemical(String)
//加:
jia = bg.add(new BigDemical(1))
//减:
jian = bg.subtract(new BigDemical(1))
//乘:
cheng = bg.multiply(new BigDemical(1))
//除:
chu = bg.divide(new BigDemical(1))
//取余
yu = bg.divideAndRemainder(new BigDemical(2))
//返回一个数组:商yu[0] 余数yu[1]
各种数值类型最大值和最小值
fmax = Float.MAX_VALUE;
fmin = Float.MIN_VALUE;
dmax = Double.MAX_VALUE;
dmin = Double.MIN_VALUE;
bmax = Byte.MAX_VALUE;
bmin = Byte.MIN_VALUE;
cmax = Character.MAX_VALUE;
cmin = Character.MIN_VALUE;
shmax = Short.MAX_VALUE;
shmin = Short.MIN_VALUE;
imax = Integer.MAX_VALUE;
imin = Integer.MIN_VALUE;
lmax = Long.MAX_VALUE;
lmin = Long.MIN_VALUE;
Java Character 类
Character 类用于对单个字符进行操作。
Character 类在对象中包装一个基本类型 char 的值
模运算
a&1 等价于 a%2 == 0
a>>=1 等价于 a/=2
next()与nextLine()
next()一定要读取到有效字符后才可以结束输入,对输入有效字符之前遇到的空格键、Tab键或Enter键等结束符,next()方法会自动将其去掉,只有在输入有效字符之后,next()方法会将输入的空格键、Tab键或Enter键等视为分隔符或结束符,所以next方法不能得到带空格的字符串。
而nextLine()方法的结束符只是Enter键,即nextLine()方法返回的是Enter键之前的所有字符,它是可以得到带空格的字符串的。
要注意的是在每一个 next()、nextDouble() 、 nextFloat()、nextInt() 等语句之后如果还有一个nextLine(),需要再加一个nextLine()语句,将next()之后Enter结束符过滤掉,否则nextLine()会读取到Enter,而不能继续读取数据。
length,length(),size() 的使用与区别
length属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了length这个属性。
length()方法是针对字符串String说的,如果想看这个字符串的长度则用到length()这个方法。
size()方法是针对泛型集合泛型集合(Collection)如Set、List、Map说的,如果想看这个泛型有多少个元素,就调用此方法来查看。数组没有size()方法。
数组:用arr.length length 是数组的一个属性。
字符串:用str.length() length() 是 StringObject 的一个方法。
集合:用list.size() size()是list的一个方法。
public static void main(String[] args) {
String[] list = {"hello", "baidu"};
String a = "hellobaidu";
List<Object> array = new ArrayList();
array.add(a);
System.out.println(list.length);
System.out.println(a.length());
System.out.println(array.size());
}
// ---> 2 10 1
比较器
Comparator<Object> cmp = new Comparator<Object>() {
@Override
public int compare(Object o1, Object o2) {
//升序
return o1-o2;
//降序
return o2-o1;
}
};
一句话,返回负数,第一个参数放前面
comparator反应两个参数的权重。1是前者权重大,-1是后者。最后按照权重由小到大排序。 o1=4, o2=6, 返回1表示4权重大,权重默认升序最后4排后面即数值降序。
比较器
边界处理
1.数组判空:if(arr == null|| arr.length == 0) ...
2. 二维数组判空:if(arr == null || arr.length == 0 || arr[0].length == 0) ...
3.字符串判空:if(str == null || str.equals("")) ...
其他常用的类,直接使用类中的静态方法
类Arrays,此类包含用来操作数组(比如排序和搜索)的各种方法。
Math类,Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。
Boolean,Byte,Short,Interger,Long,Double,Floate,Charcter等八种基本类型对应的包装类。
HashSet转Array数组
Set<String> res = new HashSet<>();
res.toArray(new String[res.size()])
八、输入输出专栏
输入、输出小结
远程在线面试的手撕代码环节,通常需要白板上写代码。
如果需要在控制台输入、输出,需要借助Scanner类。
示例如下:
Scanner scanner = new Scanner(System.in);Scanner是一个扫描器,从键盘输入的数据,先存到缓存区等待读取。
scanner.hasNext():判断是否还有输入
scanner.next()/nextInt():接受下一个String/Int。判断读取结束的标示是空白符:空格,回车,tab 等等。
next()方法读取到空白符就结束;读取空格要用nextLine()
或者BufferedReader br=new BufferedReader(new InputStreamReader(System.in));。
使用BufferedReader对象的readLine()方法必须处理java.io.IOException异常(Exception)。
scanner.close():用完关闭
import java.util.Scanner;
public class Solution {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//方法1:一般用来接收数组。以空格分界
while(scanner.hasNext()){
int num = scanner.nextInt();
/*或者*/String next = scanner.next();
}
//方法2:一般用来接受字符串。以换行符分界
while (scanner.hasNextLine()) {
String str = scanner.nextLine();
}
}
数据量大尽量使用缓冲字符流。
数据量大的话,最好用BufferedReader,数据量小就没所谓了。System.out默认每调用一次write都会直接flush,相当于没有使用缓冲区; Scanner虽然用了缓冲区,但它读取特定类型数据是通过正则匹配实现的,速度相对也是较慢的。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
输入带整数
某比赛已经进入了淘汰赛阶段,已知共有n名选手参与了此阶段比赛,他们的得分分别是a_1,a_2….a_n,小美作为比赛的裁判希望设定一个分数线m,使得所有分数大于m的选手晋级,其他人淘汰。
但是为了保护粉丝脆弱的心脏,小美希望晋级和淘汰的人数均在[x,y]之间。
显然这个m有可能是不存在的,也有可能存在多个m,如果不存在,请你输出-1,如果存在多个,请你输出符合条件的最低的分数线。
数据范围:
进阶:时间复杂度O(nlogn) ,空间复杂度O(n)
输入描述:
输入第一行仅包含三个正整数n,x,y,分别表示参赛的人数和晋级淘汰人数区间。(1<=n<=50000,1<=x,y<=n)
输入第二行包含n个整数,中间用空格隔开,表示从1号选手到n号选手的成绩。(1<=|a_i|<=1000)
输出描述:
输出仅包含一个整数,如果不存在这样的m,则输出-1,否则输出符合条件的最小的值。
示例1
输入
6 2 3
1 2 3 4 5 6
输出
3
package leetcode.editor.cn;
import java.util.Arrays;
import java.util.Scanner;
/**
* @Author ljm
* @Date 2022/3/5 9:50
* @Version 1.0
*/
import java.util.Arrays;
import java.util.Scanner;
/**
* 美团校招 第10场 第1题
*/
public class mt1 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();//6
int x = in.nextInt();//2
int y = in.nextInt();//3
int[] score = new int[n];
//score [1,2,3,4,5,6]
for (int i = 0; i < n; i++) {
score[i] = in.nextInt();
}
/**
* 符合条件的最低分数线 -> 过线的人多 -> 使用人数限制最大值 y作为过线人数 -> 判断剩下的人数 是否在[x,y]区间 ->
* 如果在 直接返回
* 如果不在
* 如果人数少于x 则直接找到分数最低的第x个人即可
* 如果人数大于y 则证明找不到一个分数线满足条件
*/
// 对成绩排序
Arrays.sort(score);
// 没过线的人数
int notOk = n - y;
if (notOk > y){
System.out.println(-1);
}else if (notOk >= x && notOk <= y){
System.out.println(score[n-y-1]);
}else{
System.out.println(score[x-1]);
}
}
}
或者Java可以模仿python这种
bufferedreader运行起来更快一点
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] params = br.readLine().trim().split(" ");
int n = Integer.parseInt(params[0]);
int x = Integer.parseInt(params[1]);
int y = Integer.parseInt(params[2]);
String[] strArr = br.readLine().trim().split(" ");
int[] scores = new int[n];
for(int i = 0; i < n; i++) scores[i] = Integer.parseInt(strArr[i]);
Arrays.sort(scores);
// 每位选手要么就晋级,要么就淘汰,是互斥的
int m = -1;
int i = 0;
int count = n - 1;
for(i = 0; i < n; i++){
m = scores[i];
if(count >= x && count <= y && n - count >= x && n - count <= y){
// 检查一下是不是真的能晋级count人
if(judge(scores, i, m, count)) {
break;
}else
m = -1;
}else{
m = -1;
count --;
}
}
System.out.println(m);
}
private static boolean judge(int[] scores, int start, int m, int target) {
start ++;
if(start == scores.length) return false;
while(start < scores.length){
if(scores[start] == m) return false;
start ++;
}
return true;
}
}
python的话是
n,x,y = [int(s) for s in input().split()] # 以空格为分隔符,包含 \n
l = [int(s) for s in input().split()]
l.sort()
我们称一个长度为n的序列为正则序列,当且仅当该序列是一个由1~n组成的排列,即该序列由n个正整数组成,取值在[1,n]范围,且不存在重复的数,同时正则序列不要求排序
有一天小团得到了一个长度为n的任意序列s,他需要在有限次操作内,将这个序列变成一个正则序列,每次操作他可以任选序列中的一个数字,并将该数字加一或者减一。
请问他最少用多少次操作可以把这个序列变成正则序列?
数据范围:
进阶:时间复杂度O(n)\O(n) ,空间复杂度O(n)\O(n)
输入描述:
输入第一行仅包含一个正整数n,表示任意序列的长度。(1<=n<=20000)
输入第二行包含n个整数,表示给出的序列,每个数的绝对值都小于10000。
输出描述:
输出仅包含一个整数,表示最少的操作数量。
示例1
输入
5
-1 2 3 10 100
输出
103
import java.util.*;
public class Main{
public static void main(String args[]){
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int []num=new int[n];
for(int i=0;i<n;i++){
num[i]=input.nextInt();
}
int sum=0;
for(int i=1;i<=n;i++){
sum+=i;
}
int cursum=0;
for(int i=0;i<n;i++){
cursum+=num[i];
}
System.out.println(sum-cursum);
}
}
bufferedreader运行起来更快一点
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
String[] strSeq = br.readLine().trim().split(" ");
int[] seq = new int[n];
for(int i = 0; i < n; i++) seq[i] = Integer.parseInt(strSeq[i]);
Arrays.sort(seq);
int modifyTimes = 0;
for(int i = 1; i <= n; i++) modifyTimes += Math.abs(seq[i - 1] - i);
System.out.println(modifyTimes);
}
}
python
n = int(input())
a = list(map(int, input().strip().split()))
a.sort()
sum = 0
for i in range(1,n+1):
sum += abs(i-a[i-1])
print(sum)
输入带字符串
小美和小团所在公司的食堂有N张餐桌,从左到右摆成一排,每张餐桌有2张餐椅供至多2人用餐,公司职员排队进入食堂用餐。小美发现职员用餐的一个规律并告诉小团:当男职员进入食堂时,他会优先选择已经坐有1人的餐桌用餐,只有当每张餐桌要么空着要么坐满2人时,他才会考虑空着的餐桌;
当女职员进入食堂时,她会优先选择未坐人的餐桌用餐,只有当每张餐桌都坐有至少1人时,她才会考虑已经坐有1人的餐桌;
无论男女,当有多张餐桌供职员选择时,他会选择最靠左的餐桌用餐。现在食堂内已有若干人在用餐,另外M个人正排队进入食堂,小团会根据小美告诉他的规律预测排队的每个人分别会坐哪张餐桌。
进阶:时间复杂度O(nlogn) ,空间复杂度O(n)
输入描述:
第一行输入一个整数T(1<=T<=10),表示数据组数。
每组数据占四行,第一行输入一个整数N(1<=N<=500000);
第二行输入一个长度为N且仅包含数字0、1、2的字符串,第i个数字表示左起第i张餐桌已坐有的用餐人数;
第三行输入一个整数M(1<=M<=2N且保证排队的每个人进入食堂时都有可供选择的餐桌);
第四行输入一个长度为M且仅包含字母M、F的字符串,若第i个字母为M,则排在第i的人为男性,否则其为女性。
输出描述:
每组数据输出占M行,第i行输出一个整数j(1<=j<=N),表示排在第i的人将选择左起第j张餐桌用餐。
示例1
输入
1
5
01102
6
MFMMFF
输出
2
1
1
3
4
4
思路是用minheap来储存人数为0和1的桌子。这样对于每个人,可实现O(logn)获取下张可用的桌子。 总时间复杂度为O(logN)。 Note:数据量过大时,在输出结果的时候,如果每次都用System.out.println则会花费大量的时间,可能会造成超时。解决方法是用String Builder先获取总的输出字符串,最后输出这个字符串。 或者使用缓冲流
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;
/**
* @Author ljm
* @Date 2022/3/5 10:39
* @Version 1.0
*/
class Person{
int pos;
boolean isMale;
Person(int p, boolean m){
pos = p;
isMale = m;
}
}
public class mt3{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int tableCount = 0;
int personCount = 0;
Person[] person = null;
int[] table = null;
for(int i = 0; i < n; i++){
tableCount = scan.nextInt();//5
table = new int[tableCount];
String tmp = scan.next();//"01102"
char[] tmpArr = tmp.trim().toCharArray();
for(int j = 0; j < tableCount; j++){
//System.out.print(j);
table[j] = tmpArr[j] - '0';
}
personCount = scan.nextInt();//6
person = new Person[personCount];
tmp = scan.next();//"MFMMFF"
tmpArr = tmp.trim().toCharArray();
for(int j = 0; j < personCount; j++){
person[j] = new Person(j, tmpArr[j]=='M');
}
findPos(table, person);
//这个方法好节约时间
StringBuilder strb = new StringBuilder();
for(int j = 0; j < personCount; j++){
strb.append(person[j].pos + 1 + "\n");
}
strb.deleteCharAt(strb.length() - 1);
System.out.println(strb.toString());
}
}
public static void findPos(int[] table, Person[] person){
Queue<Integer> emptyPos = new PriorityQueue<>();
Queue<Integer> onePos = new PriorityQueue<>();
for(int i = 0; i < table.length; i++){
if(table[i] == 0) {
emptyPos.offer(i);
}
else if(table[i] == 1) onePos.offer(i);
}
for(int i = 0; i < person.length; i++){
if(person[i].isMale){
int pos = 0;
if(onePos.size() > 0){
person[i].pos = onePos.poll();
}
else{
pos = emptyPos.poll();
person[i].pos = pos;
onePos.offer(pos);
}
}
else{
int pos = 0;
if(emptyPos.size() > 0){
pos = emptyPos.poll();
onePos.offer(pos);
person[i].pos = pos;
}
else{
person[i].pos = onePos.poll();
}
}
}
}
}
一开始用的两个小根堆存人数为0和1的座位,后来想想用三个LinkedList就可以了,一个存放一开始人数为0的座位,一个存放一开始人数为1的座位,还有一个存放人数由0变为1的座位,需要查找人数为1的座位时,比较后两个List的首元素大小,如果其中一个为空,就选择另外一个。 其实这题考的是IO的处理吧,看了评论区题解才知道用Scanner会超时,尽量使用缓冲字符流。
package leetcode.editor.cn;
import java.util.*;
import java.io.*;
public class mt3{
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
int T = Integer.parseInt(br.readLine());//1
for (int t = 0; t < T; t++) {
int N = Integer.parseInt(br.readLine());//5
String seat = br.readLine();//"01102"
int M = Integer.parseInt(br.readLine());//6
String sex = br.readLine();//"MFMMFF"
List<Integer> zero = new LinkedList<>();
List<Integer> one = new LinkedList<>();
List<Integer> zeroToOne = new LinkedList<>();
for (int i = 0; i < N; i++) {
if (seat.charAt(i) == '0') {
zero.add(i);
} else if (seat.charAt(i) == '1') {
one.add(i);
}
}
for (int i = 0; i < M; i++) {
int index = 0;
if (sex.charAt(i) == 'M') {
if (!one.isEmpty() || !zeroToOne.isEmpty()) {
if (zeroToOne.isEmpty() || (!one.isEmpty() && one.get(0) < zeroToOne.get(0))) {
index = one.remove(0);
} else {
index = zeroToOne.remove(0);
}
} else {
index = zero.remove(0);
zeroToOne.add(index);
}
} else {
if (!zero.isEmpty()) {
index = zero.remove(0);
zeroToOne.add(index);
} else {
if (zeroToOne.isEmpty() || (!one.isEmpty() && one.get(0) < zeroToOne.get(0))) {
index = one.remove(0);
} else {
index = zeroToOne.remove(0);
}
}
}
bw.write(String.valueOf(index + 1));
//将下一行作为新行
bw.newLine();
}
}
//BufferedWriter是缓冲输入流,意思是调用BufferedWriter的write方法时候。
// 数据是先写入到缓冲区里,并没有直接写入到目的文件里。必须调用BufferedWriter的flush()方法。
// 这个方法会刷新一下该缓冲流,也就是会把数据写入到目的文件里。
bw.flush();
// 关闭流
bw.close();
br.close();
}
}
python
import heapq
T = int(input())
while T:
T -= 1
N = int(input()) # 5
people = input() # '01102'
M = int(input()) # 6
genders = input() # 'MFMMFF'
people = [int(x) for x in people] # [0, 1, 1, 0, 2]
heap0 = []
heap1 = []
for i, person in enumerate(people, start=1):
if person == 0:
heapq.heappush(heap0, i)
if person == 1:
heapq.heappush(heap1, i)
for gender in genders:
f0 = heap0[0] if heap0 else 0
f1 = heap1[0] if heap1 else 0
if gender == 'M':
ans = 'f1' if f1 else 'f0'
else:
ans = 'f0' if f0 else 'f1'
if ans == 'f1':
heapq.heappop(heap1)
print(f1)
else:
heapq.heappush(heap1, heapq.heappop(heap0))
print(f0)
对于极其恶心的输入
https://leetcode.cn/problems/lru-cache/
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
示例:
输入
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author ljm
* @Date 2022/8/19 20:22
* @Version 1.0
*/
/*
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
*/
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = br.readLine();
String[] arr1 = s.substring(1, s.length() - 1).split(",");
String split = br.readLine();
split = split.substring(1,split.length() - 1);
String regex = "\\[(.*?)]";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(split);
ArrayList<String[]> list = new ArrayList<>();
while (matcher.find()) {
String[] arr = matcher.group(1).split(",");
list.add(arr);
}
// System.out.println(list);
ArrayList<String> res = new ArrayList<>();
LRUCache lRUCache = new LRUCache(Integer.valueOf(list.get(0)[0]));;
res.add("null");
for (int i = 1; i < arr1.length; i++) {
String s1 = arr1[i].trim().replace("\"", "");
if (s1.equals("put")) {
lRUCache.put(Integer.valueOf(list.get(i)[0]),Integer.valueOf(list.get(i)[1].trim()));
res.add("null");
}else if (s1.equals("get")) {
res.add(String.valueOf(lRUCache.get(Integer.valueOf(list.get(i)[0]))));
}
}
StringBuilder sb = new StringBuilder();
sb.append("[");
for(int i = 0 ; i<res.size();i++){
sb.append(res.get(i));
sb.append(",");
}
sb.deleteCharAt(sb.length() -1).append("]");
System.out.println(sb.toString());
}
}
class LRUCache {
int cap;
LinkedHashMap<Integer,Integer> map = new LinkedHashMap<>();
public LRUCache(int capacity) {
this.cap = capacity;
}
public int get(int key) {
if(!map.containsKey(key)){
return -1;
}
if(map.containsKey(key)){
maker(key);
}
return map.get(key);
}
public void put(int key, int value) {
if(map.containsKey(key)){
map.put(key,value);
maker(key);
return;
}
if(map.size()>=this.cap){
int oldkey = map.keySet().iterator().next();
map.remove(oldkey);
}
map.put(key,value);
}
public void maker(int key){
int va = map.get(key);
map.remove(key);
map.put(key,va);
}
}