Table of Contents
Problem Statement
Invalid Transactions LeetCode Solution – A transaction is possibly invalid if:
- the amount exceeds
$1000
, or; - if it occurs within (and including)
60
minutes of another transaction with the same name in a different city.
You are given an array of strings transaction
where transactions[i]
consists of comma-separated values representing the name, time (in minutes), amount, and city of the transaction.
Return a list of transactions
that are possibly invalid. You may return the answer in any order.
Input: transactions = ["alice,20,800,mtv","alice,50,100,beijing"] Output: ["alice,20,800,mtv","alice,50,100,beijing"] Explanation: The first transaction is invalid because the second transaction occurs within a difference of 60 minutes, have the same name and is in a different city. Similarly the second one is invalid too.
Explanation
Bruteforce Approach:
The basic idea is to create a set to remove possible duplicates when looking at the two different ($1000 and within 60 minutes in a different city) invalid conditions. Then just brute force it by iteratively comparing every transaction with all the other transactions. This is extremely easy to do, but not optimal.
Optimized Approach:
- Create a
transaction
data structure so the attributes can easily be queried and stored in atransactions
array - Sort the
transactions
array-based ontime
- Iterate through the
transactions
array and create a hash table/dictionary withname
as the key, and an array oftransaction indexes
as the value. - Iterate through the dictionary and loop through the
name
specifictransaction indexes
array.- Over
1000
dollars check: If the current transactionamount
is greater than1000
, then add to theres
and continue to the next transaction. - Within
60
minutes check: Use left, and right pointers to represent the possible neighbor transactions that are within the60
minutes. - If the current transaction has any neighbors within the
60
minutes then check if the city is different. If so, add to theres
and continue to the next transactions.
- Over
1 We need to sort the transactions by time, so a function convert string to a customized struct is required.
2 To reduce the effort of compare of transactions, we use unordered_map to classify them by name.
3 To generate the result easily, we add the input string and a bool flag to the struct, with other info in the transactions we get the 6-member struct design.
4 To reach better performance, we can do sort only for transactions from the same name.
Code
C++ Code for Invalid Transaction
class Solution { public: struct CustomerDetail{ string name; int time; int amount; string city; }; CustomerDetail prepareCustomerObject(string s){ vector<string>temp; string t; istringstream f(s); while(getline(f,t,',')){ temp.push_back(t); } CustomerDetail obj = CustomerDetail(); obj.name=temp[0]; obj.time=stoi(temp[1]); obj.amount=stoi(temp[2]); obj.city=temp[3]; return obj; } vector<string> invalidTransactions(vector<string>& transactions) { vector<bool> invalid(transactions.size()); vector<CustomerDetail> details; map<string/*name*/,vector<int>/*list of indexes*/> hashmap; int i=0; for(string s: transactions) { CustomerDetail obj = prepareCustomerObject(s); invalid[i]=obj.amount>1000; if(hashmap.find(obj.name) != hashmap.end()) { vector<int> indexes = hashmap[obj.name]; for(int ind: indexes) { if(details[ind].city != obj.city && abs(obj.time-details[ind].time)<= 60) { invalid[ind]=true; invalid[i]=true; } } } hashmap[obj.name].push_back(i); details.push_back(obj); i++; } vector<string> ans; for(i=0;i<transactions.size();i++){ if(invalid[i]) ans.push_back(transactions[i]); } return ans; } };
Java Code for Invalid Transaction
class Solution { public List<String> invalidTransactions(final String[] transactions) { final List<String> invalid = new ArrayList<>(); final Map<String, List<Transaction>> map = new HashMap<>(); /* * build a map with name as key and value as list of transactions for that name */ for (final String transaction : transactions) { final Transaction tran = new Transaction(transaction); if (map.containsKey(tran.name)) { map.get(tran.name).add(tran); } else { final List<Transaction> list = new ArrayList<>(); list.add(tran); map.put(tran.name, list); } } for (final String transaction : transactions) { final Transaction tran = new Transaction(transaction); if (!isValid(map.get(tran.name), tran)) { invalid.add(transaction); } } return invalid; } public boolean isValid(final List<Transaction> transactions, final Transaction transaction) { /* if there is only one transaction and the amount is less than 1000 */ if (transactions.size() <= 1 && transaction.amount < 1000) return true; /* check against all other transaction to check it this one is valid */ for (final Transaction tran : transactions) { if (transaction.invalidTransaction(tran.city, tran.time)) { return false; } } return true; } class Transaction { String name; int time; int amount; String city; Transaction(final String transaction) { final String[] t = transaction.split(","); this.name = t[0]; this.time = Integer.parseInt(t[1]); this.amount = Integer.parseInt(t[2]); this.city = t[3]; } /* * the amount exceeds $1000, or; * * if it occurs within (and including) 60 minutes of another transaction with * the same name in a different city. Each transaction string transactions[i] * consists of comma separated values representing the name, time (in minutes), * amount, and city of the transaction. */ public boolean invalidTransaction(final String city, final int time) { return invalidAmount() || differentCity(city, time); } private boolean differentCity(final String city, final int time) { return !this.city.equals(city) && Math.abs(this.time - time) <= 60; } private boolean invalidAmount() { return this.amount > 1000; } } }
Python Code for Invalid Transaction
class Transaction: def __init__(self, name, time, amount, city): self.name = name self.time = int(time) self.amount = int(amount) self.city = city from collections import defaultdict class Solution: def invalidTransactions(self, transactions): transactions = [Transaction(*transaction.split(',')) for transaction in transactions] transactions.sort(key=lambda t: t.time) # O(nlogn) time trans_indexes = defaultdict(list) for i, t in enumerate(transactions): # O(n) time trans_indexes[t.name].append(i) res = [] for name, indexes in trans_indexes.items(): # O(n) time left = right = 0 for i, t_index in enumerate(indexes): t = transactions[t_index] if (t.amount > 1000): res.append("{},{},{},{}".format(t.name, t.time, t.amount, t.city)) continue while left <= len(indexes)-2 and transactions[indexes[left]].time < t.time - 60: # O(60) time left += 1 while right <= len(indexes)-2 and transactions[indexes[right+1]].time <= t.time + 60: # O(60) time right += 1 for i in range(left,right+1): # O(120) time if transactions[indexes[i]].city != t.city: res.append("{},{},{},{}".format(t.name, t.time, t.amount, t.city)) break return res
Complexity Analysis for Invalid Transactions LeetCode Solution
Time Complexity
O(NLogN)
Space Complexity
O(1) as we are not using any extra space.
Reference: https://en.wikipedia.org/wiki/Hash_table