一、题目
如果出现下述两种情况,交易可能无效:
① 交易金额超过 ¥1000
② 它和另一个城市中同名的另一笔交易相隔不超过 60 分钟(包含 60 分钟整)
每个交易字符串transactions[i]
由一些用逗号分隔的值组成,这些值分别表示交易的名称,时间(以分钟计),金额以及城市。
给你一份交易清单transactions
,返回可能无效的交易列表。你可以按任何顺序返回答案。
[来源:https://leetcode-cn.com/problems/invalid-transactions/]
二、初次题解
我们的思路:先存储,后过滤
这种题解使用 HashMap 作为存储容器,且将交易人员作为Key将交易清单划分至Value,实现了账单写至账户名下。每次取出需调用forEach方法
,排序实现了Comparator
接口,使得整个题解过于冗余。
虽作为题解有些许笨重,但这样的存储方式方便了后续操作。不过这不是解本题的重点,所以实际上只需将每笔交易用两次循环依次比较即可。
class Solution {
public List<String> invalidTransactions(String[] transactions) {
//记录数组长度
int len = transactions.length;
//构建结果List集合
List<String> result = new ArrayList<>(len);
//构建Map键值对,以人为单位存储数据,追求数据对应性
Map<String,List<Transaction>> map = new HashMap<>();
/*
* 第一步:先存储
*/
//循环将字符串分割、构成“清单Transaction”对象,在逐次放入Map结构中
for (int i = 0; i < len; i++) {
String[] s = transactions[i].split(","); //分割字符串
if(!map.containsKey(s[0])){ //当map中不存在Key,就将Key值放入Map中
List<Transaction> list = new ArrayList<>(len);
map.put(s[0],list);
}
Transaction t = new Transaction( //创建相应的Transaction对象
s[0],
Integer.valueOf(s[1]),
Integer.valueOf(s[2]),
s[3]
);
map.get(s[0]).add(t); //获取Key值并放入“清单Transaction”对象
}
/*
* 第二步:后存储
*/
//将Map中的值通过forEach方法依次取出、排序再将符合条件的结果放入结果List中
map.forEach((s,ts) -> {
ts.sort(new Comparator<Transaction>() { //重写 Comparator 接口排序
@Override
public int compare(Transaction o1, Transaction o2) {
return o1.time - o2.time;
}
});
if(ts.size() > 1){ //交易人员有多笔交易
int size = ts.size();
int[] inner = new int[size]; //若交易有效,则“亮灯”(即对应位数字+1)
for (int i = 0; i < size; i++) {
Transaction t1 = ts.get(i);
for (int j = i + 1; j < size ; j++) {
Transaction t2 = ts.get(j);
if((!t2.city.equals(t1.city) && t2.time - t1.time <= 60)){
inner[i]++;
inner[j]++;
}
}
if(t1.cost >= 1000){
inner[i]++;
}
}
for (int i = 0; i < size; i++) { //根据指示取出值并加入List集合
if(inner[i] > 0){
result.add(ts.get(i).toString());
}
}
}else{ //交易人员仅有单笔交易
Transaction single = ts.get(0);
if(single.cost >= 1000){
result.add(single.toString());
}
}
});
//返回结果List
return result;
}
/**
* 创建交易清单类,用以存储相关数据
*/
class Transaction{
public String name;
public int time;
public int cost;
public String city;
public Transaction(String name, int time, int cost, String city) {
this.name = name;
this.time = time;
this.cost = cost;
this.city = city;
}
//重写toString方法,方便加入List集合
@Override
public String toString() {
return name + "," + time + "," + cost + "," + city;
}
}
}
三、优化题解
我们的思路不变,仍是:先存储,后过滤
这种题解将所有的对象存储至数组中,减轻了存储压力、加快了运行速度,不用构建HashMap容器,简单的使用两层循环加判断解决了相应问题,作为优化方案相当合适。
class Solution {
public List<String> invalidTransactions(String[] transactions) {
//记录数组长度
int len = transactions.length;
//维护对象数组,长度与原数组相等
Transactions[] arr = new Transactions[len];
/*
* 第一步:先存储
*/
//循环
for (int i = 0; i < len; i++) {
//分割字符串
String[] tmp = transactions[i].split(",");
//创建相应的Transaction对象,并放入“清单Transactions”对象数组中
arr[i] = new Transactions(
tmp[0],
Integer.valueOf(tmp[1]),
Integer.valueOf(tmp[2]),
tmp[3]
);
}
//构建结果List集合
List<String> result= new ArrayList<>();
/*
* 第二步:后过滤
*/
//循环开始比较
for (int i = 0; i <len; i++) {
//先判断第一个条件
if (arr[i].money > 1000) {
result.add(transactions[i]);
continue;
}
//再循环判断该交易与其他交是否满足第二个条件
for(int j = 0; j < len; j++) {
//如若相同交易则不进行比较
if (i == j) continue;
//
if (arr[i].name.equals(arr[j].name) && !arr[i].city.equals(arr[j].city) &&
Math.abs(arr[i].time - arr[j].time) <= 60) {
result.add(transactions[i]);
break;
}
}
}
return result;
}
}
class Transactions {
String name;
Integer time;
Integer money;
String city;
public Transactions(String name, Integer time, Integer money, String city) {
this.name = name;
this.time = time;
this.money = money;
this.city = city;
}
}
【思考】
- 可以看到这种方案的运行用时降低了许多。在解决问题的过程中,我们应灵活使用数组、容器,在众多结构中,Java的容器提供了很多好的方法、接口供我们使用,但数组是最基础的结构,也是常用结构之一,在某些情况下可以简化操作。我们应结合情景慎重考虑,选择合适的结构存储数据,从而减少不必要的开销。