Lam NguyenLibrary

Library · Data Structures · compression

Compressed Sparse Row

#pragma once
#include <bits/stdc++.h>
using namespace std;

using vi = vector<int>;

#define REP(i, n) for (int i = 0; i < (n); i++)
#define FOR(i, a, b) for (int i = (a); i <= (b); i++)

#define sz(x) (int)((x).size())

#define pb push_back

template<typename T>
struct CSR {
    int n;
    bool built = false;
    vi offset;
    vi row;
    vector<T> data;

    CSR(int n = 0) : n(n) {}

    // stage entry x into row i; any order, any number of times
    void add(int i, const T& x) {
        assert(0 <= i && i < n && !built);
        row.pb(i);
        data.pb(x);
    }

    // group everything by row in one O(n + nnz) counting sort
    // nnz = number of non zero elements
    void build() {
        assert(!built);
        built = true;
        int m = sz(data);

        offset.assign(n+1, 0);
        for (int i : row) offset[i+1]++; // count entries per row
        FOR(i, 1, n) offset[i] += offset[i-1]; // prefix sums = row starts

        // scatter into each slice
        vi cur = offset;
        vector<T> tmp(m);
        REP(k, m) tmp[cur[row[k]]++] = data[k];
        
        swap(data, tmp);
        row.clear();
    }

    struct range {
        T *first, *last;
        T* begin() const { return first; }
        T* end() const { return last; }
        int size() const { return int(last - first); }
        bool empty() const { return first == last; }
    };

    range operator[](int i) {
        assert(built);
        return range{data.data() + offset[i], data.data() + offset[i+1]};
    }
};
# Compressed sparse row: entries grouped by row in one flat list.
# row i is data[offset[i]:offset[i+1]]


class CSR:
    def __init__(self, n: int):
        self.n = n
        self.built = False
        self.row, self.data = [], []

    # stage entry x into row i; any order, any number of times
    def add(self, i: int, x) -> None:
        assert 0 <= i < self.n and not self.built
        self.row.append(i)
        self.data.append(x)

    # group everything by row in one O(n + nnz) counting sort
    def build(self) -> None:
        assert not self.built
        self.built = True
        self.offset = [0] * (self.n + 1)
        for i in self.row: self.offset[i + 1] += 1 # count entries per row
        for i in range(1, self.n + 1): self.offset[i] += self.offset[i - 1] # prefix sums = row starts
        cur = self.offset[:]
        tmp = [None] * len(self.data)
        for i, x in zip(self.row, self.data): # scatter into each slice
            tmp[cur[i]] = x
            cur[i] += 1
        self.data, self.row = tmp, []

    def __getitem__(self, i: int) -> list:
        assert self.built
        return self.data[self.offset[i]:self.offset[i + 1]]