Saturday, May 10, 2025

DataStructure Project #2: E-commerce Solution

 

Project Scenario: E-commerce Warehouse Inventory System


Problem Statement: Design an inventory management system for an e-commerce warehouse that needs to:

  1. Track products in real-time

  2. Support fast lookups by product ID and category

  3. Maintain products sorted by price for quick filtering

  4. Manage inventory levels and alert when stock is low

  5. Show product relationships (frequently bought together)

This project implements an Inventory Management System for an e-commerce warehouse using various Data Structures and Algorithms to efficiently manage products, track inventory, and provide real-time insights.


Key Features:

  • Product Management (Add, Update, Delete)

  • Real-time Inventory Tracking

  • Low Stock Alerts

  • Category-wise Product Filtering

  • Price-based Sorting & Searching

  • Product Relationship Management (Frequently Bought Together)


2. System Architecture

The system is built using the following Data Structures and Algorithms:

ComponentData Structure/AlgorithmPurpose
Product LookupHashMap (ConcurrentHashMap)O(1) access by Product ID
Price-based SortingTreeSet (Red-Black Tree)Maintains sorted order by price
Low Stock AlertsPriorityQueue (Min-Heap)Efficiently tracks low-stock items
Category IndexHashMap<String, List<Product>>Groups products by category
Product RelationshipsGraph (Adjacency List)Tracks frequently bought together items
Price FilteringBinary SearchEfficient range queries
SortingQuickSortAlternative sorting method

Solution Implementation


import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

class Product implements Comparable<Product> {
    String id;
    String name;
    String category;
    double price;
    int quantity;
    List<String> relatedProducts;
    
    public Product(String id, String name, String category, double price, int quantity) {
        this.id = id;
        this.name = name;
        this.category = category;
        this.price = price;
        this.quantity = quantity;
        this.relatedProducts = new ArrayList<>();
    }
    
    public void addRelatedProduct(String productId) {
        relatedProducts.add(productId);
    }
    
    @Override
    public int compareTo(Product other) {
        return Double.compare(this.price, other.price);
    }
    
    @Override
    public String toString() {
        return String.format("%s - %s ($%.2f, Qty: %d)", id, name, price, quantity);
    }
}

class InventoryManagementSystem {
    // Hash Table for O(1) product lookup by ID
    private Map<String, Product> productTable;
    
    // Tree structure for products sorted by price
    private TreeSet<Product> productsByPrice;
    
    // Graph for product relationships
    private Map<String, List<String>> productGraph;
    
    // Heap for low stock alerts
    private PriorityQueue<Product> lowStockAlert;
    
    // Array for category index
    private Map<String, List<Product>> categoryIndex;
    
    public InventoryManagementSystem() {
        productTable = new ConcurrentHashMap<>(); // Thread-safe for real-time updates
        productsByPrice = new TreeSet<>();
        productGraph = new HashMap<>();
        lowStockAlert = new PriorityQueue<>(Comparator.comparingInt(p -> p.quantity));
        categoryIndex = new HashMap<>();
    }
    
    // Add product to system (O(log n) for TreeSet)
    public void addProduct(Product product) {
        productTable.put(product.id, product);
        productsByPrice.add(product);
        
        // Maintain category index
        categoryIndex.computeIfAbsent(product.category, k -> new ArrayList<>()).add(product);
        
        // Add to low stock monitoring
        checkLowStock(product);
        
        // Initialize graph node
        productGraph.putIfAbsent(product.id, new ArrayList<>());
    }
    
    // Binary search in sorted products by price (O(log n))
    public List<Product> getProductsInPriceRange(double min, double max) {
        Product dummyMin = new Product("", "", "", min, 0);
        Product dummyMax = new Product("", "", "", max, 0);
        
        return new ArrayList<>(productsByPrice.subSet(dummyMin, true, dummyMax, true));
    }
    
    // O(1) lookup by product ID
    public Product getProductById(String id) {
        return productTable.get(id);
    }
    
    // Get products by category (O(1) for access, O(n) for copy)
    public List<Product> getProductsByCategory(String category) {
        return new ArrayList<>(categoryIndex.getOrDefault(category, Collections.emptyList()));
    }
    
    // Add relationship between products (graph edge)
    public void addProductRelationship(String productId1, String productId2) {
        if (productTable.containsKey(productId1) && productTable.containsKey(productId2)) {
            productGraph.get(productId1).add(productId2);
            productGraph.get(productId2).add(productId1);
            
            // Also add to the product objects
            productTable.get(productId1).addRelatedProduct(productId2);
            productTable.get(productId2).addRelatedProduct(productId1);
        }
    }
    
    // Get frequently bought together products (graph neighbors)
    public List<Product> getRelatedProducts(String productId) {
        List<Product> related = new ArrayList<>();
        for (String relatedId : productGraph.getOrDefault(productId, Collections.emptyList())) {
            related.add(productTable.get(relatedId));
        }
        return related;
    }
    
    // Update inventory (O(n) for heap update)
    public void updateInventory(String productId, int quantityChange) {
        Product product = productTable.get(productId);
        if (product != null) {
            // Remove from TreeSet to maintain sorting
            productsByPrice.remove(product);
            
            // Update quantity
            product.quantity += quantityChange;
            
            // Add back to TreeSet
            productsByPrice.add(product);
            
            // Check stock level
            checkLowStock(product);
        }
    }
    
    private void checkLowStock(Product product) {
        if (product.quantity < 5) { // Threshold for low stock
            lowStockAlert.remove(product); // Remove if already present
            lowStockAlert.add(product);
        } else {
            lowStockAlert.remove(product);
        }
    }
    
    // Get low stock alerts (O(1) for peek)
    public List<Product> getLowStockAlerts() {
        List<Product> alerts = new ArrayList<>();
        PriorityQueue<Product> temp = new PriorityQueue<>(lowStockAlert);
        while (!temp.isEmpty()) {
            alerts.add(temp.poll());
        }
        return alerts;
    }
    
    // Merge two categories (using Union-Find concept)
    public void mergeCategories(String category1, String category2) {
        List<Product> list1 = categoryIndex.get(category1);
        List<Product> list2 = categoryIndex.get(category2);
        
        if (list1 != null && list2 != null) {
            list1.addAll(list2);
            categoryIndex.put(category2, list1);
        }
    }
    
    // Sort products by price using QuickSort (alternative to TreeSet)
    public List<Product> getProductsSortedByPrice() {
        List<Product> products = new ArrayList<>(productTable.values());
        quickSort(products, 0, products.size() - 1);
        return products;
    }
    
    private void quickSort(List<Product> products, int low, int high) {
        if (low < high) {
            int pi = partition(products, low, high);
            quickSort(products, low, pi - 1);
            quickSort(products, pi + 1, high);
        }
    }
    
    private int partition(List<Product> products, int low, int high) {
        Product pivot = products.get(high);
        int i = low - 1;
        
        for (int j = low; j < high; j++) {
            if (products.get(j).compareTo(pivot) <= 0) {
                i++;
                Collections.swap(products, i, j);
            }
        }
        Collections.swap(products, i + 1, high);
        return i + 1;
    }
    
    // Binary search in sorted array (alternative to TreeSet)
    public Product binarySearchByPrice(List<Product> sortedProducts, double targetPrice) {
        int left = 0;
        int right = sortedProducts.size() - 1;
        
        while (left <= right) {
            int mid = left + (right - left) / 2;
            double midPrice = sortedProducts.get(mid).price;
            
            if (midPrice == targetPrice) {
                return sortedProducts.get(mid);
            } else if (midPrice < targetPrice) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        
        return null;
    }
}

public class Main {
    public static void main(String[] args) {
        InventoryManagementSystem ims = new InventoryManagementSystem();
        
        // Add products
        ims.addProduct(new Product("P100", "Laptop", "Electronics", 999.99, 10));
        ims.addProduct(new Product("P101", "Mouse", "Electronics", 19.99, 50));
        ims.addProduct(new Product("P102", "Keyboard", "Electronics", 49.99, 30));
        ims.addProduct(new Product("P103", "Monitor", "Electronics", 199.99, 15));
        ims.addProduct(new Product("P104", "Desk", "Furniture", 149.99, 8));
        ims.addProduct(new Product("P105", "Chair", "Furniture", 79.99, 12));
        ims.addProduct(new Product("P106", "Mouse Pad", "Accessories", 9.99, 100));
        
        // Add relationships
        ims.addProductRelationship("P100", "P101"); // Laptop and Mouse
        ims.addProductRelationship("P100", "P102"); // Laptop and Keyboard
        ims.addProductRelationship("P100", "P103"); // Laptop and Monitor
        ims.addProductRelationship("P101", "P106"); // Mouse and Mouse Pad
        ims.addProductRelationship("P104", "P105"); // Desk and Chair
        
        // Update inventory
        ims.updateInventory("P100", -8); // Sell 8 laptops
        ims.updateInventory("P104", -5); // Sell 5 desks
        
        // Demonstrate features
        System.out.println("=== Low Stock Alerts ===");
        ims.getLowStockAlerts().forEach(System.out::println);
        
        System.out.println("\n=== Products in Electronics category ===");
        ims.getProductsByCategory("Electronics").forEach(System.out::println);
        
        System.out.println("\n=== Products between $50 and $200 ===");
        ims.getProductsInPriceRange(50, 200).forEach(System.out::println);
        
        System.out.println("\n=== Frequently bought with Laptop ===");
        ims.getRelatedProducts("P100").forEach(System.out::println);
        
        System.out.println("\n=== All products sorted by price (QuickSort) ===");
        ims.getProductsSortedByPrice().forEach(System.out::println);
        
        // Binary search demonstration
        System.out.println("\n=== Binary search for product at $49.99 ===");
        Product found = ims.binarySearchByPrice(ims.getProductsSortedByPrice(), 49.99);
        System.out.println(found != null ? found : "Not found");
    }
}


Key Data Structures and Algorithms Used:


  1. Hash Table (ConcurrentHashMap): For O(1) product lookups by ID in a thread-safe manner

  2. TreeSet (Red-Black Tree): To maintain products sorted by price

  3. Graph (Adjacency List): To represent product relationships (frequently bought together)

  4. Priority Queue (Min-Heap): For efficient low stock alerts

  5. QuickSort: Alternative sorting algorithm for products by price

  6. Binary Search: For efficient price-based searching in sorted arrays

  7. Recursion: Used in the QuickSort implementation

Real-Time Features:

  1. Concurrent Updates: Uses ConcurrentHashMap to handle simultaneous inventory updates

  2. Low Stock Monitoring: Heap data structure provides efficient alerts

  3. Fast Filtering: Multiple indexes (price tree, category hash) enable quick filtering

  4. Relationship Management: Graph structure tracks product bundles

  5. Efficient Sorting: Both TreeSet and QuickSort implementations provided for different use cases

No comments:

Post a Comment