coding - r

8 阅读15分钟

Referral Count

// "static void main" must be defined in a public class.
// Robinhood is famous for its referral program. It’s exciting to see our users spreading the word across their friends and family. One thing that is interesting about the program is the network effect it creates. We would like to build a dashboard to track the status of the program. Specifically, we would like to learn about how people refer others through the chain of referral.

// For the purpose of this question, we consider that a person refers all other people down the referral chain. For example, A refers B, C, and D in a referral chain of A -> B -> C -> D. Please build a leaderboard for the top 3 users who have the most referred users along with the referral count.

// Referral rules:

// A user can only be referred once.
// Once the user is on the RH platform, he/she cannot be referred by other users. For example: if A refers B, no other user can refer A or B since both of them are on the RH platform.
// Referrals in the input will appear in the order they were made.
// Leaderboard rules:

// The user must have at least 1 referral count to be on the leaderboard.
// The leaderboard contains at most 3 users.
// The list should be sorted by the referral count in descending order.
// If there are users with the same referral count, break the ties by the alphabetical order of the user name.
// Input

// rh_users = ["A", "B", "C"]
// | | |
// v v v
// new_users = ["B", "C", "D"]
// Output

// ["A 3", "B 2", "C 1"]
// [execution time limit] 3 seconds (java)

// [memory limit] 1 GB

// [input] array.string rh_users

// A list of referring users.

// [input] array.string new_users

// A list of user that was referred by the users in the referrers array with the same order.

// [output] array.string

// An array of 3 users on the leaderboard. Each of the element here would have the "[user] [referral count]" format. For example, "A 4".

public class Main {
    
    
    
    public static String[] countReferral(String[] rh_users, String[] new_users) {
        
        Map<String, List<String>> graph = new HashMap<>();
    
        Set<String> existingUser = new HashSet<>();
        
        // build graph
        for (int i = 0; i < rh_users.length; i++) {
            String from = rh_users[i];
            String to = new_users[i];
            
            // // can't refer an existing user
            if (!existingUser.contains(to)) {
                graph.putIfAbsent(from, new ArrayList<>());
                graph.get(from).add(to);
            }
            
            // both add to existing
            existingUser.add(from);
            existingUser.add(to);
        }
        
        // <User, referral number (subtree nodes#)>
        Map<String, Integer> referralCount = new HashMap<>();
        for (String rh_user : rh_users) {
            int count = dfs(graph, rh_user) - 1;
            if (count > 0) { // for a-b b-a case, b refer 0 不上榜
                referralCount.put(rh_user, count);
            }
        }
        
        // Sorted list
        List<Map.Entry<String, Integer>> list = new ArrayList<>(referralCount.entrySet());
        
        Collections.sort(list, (a, b) -> {
            if (a.getValue() != b.getValue()) {
                return b.getValue() - a.getValue();// first sort by referral# desc
            } else {
                return a.getKey().compareTo(b.getKey()); // sor by alphabetically
            }
        });
        
        String[] res = new String[3];
        int i = 0;
        for (Map.Entry<String, Integer> entry : list) {
            res[i] = entry.getKey() + " " + entry.getValue();
            i++;
            if(i == 3) {
                break;
            }
        }
        
        return res;
        
    }
    
    // returns the # of nodes of subtree cur, inclusive
    public static int dfs(Map<String, List<String>> graph, String cur) {
        if (!graph.containsKey(cur)) {
            return 1;
        }
        
        int adjReferralCount = 0;
        for (String adj : graph.get(cur)) {
            adjReferralCount += dfs(graph, adj);
        }
                
        return adjReferralCount + 1;
    }

Microservice Dependency - load factor

// /* 
// You are building an application that consists of many different services that can depend on each other. One of these services is the entrypoint which receives user requests and then makes requests to each of its dependencies, which will in turn call each of their dependencies and so on before returning.
// Given a directed acyclic graph that contains these dependencies, you are tasked with determining the "load factor" for each of these services to handle this load. The load factor of a service is defined as the number of units of load it will receive if the entrypoint receives a 1 unit of load. Note that we are interested in the worst case capacity. For a given downstream service, its load factor is the number of units of load it is required to handle if all upstream services made simultaneous requests. For example, in the following dependency graph where A is the entrypoint:
// Each query to A will generate one query to B which will pass it on to C and from there to D. A will also generate a query to C which will pass it on to D, so the worst case (maximum) load factors for each service is A:1, B:1, C:2, D:2.
// (Important: make sure you've fully understood the above example before proceeding!)


// Problem Details
// service_list: An array of strings of format service_name=dependency1,dependency2. Dependencies can be blank (e.g. dashboard=) and non-existent dependency references should be ignored (e.g. prices=users,foobar and foobar is not a service defined in the graph). Each service is defined only once in the graph.
// entrypoint: An arbitrary service that is guaranteed to exist within the graph
// Output: A list of all services depended by (and including) entrypoint as an array of strings with the format service_name*load_factor sorted by service name.
// Example
// Input:
// service_list = ["logging=",
// "user=logging",
// "orders=user,foobar",
// "recommendations=user,orders",
// "dashboard=user,orders,recommendations"]
// entrypoint = "dashboard"

// A -> C, B -> C, D -> C => C:3 

// {db:[user,orders,recom], user:[logging], orders:[user, foobar], recom:[user,orders]}
// [db] {db:1}
// [user,orders,recom] {db:1, user:1, orders:1, recom:1}
// [logging, user, foobar, user, orders] {db:1, logging:1, user:3, foobar:1, order:1}

// // [dashboard] -> db:1
// // [user, orders, recom] -> db:1, user:1, orders:1, recom:1
// // [logging, user, foobar, user, orders] -> db:1, user:3, orders:2, recom:1, logging:1

// Output (note sorted by service name)
// ["dashboard*1",
// "logging*4",
// "orders*2",
// "recommendations*1",
// "user*4"]
// [execution time limit] 3 seconds (cs)
// [input] array.string service_list
// [input] string entrypoint
// [output] array.string
// */


// T(n) = nlogn S(n) = n n: the number of service dependencies (ENlogN)
public class Main {
    public static String[] getLoadFactor(String[] serviceList, String entryPoint) {
        // build graph
        Map<String, List<String>> graph = new HashMap<>();
        // relation: "orders=user,foobar"
        for (String relation : serviceList) {
            String from = relation.split("=")[0];
            
            graph.putIfAbsent(from, new ArrayList<>());
            
            // "logging=", no child, but still need to be in map("exist")
            if(relation.split("=").length > 1) {
                String to = relation.split("=")[1];
                for (String s : to.split(",")) {
                    graph.get(from).add(s);
                }
            }
            
        }
        
        // sort by key
        TreeMap<String, Integer> loadMap = new TreeMap<>();
        loadMap.put(entryPoint, 1);
        
        // bfs
        Queue<String> queue = new LinkedList<>();
        queue.offer(entryPoint);
        
        while (!queue.isEmpty()) {
            String cur = queue.poll();
            // end "to" points dont have adj
            if(graph.containsKey(cur)) {
                 for (String adj : graph.get(cur)) {
                    loadMap.put(adj, loadMap.getOrDefault(adj, 0) + 1);
                    queue.offer(adj);
                }
            }
        }
        
        // return in "logging*4" format
        String[] res = new String[graph.size()];
        int i = 0;
        for(String service : loadMap.keySet()) {
            if (graph.containsKey(service)) { // need to filter out non-exist nodes like "foobar"
                res[i] = service + "*" + loadMap.get(service);
                i++;
            }
        }
        
        return res;
    }
    
    public static void main(String[] args) {
        
        // foobar will not be in the res. it is "non-existence"(didnt appear before =)
        String[] service_list = {"logging=",
                                "user=logging",
                                "orders=user,foobar",
                                "recommendations=user,orders",
                                "dashboard=user,orders,recommendations"};
        
        String entry = "dashboard";
        
        String[] res = getLoadFactor(service_list, entry);
        
        for (String s : res) {
            System.out.println(s);
        }
        
         String[] test2 = new String[]{
            "A=B",
            "B=C",
            "C=D",
            "A=C",
            "D="
        };
        String entrypoint2 = "A";
        
        String[] res2 = getLoadFactor(test2, entrypoint2);
        
        for (String s : res2) {
            System.out.println(s);
        }
        

    }
}

Offset commit

// For example, a 4-message stream may look like:

// [offset = 0][offset = 1][offset = 2][offset = 3]
// However, while messages may arrive in order, we might not necessarily process them in order.

// For example, imagine we are processing messages in a multi-threaded environment, and thus we are at the whim of the scheduler. We may process messages in the order [offset = 3][offset = 0][offset = 1][offset = 2], for instance.

// When we're finished with a message (i.e. an offset), we need to tell the stream this, so it knows not to send it again. This is called committing an offset.

// To "commit an offset" means that we're done with every message up to that offset. In other words, committing an offset of 2 means "I'm done with messages with offsets 0, 1, and 2". That means we can ONLY commit to 2 if we're ALSO done with 0, 1, and 2.

// Problem statement

// Given a list of offsets, ordered by when they are processed, return a list of offsets that represent the greediest order of commits. That is, when an offset CAN be committed, we MUST commit it.

// We can commit an offset X when EITHER:

// X = 0, since it is the first message of the stream
// All offsets < X are either ready to be committed or are already committed
// If we cannot commit offset X, we represent this as -1.
// Example 1:
// Input: [2, 0, 1]
// Output: [-1, 0, 2]

// We iterate through each message from left to right:

// 1). We try to commit 2, but we CANNOT because all previous offsets (0, 1) have not been committed yet. Thus, we append -1 in our result list, which represents NO commit on this offset.
// It might help to visualize this state as something like:

//                     (ready to be committed 2)
//                      ----------
// [offset = 0][offset = 1][offset = 2]
// 2). We try to commit 0, and we CAN because it's the first message of the stream. We commit the offset 0. Thus, we append 0 to our result list.

// (committed 0)
// xxxxxxxxxx ----------
// [offset = 0][offset = 1][offset = 2]
// 3). We try to commit 1, and we CAN because all messages up to 1 have been committed. We commit the offset 2. Thus, we append 2 to our result list.

//                     (committed 2)    
// xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx
// [offset = 0][offset = 1][offset = 2]
// Thus, we output [-1, 0, 2]. Remember, 1 is NOT in the output because the commit of offset 2 encapsulates it.

// Example 2
// Input: [0, 1, 2]
// Output: [0, 1, 2]

// We can commit each message as we iterate because each successive offset is the lowest offset we can possibly commit.

// Example 3
// Input: [2, 1, 0, 5, 4]
// Output: [-1, -1, 2, -1, -1]

// We do NOT commit 4 and 5. Had we received 3, we would have committed 4 and 5.

// Important things to remember

// Assume a clean "state of the world" for every function call, i.e. no offsets have been committed thus far (so we must always start at 0).
// Every offset is >= 0.
// We never have any duplicate offsets.

public class Main {
    public static int[] getCommits(int[] input) {
        Set<Integer> buffer = new HashSet<>();
        
        int[] res = new int[input.length];
        
        int expect = 0; // expected next number to process, initially 0
        
        for (int i = 0; i < input.length; i++) {
        
            buffer.add(input[i]);
            
            if (input[i] == expect) {
                // can commit! neet to find the last consecutive number
                int cur = expect;
                int last = cur;
                while (buffer.contains(cur)) {
                    last = cur;
                    cur++;
                }
                
                res[i] = last;
                expect = last + 1; // update expected next 
            
            } else {
                res[i] = -1; // cant commit
            }
        }
        
        return res;
    }
    
    // O(1) space
    public static int[] getCommits(int[] input) {        
        int[] res = new int[input.length];
        
        int expect = 0; // expected next number to process, initially 0
        
        for (int i = 0; i < input.length; i++) {
                    
            if (input[i] == expect) {
                // can commit! neet to find the last consecutive number
                int cur = expect;
                int last = cur;
                
                boolean found = true;
                while (found) {
                    found = false;
                    // searching cur+1 in visited part
                    for (int j = 0; j < i; j++) {
                        if (input[j] == cur + 1) {
                            found = true;
                            last = input[j];
                            cur = cur + 1;
                        }
                    }
                    // if found = true, keep looking for next consecutive
                    // if found = false, terminate
                }
                
                res[i] = last;
                expect = last + 1; // update expected next 
            
            } else {
                res[i] = -1; // cant commit
            }
        }
        
        return res;
    }
    
    public static void main(String[] args) {
        // int[] input = {2,0,1};
        int[] input = {0,2};
        // int[] input = {0,1,2};
        // int[] input = {5,4,2,0,1};
        // int[] input = {2,1,0,5,4};
        // int[] input = {1,2,0,5,4};
        // int[] input = {0,7,1,2,6,3,5,4};
         // int[] input = {3,1,2,0};
        // int[] input = {5,4,3,2,1};
        // int[] input = {0, 1, 3, 4};
        // int[] input = {0, 2, 1, 4, 3};
        
        for (int x : getCommits(input)) {
            System.out.print(x + " ");
        }
        
    }
}

Course schedule mid

Part1

// ** PART 1
// Each course at our university has at most one prerequisite that must be taken first. 
// No two courses share a prerequisite. There is only one path through the program.
// Write a function that takes a list of (prerequisite, course) pairs,
//  and returns the name  of the course that the student will be taking when they are halfway through their program. 
//  (If a track has an even number of courses, and therefore has two "middle" courses,   you should return the first one.)

public static String findMiddleCourse(List<List<String>> relations) {
        Map<String, String> map = new HashMap<>();
        // stores "to" courses that has prerequisits
        Set<String> dest = new HashSet<>(); 
        
        for (List<String> r : relations) {
            map.put(r.get(0), r.get(1));
            dest.add(r.get(1));
        }
        
        // a->b b->c c->e e->z
        // find start, which is the course with 0 prereq
        String start = "";
        for (List<String> r : relations) {
            if (!dest.contains(r.get(0))) {
                start = r.get(0);
                break;
            }
        }
        
        int totalCourses = map.size() + 1;
        int midCourseIndex = (totalCourses - 1) / 2;
        
        // a-b-c   a-b-c-d
        String cur = start;
        for (int i = 0; i < midCourseIndex; i++) {
            cur = map.get(cur);
        }
      
        return cur;
    }
    

Part 2


// PART 2:
// Not a linked list, a graph.
// multiple track, multiple path, multiple start course, return all the 'middle' courses

public static List<String> findAllMiddleCourses(List<List<String>> relations) {
        Map<String, List<String>> graph = new HashMap<>();
        Map<String, Integer> inDegree = new HashMap<>();
                
        for (List<String> r : relations) {
            String from = r.get(0), to = r.get(1);
            graph.putIfAbsent(from, new ArrayList<>());
            graph.get(from).add(to);
            inDegree.put(to, inDegree.getOrDefault(to, 0) + 1);
            inDegree.putIfAbsent(from, 0); // make sure add this
        }
        
        Set<String> res = new HashSet<>();
        // find source nodes, indegree = 0
        for (String s : inDegree.keySet()) {
            if (inDegree.get(s) == 0) {
                // s is a start course, do dfs
                dfs(graph, new ArrayList<>(), s, res);
            }
        }
        return new ArrayList<>(res);
    }
    
    // res: no redundant
    public static void dfs(Map<String, List<String>> graph, List<String> path, String cur, Set<String> res) {
        path.add(cur);
        
        // cur is the end node
        if (!graph.containsKey(cur)) {
            String mid = path.get((path.size() - 1) / 2);
            res.add(mid);
            return;
        }
        
        // copied a new path
        for (String adj : graph.get(cur)) {
            dfs(graph, new ArrayList<>(path), adj, res);
        }
        
    }

part 3

// finds the longest path and returns the course in the middle of that path

 public String findLongestPathMiddleCourse(List<List<String>> pairs) {
        // Build the graph (adjacency list) and compute in-degrees.
        Map<String, List<String>> graph = new HashMap<>();
        Map<String, Integer> indegree = new HashMap<>();
        

        for (List<String> pair : pairs) {
            String from = pair.get(0);
            String to = pair.get(1);
            graph.computeIfAbsent(from, k -> new ArrayList<>()).add(to);
            indegree.put(to, indegree.getOrDefault(to, 0) + 1);
            indegree.putIfAbsent(from, 0);
        }

        // Identify source nodes (with zero in-degree).
        List<String> sources = new ArrayList<>();
        for (String node : indegree.keySet()) {
            if (indegree.getOrDefault(node, 0) == 0) {
                sources.add(node);
            }
        }

        // Use memoization to store the longest path starting from a node.
        Map<String, List<String>> map = new HashMap<>();

        // Find the longest path among all sources.
        List<String> longestPath = new ArrayList<>();
        for (String src : sources) {
            List<String> path = dfs(src, graph, map);
            if (path.size() > longestPath.size()) {
                longestPath = path;
            }
        }
        // Determine the middle course.
        int n = longestPath.size();
        int midIndex;
        if (n % 2 == 1) {
            midIndex = n / 2;
        } else {
            midIndex = n / 2 - 1;
        }
        return longestPath.get(midIndex);
    }
    
// memoization is used to cache the longest path starting from each node, ensuring that each node’s result is computed only once. For each node:

// If it has no children (i.e., it’s a sink), the DFS returns a list containing just that node.
// Otherwise, the DFS computes the longest path among its children and prepends the current node to this path, effectively building the longest path from that node onward.

    // dfs
    private List<String> dfs(String node, Map<String, List<String>> graph, Map<String, List<String>> map) {
        if (map.containsKey(node)) {
            return map.get(node);
        }
        // If no children, the longest path is just [node].
        if (!graph.containsKey(node) || graph.get(node).isEmpty()) {
            List<String> list = new ArrayList<>();
            list.add(node);
            map.put(node, list);
            return list;
        }
        
        List<String> maxPath = new ArrayList<>();
        for (String neighbor : graph.get(node)) {
            List<String> path = dfs(neighbor, graph, map);
            if (path.size() > maxPath.size()) {
                maxPath = path;
            }
        }
        List<String> result = new ArrayList<>();
        result.add(node);
        result.addAll(maxPath);
        map.put(node, result);
        return result;
    }

Fractional system

// """
// On our journey to democratize finance for all, we’ve created the concept of fractional shares. Fractional shares are pieces, or fractions, of whole shares of a company or ETF.

// However, exchanges only trade in whole shares. Robinhood is required to manage the fractional portion of each trade.

// If Robinhood has 0 shares of AAPL and then a customer wishes to purchase 1.5 (150) AAPL shares, Robinhood will need to request 2 (200) shares from the exchange and hold on to the remaining 0.5 (50) shares.
// If another customer requests to purchase 0.4 shares of AAPL, Robinhood can use its inventory (0.5 shares) instead of going out to the exchange and will have 0.1 shares of AAPL remaining.
// If the third customer requests 0.5 shares, Robinhood can fill 0.1 shares out of inventory but will need to go to the exchange for an additional share leaving Robinhood's inventory at 0.6 shares.
// If a customer requests a dollar based order, we need to convert it to the relevant number of shares and run through the above steps.
// Always ensure the firm has a positive quantity in inventory and has under one share after handling an order. There's no need for us to hold onto whole shares!



// Steps:

// Handle buying fractional shares.
// Handle selling fractional shares.
// Ensure inventory is less than 1 after each order.
// e.g. Customer sells AAPL for 0.75 and then another sells AAPL for 0.50 -- we have 1.25 inventory. We can sell 1 share to the market and keep our inventory small at 0.25.
// Ensure inventory is always non-negative after each order.
// e.g. Inventory is 0.2 and the customer buys 0.5 shares: ensure we end up with 0.7 shares in inventory.
// Always “flatten”! (steps 3+4)
// The final 2 digits of every integer is the decimal. e.g. 1000 = 10.00, 20 = 0.20, 100 = 1.

// Example scenario:

// Input:
// // One AAPL buy order for 0.42 shares. AAPL is currently worth $1.
// orders: [["AAPL","B","42","100"]]

// // Inventory for AAPL is currently 0.99 shares.
// inventory: [["AAPL","99"]]


// Expected Output:
// // The users buys 0.42 shares from inventory, leaving us with 0.57 shares.
// [["AAPL","57"]]
// Another example scenario:

// Input:
// // One AAPL buy order for $0.42. AAPL is currently worth $1, so that's 0.42 shares.
// orders: [["AAPL","B","$42","100"]]
// // Existing AAPL inventory is 0.50 shares.
// inventory: [["AAPL","50"]]

// Expected Output:
// // 0.50 - 0.42 = 0.08 shares leftover.
// [["AAPL","8"]]

// [execution time limit] 3 seconds (java)

// [memory limit] 1 GB

// [input] array.array.string orders

// A list of orders in the format of [$SYMBOL, $BUY_OR_SELL, $QUANTITY, $CURRENT_PRICE]. Each parameter is a string.

// $SYMBOL: Can be "AAPL", "GOOGL", "MEOOOOOW" or anything really.
// $BUY_OR_SELL: "B" or "S". B for BUY, S for SELL.
// $QUANTITY: Can be a number or a dollar amount (prefixed with $). e.g. "100" for 1 quantity or "$150" for $1.50.
// $CURRENT_PRICE: Current price of the symbol with no $ sign. e.g. "1000" for $10.

// ** All numbers are multiplied by 100 to store two significant digits. e.g. 100 = 1.00, 150 = 1.50, 1025 = 10.25 **

// [input] array.array.string inventory

// Inventory is a list of the inventory of each symbol. Each element in the list a 2 item list of [$SYMBOL, $QUANTITY] (remember quantity is multiplied by 100!).

// An example for AAPL of 0.50 shares and GOOGL of 0.75 shares would be:

// [["AAPL","50"], 
//  ["GOOG","75"]]
// [output] array.array.string

// The output is the final inventory of each symbol after iterating through each trade. This is expected to be in the same order and format as the inventory parameter.

// e.g.

// ["AAPL","58"], 
//  ["GOOG","50"]]
// """

public List<List<String>> fractionInvent(List<List<String>> orders, List<List<String>> inventory) {
		  // initialize inventory
        Map<String, Integer> inventoryMap = new LinkedHashMap<>();
        for (List<String> list : inventory) {
            String symbol = list.get(0);
            int quantity = Integer.parseInt(list.get(1));
            inventoryMap.put(symbol, quantity);
        }
        
        // process orders
        // [$SYMBOL, $BUY_OR_SELL, $QUANTITY, $CURRENT_PRICE]
        for (List<String> order : orders) {
            String symbol = order.get(0);
            String operation = order.get(1);
            String quantity = order.get(2);
            String price = order.get(3);
            
            // Ensure the symbol exists in our inventory. If not, initialize to 0.
            if (!inventoryMap.containsKey(symbol)) {
                inventoryMap.put(symbol, 0);
            }
            
            // calculate shares 
            int orderShares = 0;
            if (quantity.startsWith("$")) {
                int amount = Integer.parseInt(quantity.substring(1));
                orderShares = amount * 100 / Integer.parseInt(price);// !!! lose precision if / *
            } else {
                orderShares = Integer.parseInt(quantity);
            }
            
            int curShares = inventoryMap.get(symbol);
            
            // buy or sell
            if (operation.equals("B")) {
                if (curShares >= orderShares) {
                    inventoryMap.put(symbol, curShares - orderShares);
                } else {
                    // hood need to buy whole shares first
                    int sharesToBuy = 100 * (int) Math.ceil((orderShares - curShares) / 100.00);
                    inventoryMap.put(symbol, curShares + sharesToBuy - orderShares);
                }
            } else if (operation.equals("S")) {
                int newShares = (curShares + orderShares) % 100;
                inventoryMap.put(symbol, newShares);
            }
		}

		// [ ["AAPL","58"], ...]
		List<List<String>> res = new ArrayList<>();
		for (String s : inventoryMap.keySet()) {
			List<String> list = Arrays.asList(s, inventoryMap.get(s) + "");
			res.add(list);
		}
            
        return res;

	 }
    

Buy and Sell orders match

// Given a stream of incoming "buy" and "sell" orders (as lists of limit price, quantity, and side, like
// ["155", "3", "buy"]), determine the total quantity (or number of "shares") executed.

// A "buy" order can be executed if there is a corresponding "sell" order with a price that is less than or
// equal to the price of the "buy" order.
// Similarly, a "sell" order can be executed if there is a corresponding "buy" order with a price that is
// greater than or equal to the price of the "sell" order.
// It is possible that an order does not execute immediately if it isn't paired to a counterparty. In that 
// case, you should keep track of that order and execute it at a later time when a pairing order is found.
// You should ensure that orders are filled immediately at the best possible price. That is, an order 
// should be executed when it is processed, if possible. Further, "buy" orders should execute at the 
// lowest possible price and "sell" orders at the highest possible price at the time the order is handled.

// Note that orders can be partially executed.

// --- Sample Input ---

// orders = [
//   ['150', '5', 'buy'],    # Order A
//   ['190', '1', 'sell'],   # Order B
//   ['200', '1', 'sell'],   # Order C
//   ['100', '9', 'buy'],    # Order D
//   ['140', '8', 'sell'],   # Order E
//   ['210', '4', 'buy'],    # Order F
// ]

// Sample Output
// 9



    // define
    static class Order{
        int price;
        int shares;
        // String operation; no need!
        
        Order(int price, int shares) {
            this.price = price;
            this.shares = shares;
        }
    }
    
    public static int calculateExecutedShares(List<List<String>> orders) {
        // min heap for sell orders
        PriorityQueue<Order> sellQueue = new PriorityQueue<>((a, b) -> (a.price - b.price));
        // max heap for buy orders
        PriorityQueue<Order> buyQueue = new PriorityQueue<>((a, b) -> (b.price - a.price));
        
        int total = 0;
        // ['150', '5', 'buy']
        for (List<String> list : orders) {
            int price = Integer.parseInt(list.get(0));
            int shares = Integer.parseInt(list.get(1));
            String operation = list.get(2);
            
            int sharesBefore = shares;
            if (operation.equals("buy")) {
                // find all sell orders
                while (shares > 0 && !sellQueue.isEmpty()) {
                    Order sellOrder = sellQueue.peek();
                    if (price >= sellOrder.price) {
                        // matched amount, take min
                        int count = Math.min(shares, sellOrder.shares);
                        shares -= count;
                        sellOrder.shares -= count;
                        if (sellOrder.shares == 0) {
                            sellQueue.poll(); // sold all
                        }
                    } else {
                        break; // needed!!!
                    }
                }
                if (shares > 0) {
                    buyQueue.offer(new Order(price, shares));
                }
            } else if (operation.equals("sell")) {
                while (shares > 0 && !buyQueue.isEmpty()) {
                    Order buyOrder = buyQueue.peek();
                    if (price <= buyOrder.price) {
                        int count = Math.min(shares, buyOrder.shares);
                        shares -= count;
                        buyOrder.shares -= count;
                        if (buyOrder.shares == 0) {
                            buyQueue.poll();
                        }
                    } else {
                        break;
                    }
                }
                if (shares > 0) {
                    sellQueue.offer(new Order(price, shares));
                }
            }
            
            total += sharesBefore - shares;
        }
        
        return total;
    }

Margin Call

part 1

//     Our goal is to build a simplified version of a real Robinhood system that reads a customer's trades from a stream, maintains what they own, and rectifies running out of cash (through a process called a "margin call", which we'll define later). We’re looking for clean code, good naming, testing, etc. We're not particularly looking for the most performant solution.

//     **Step 1 (tests 1-4): Parse trades and build a customer portfolio**

//     Your input will be a list of trades, each of which is itself a list of strings in the form [timestamp, symbol, B/S (for buy/sell), quantity, price], e.g.

//     [["1", "AAPL", "B", "10", "10"], ["3", "GOOG", "B", "20", "5"], ["10", "AAPL", "S", "5", "15"]]

//     is equivalent to buying 10 shares (i.e. units) of AAPL for 5 each at timestamp 3, and selling 5 shares of AAPL for $15 at timestamp 10.

//     **Input assumptions:**

//     - The input is sorted by timestamp
//     - All numerical values are nonnegative integers
//     - Trades will always be valid (i.e. a customer will never sell more of a stock than they own).

//     From the provided list of trades, our goal is to maintain the customer's resulting portfolio (meaning everything they own), **assuming they begin with $1000**. For instance, in the above example, the customer would end up with $875, 5 shares of AAPL, and 20 shares of GOOG. You should return a list representing this portfolio, formatting each individual position as a list of strings in the form [symbol, quantity], using 'CASH' as the symbol for cash and sorting the remaining stocks alphabetically based on symbol. For instance, the above portfolio would be represented as

//     [["CASH", "875"], ["AAPL", "5"], ["GOOG", "20"]]
  // PART 1:  maintain the customer's resulting portfolio
    public static List<List<String>> buildPortfolio(List<List<String>> trades) {
		int cash = 1000;
        // user's holding, sort alphabetically
        TreeMap<String, Integer> holdings = new TreeMap<>();
        
		for (List<String> trade : trades) { 
            String symbol = trade.get(1);
            String operation = trade.get(2);
            int quantity = Integer.parseInt(trade.get(3));
            int price = Integer.parseInt(trade.get(4));
            
            if (operation.equals("B")) {
                holdings.put(symbol, holdings.getOrDefault(symbol, 0) + quantity);
                cash -= price * quantity;
            } else {
                holdings.put(symbol, holdings.get(symbol) - quantity);
                cash += price * quantity;
            }
        }
        
        // build result
        List<List<String>> res = new ArrayList<>();
        res.add(new ArrayList<>(Arrays.asList("CASH", cash + "")));
        for (String s : holdings.keySet()) {
            int quantity = holdings.get(s);
            // APPL, 0 - 不显示
            if (quantity != 0) {
                res.add(new ArrayList<>(Arrays.asList(s, quantity + "")));
            }
        }
        
        return res;
	 }


part 2


//    **Step 2 (tests 5-7): Margin calls**

//     If the customer ever ends up with a negative amount of cash **after a buy**, they then enter a process known as a **margin call** to correct the situation. In this process, we forcefully sell stocks in the customer's portfolio (sometimes including the shares we just bought) until their cash becomes non-negative again.

//     We sell shares from the most expensive to least expensive shares (based on each symbol's most-recently-traded price) with ties broken by preferring the alphabetically earliest symbol. Assume we're able to sell any number of shares in a symbol at that symbol's most-recently-traded price.

//     For example, for this input:

//     ```
//     [["1", "AAPL", "B", "10", "100"],
//     ["2", "AAPL", "S", "2", "80"],
//     ["3", "GOOG", "B", "15", "20"]]

//     ```

//     The customer would be left with 8 AAPL shares, 15 GOOG shares, and 80 a share) to cover the deficit. Afterwards, they would have 6 shares of AAPL, 15 shares of GOOG, and a cash balance of $20.

//     The expected output would be

//     [["CASH", "20"], ["AAPL", "6"], ["GOOG", "15"]]

 // PART 2: margin call
    public static List<List<String>> buildPortfolio(List<List<String>> trades) {
		int cash = 1000;
        
        // sort alphabetically
        TreeMap<String, Integer> holdings = new TreeMap<>();
        
        // recent trading price
        Map<String, Integer> lastPrice = new HashMap<>();
        
		for (List<String> trade : trades) { 
            String symbol = trade.get(1);
            String operation = trade.get(2);
            int quantity = Integer.parseInt(trade.get(3));
            int price = Integer.parseInt(trade.get(4));
            
            // update recent price
            lastPrice.put(symbol, price);
            
            if (operation.equals("B")) {
                holdings.put(symbol, holdings.getOrDefault(symbol, 0) + quantity);
                cash -= price * quantity;
                
                if (cash < 0) {
                    // determine which stock to sell to cover the gap
                    List<String> symbols = new ArrayList<>(holdings.keySet());
                    
                    // sorting logic 1.most exp recent price 2. tiebreak-alphabetic 
                    symbols.sort((a, b) -> {
                        if (lastPrice.get(a) != lastPrice.get(b)) {
                            return lastPrice.get(b) - lastPrice.get(a);
                        } else {
                            return a.compareTo(b);
                        }
                    });
                    
                    // chose the ones to sell
                    for (String s : symbols) {
                        int qty = holdings.get(s);
                        int p = lastPrice.get(s);
                        
                        while (qty > 0 && cash < 0) {
                            cash += p;
                            qty--;
                        }
                        
                        if (qty == 0) {
                            holdings.remove(s);
                        } else {
                            holdings.put(s, qty);
                        }
                        
                        if (cash >= 0) {
                            break;
                        }
                    }
                    
                }  
            } else { // remain same
                holdings.put(symbol, holdings.get(symbol) - quantity);
                cash += price * quantity;
            }
        }
        
        // build result, remain same
        List<List<String>> res = new ArrayList<>();
        res.add(new ArrayList<>(Arrays.asList("CASH", cash + "")));
        for (String s : holdings.keySet()) {
            int quantity = holdings.get(s);
            // APPL, 0 - 不显示
            if (quantity != 0) {
                res.add(new ArrayList<>(Arrays.asList(s, quantity + "")));
            }
        }
        
        return res;
	 }

part 3

House Street Order Match

Part 1


// "static void main" must be defined in a public class.
// A trade is defined as a string containing the 4 properties given below separated by commas:

//     Symbol (4 alphabetical characters, left-padded by spaces)
//     Side (either "B" or "S" for buy or sell)
//     Quantity (4 digits, left-padded by zeros)
//     ID (6 alphanumeric characters)
//     e.g.
//     "AAPL,B,0100,ABC123"

//     which represents a trade of a buy of 100 shares of AAPL with ID "ABC123"

//     Given two lists of trades - called "house" and "street" trades, write code to create groups of matches between trades and return a list of unmatched house and street trades sorted alphabetically. Without any matching, the output list would contain all elements of both house and street trades. There are many ways to match trades, the first and most important way is an exact match:

//     An exact match is a pair of trades containing exactly one house trade and exactly one street trade with identical symbol, side, quantity, and ID
//     Note: Trades are distinct but not unique

//     For example, given the following input:
//     house_trades:
//     [ "AAPL,B,0100,ABC123", "AAPL,B,0100,ABC123", "GOOG,S,0050,CDC333" ]
//     street_trades:
//     [ " FB,B,0100,GBGGGG", "AAPL,B,0100,ABC123" ]
//     We would expect the following output:
//     [ " FB,B,0100,GBGGGG", "AAPL,B,0100,ABC123", "GOOG,S,0050,CDC333" ]

//     Because the first (or second) house trade and second street trade form an exact match, leaving behind three unmatched trades.

public List<String> exactTradeMatch(List<String> house, List<String> street) {
        Map<String, Integer> houseFreq = getOrderFreq(house);
        Map<String, Integer> streetFreq = getOrderFreq(street);
        
        // Remove exact matches
        for (String h : houseFreq.keySet()) {
            if (streetFreq.containsKey(h)) {
                // take min, is matched
                int matched = Math.min(houseFreq.get(h), streetFreq.get(h));
                houseFreq.put(h, houseFreq.get(h) - matched);
                streetFreq.put(h, streetFreq.get(h) - matched);
            }
        }
        
        // collecting unmatched, add to res
        List<String> unmatched = new ArrayList<>();
        for (String s : houseFreq.keySet()) {
            for (int i = 0; i < houseFreq.get(s); i++) {
                unmatched.add(s); // repeat freq times
            }
        }
        for (String s : streetFreq.keySet()) {
            for (int i = 0; i < streetFreq.get(s); i++) {
                unmatched.add(s); // repeat freq times
            }
        }
        // sort alphabetically
        Collections.sort(unmatched);
        
        return unmatched;
    }
    
    // calculate frequency of order [xx,xx,xx]
    public static Map<String, Integer> getOrderFreq(List<String> list) {
        Map<String, Integer> map = new HashMap<>();
        
        for (String s : list) {
            map.put(s, map.getOrDefault(s, 0) + 1);
        }
        return map;
    }

Part 2

//  (same symbol + side + quantity)

 public List<String> getUnmatchTrades(List<String> house, List<String> street) {
        // Group trades by "symbol + quantity" into houseMap and streetMap
        Map<String, List<String>> houseMap = getTradeMap(house);
        Map<String, List<String>> streetMap = getTradeMap(street);

        // Step 1: Remove exact matches between house and street
        removeMatches(houseMap, streetMap, "exact");

        // Step 2: Remove attribute matches (same symbol + side + quantity) between house and street
        removeMatches(houseMap, streetMap, "attribute");

        // Step 3: Combine all unmatched trades from both maps
        List<String> unmatched = new ArrayList<>();
        for (List<String> trades : houseMap.values()) {
            unmatched.addAll(trades);
        }
        for (List<String> trades : streetMap.values()) {
            unmatched.addAll(trades);
        }

        // Return sorted unmatched trades
        Collections.sort(unmatched);
        return unmatched;
    }

  
     //* Groups trades into a map where each key is "<symbol> <side><quantity>",
    // * and the value is a list of full trade strings belonging to that key.
     //* This enables grouping trades with the same symbol and quantity.
     //*/
    private Map<String, List<String>> getTradeMap(List<String> trades) {
        Map<String, List<String>> tradeMap = new HashMap<>();
        for (String trade : trades) {
            String[] parts = trade.split(",", 4);  // symbol, side, quantity, id
            String symbol = parts[0];
            String side = parts[1];
            String quantity = parts[2];
            // Now grouping by all 3 fields: symbol + side + quantity
            String key = symbol + "," + side + "," + quantity;
            if (!tradeMap.containsKey(key)) {
                tradeMap.put(key, new ArrayList<>());
            }
            tradeMap.get(key).add(trade);
        }
        return tradeMap;
    }

   // /**
    /// * Performs matching between house and street trades based on match type:
    // * - "exact": match if the entire trade string matches
    /// * - "attribute": match if symbol + side + quantity are equal (ignore ID)
    // * Matched trades are removed from both house and street maps.
    // */
    private void removeMatches(Map<String, List<String>> houseMap, 
    Map<String, List<String>> streetMap, 
    String matchType) {
        // Loop through each group key in houseMap (e.g., "AAPL 0100")
        List<String> keys = new ArrayList<>(houseMap.keySet());
        Collections.sort(keys);  // For deterministic matching order

        for (String key : keys) {
            if (!streetMap.containsKey(key)) continue;

            List<String> hTrades = new ArrayList<>(houseMap.get(key));  // Copy to avoid modifying original while iterating
            List<String> sTrades = new ArrayList<>(streetMap.get(key));
            Collections.sort(hTrades);  // Lexical order priority
            Collections.sort(sTrades);

            // Store indices of trades that will be removed after matching
            List<Integer> hToRemove = new ArrayList<>();
            List<Integer> sToRemove = new ArrayList<>();

            // Try to match each house trade with a street trade
            for (int i = 0; i < hTrades.size(); i++) {
                String hTrade = hTrades.get(i);
                String[] hParts = hTrade.split(",", 4);

                for (int j = 0; j < sTrades.size(); j++) {
                    if (sToRemove.contains(j)) continue;  // Already matched

                    String sTrade = sTrades.get(j);
                    String[] sParts = sTrade.split(",", 4);

                    if ("exact".equals(matchType)) {
                        // Exact match: full string must be equal
                        if (hTrade.equals(sTrade)) {
                            hToRemove.add(i);
                            sToRemove.add(j);
                            break;
                        }
                    } else if ("attribute".equals(matchType)) {
                        // Attribute match: symbol + side + quantity must match (ignore ID)
                        if (hParts[0].equals(sParts[0]) &&
                            hParts[1].equals(sParts[1]) &&
                            hParts[2].equals(sParts[2])) {
                            hToRemove.add(i);
                            sToRemove.add(j);
                            break;
                        }
                    }
                }
            }

            // Update the maps to remove matched trades, keep only unmatched
            houseMap.put(key, getRemaining(hTrades, hToRemove));
            streetMap.put(key, getRemaining(sTrades, sToRemove));
        }
    }

    /**
     * Given a list of trades and a list of indices to remove,
     * returns a new list containing only the trades that were not removed.
     */
    private List<String> getRemaining(List<String> trades, List<Integer> toRemove) {
        List<String> remaining = new ArrayList<>();
        for (int i = 0; i < trades.size(); i++) {
            if (!toRemove.contains(i)) {
                remaining.add(trades.get(i));
            }
        }
        return remaining;
    }

part3

Maximum Multiplier Path

 class Solution {
        public int maxMultiplierProduct(int n, List<List<Integer>> edges, int start, int end) {
            // build graph
            Map<Integer, Map<Integer, Integer>> graph = new HashMap<>();

            for (List<Integer> edge : edges) {
                int from = edge.get(0);
                int to = edge.get(1);
                int weight = edge.get(2);

                if (!graph.containsKey(from)) {
                    graph.put(from, new HashMap<>());
                }
                graph.get(from).put(to, weight);
            }
            
            // dfs to find max product
            // dfs tells 1. if we can find path from start to end
            // 2. if there is a path from start to end, return max prod
            int max = dfs(graph, start, end, new HashSet<>());

            return max == -1 ? -1 : max;
         }

        // returns max product of path going from cur
         public int dfs(Map<Integer, Map<Integer, Integer>> graph, 
                         int cur, int end, 
                         Set<Integer> visited) {  
         
            if (cur == end) {
                return 1;
            }

            visited.add(cur);

            // Return -1 later if no valid path was found from curr to end
            int max = -1;

            // not reached leaf node
            if (graph.containsKey(cur)) {
                for (Map.Entry<Integer, Integer> entry : graph.get(cur).entrySet()) {
                    int adj = entry.getKey();
                    int weight = entry.getValue();

                    // cant go back!
                    if (visited.contains(adj)) {
                        continue;
                    }

                    int adjProduct = dfs(graph, adj, end, visited);
                    // means there is a valid path was found from curr to end
                    if (adjProduct != -1) {
                        max = Math.max(max, weight * adjProduct);
                    }
                }
            }
            // backtrack
            visited.remove(cur);
            return max;
         }
    }

Portofolio Value Optmization

Greedy strategy: max ROI

class Solution {

    // Represents one security's data
    static class Security {
        double curPrice;
        double futurePrice;
        double maxShares;
        double roi; // return on investment

        Security(double curPrice, double futurePrice, double maxShares) {
            this.curPrice = curPrice;
            this.futurePrice = futurePrice;
            this.maxShares = maxShares;
            this.roi = futurePrice / curPrice;
        }
    }

    public double portValOptimization(int initialFund, List<List<String>> securities) {
        // A list that holds all the profitable securities
        List<Security> list = new ArrayList<>();

        // Parse input and filter only profitable securities
        for (List<String> sec : securities) {
            double curPrice = Double.parseDouble(sec.get(1));
            double futurePrice = Double.parseDouble(sec.get(2));
            double maxShares = Double.parseDouble(sec.get(3));

            // profitable
            if (futurePrice > curPrice) {
                list.add(new Security(curPrice, futurePrice, maxShares));
            }
        }

        // Sort by ROI in desc
        // this will warn!!! list.sort((a, b) -> (b.roi - a.roi)); 
        list.sort((a, b) -> Double.compare(b.roi, a.roi));

        // Greedily purchase shares with available funds
        double totalFutureValue = 0;
        double remainingFund = initialFund;

        for (Security sec : list) {
            double affordableShares = remainingFund / sec.curPrice;

            if (affordableShares >= sec.maxShares) {
                // Buy full allowed shares
                totalFutureValue += sec.maxShares * sec.futurePrice;
                remainingFund -= sec.maxShares * sec.curPrice;
            } else {
                // Buy as many fractional shares as we can afford
                totalFutureValue += affordableShares * sec.futurePrice;
                return totalFutureValue;  // budget exhausted
            }
        }

        // Step 4: Add leftover cash as-is (assuming no future growth on cash)
        return totalFutureValue + remainingFund;
    }
}