'How to represent sparse matrix (including zero elements) in C using linked list?

I want a code that represents a sparse matrix including zero elements using linked list, I basically want the output to be like this:

1  0  0  0  


0  6  1  0  

I already have a code that does the representation but it only gives as an output the non-Zero elements like so :

input :

2  0  0

0  0  0

0  0  0
      

output :

row : 1 col : 1 value : 2

Here is the code :

// C program for Sparse Matrix Representation
// using Linked Lists

#include<stdio.h>

#include<stdlib.h>

 
// Node to represent sparse matrix

struct Node

{

    int value;

    int row_position;

    int column_postion;

    struct Node *next;

};
 

// Function to create new node

void create_new_node(struct Node** start, int non_zero_element,

                     int row_index, int column_index )

{

    struct Node *temp, *r;

    temp = *start;

    if (temp == NULL)

    {

        // Create new node dynamically

        temp = (struct Node *) malloc (sizeof(struct Node));

        temp->value = non_zero_element;

        temp->row_position = row_index;

        temp->column_postion = column_index;

        temp->next = NULL;

        *start = temp;

 
    }

    else

    {

        while (temp->next != NULL)

            temp = temp->next;

 
        // Create new node dynamically

        r = (struct Node *) malloc (sizeof(struct Node));

        r->value = non_zero_element;

        r->row_position = row_index;

        r->column_postion = column_index;

        r->next = NULL;

        temp->next = r;

 
    }

}

 
// This function prints contents of linked list
// starting from start

void PrintList(struct Node* start)

{

    struct Node *temp, *r, *s;

    temp = r = s = start;

 
    printf("row_position: ");

    while(temp != NULL)

    {

 
        printf("%d ", temp->row_position);

        temp = temp->next;

    }

    printf("\n");

 
    printf("column_postion: ");

    while(r != NULL)

    {

        printf("%d ", r->column_postion);

        r = r->next;

    }

    printf("\n");

    printf("Value: ");

    while(s != NULL)

    {

        printf("%d ", s->value);

        s = s->next;

    }

    printf("\n");

}


Solution 1:[1]

Your code was incomplete.

It could/should do the insertion of a new node in row/column sorted order. But, it appears it just appended to the end of the list. This makes the ordered printing of the matrix difficult. And, it does not allow for [true] random access (e.g. the same node is updated more than once).

So, I had to refactor the code.

It's no more extra time to scan the list and look for an insertion point that is in sorted order. That's because the function that inserts a new node must check for an already existing node to make the sparse matrix work like a real matrix.

I've included some other scan/match functions and created a matrix "pretty" print function.

The print code could be rewritten as it makes many calls to a nodefind function. The print could be rewritten to just loop through all nodes and print them in their correct columns.

The code is annotated:

#include <stdio.h>
#include <stdlib.h>

// node control
typedef struct node {
    int node_row;
    int node_col;
    int node_val;
    struct node *node_next;
} node_t;

// matrix control
typedef struct {
    node_t *mtx_head;                   // head of list

    // insertion temporaries
    node_t *mtx_bef;                    // node before node to insert
    node_t *mtx_aft;                    // node after node to insert

    // printing temporaries
    int mtx_rowmin;                     // minumum row
    int mtx_rowmax;                     // maximum row
    int mtx_colmin;                     // minumum column
    int mtx_colmax;                     // maximum column
    int mtx_valmax;                     // maximum matrix value
} mtx_t;

#define MAXOF(_lhs,_rhs) \
    if (_rhs > _lhs) \
        _lhs = _rhs

#define MINOF(_lhs,_rhs) \
    if (_rhs < _lhs) \
        _lhs = _rhs

#define SEPOUT(_sep) \
    for (int idx = 0;  idx < xwid;  ++idx) \
        fputc(_sep,stdout)

// colscan -- scan a given row for column insertion point
// RETURNS: add pointer (or NULL)
node_t *
colscan(mtx_t *mtx,node_t *cur,node_t *add)
{

    for (;  cur != NULL;  cur = cur->node_next) {
        // stop if row changes -- we are beyond the insertion point
        if (cur->node_row > add->node_row) {
            mtx->mtx_aft = cur;
            break;
        }

        // duplicate node -- just update existing value and release new node
        if (cur->node_col == add->node_col) {
            cur->node_val = add->node_val;
            free(add);
            add = NULL;
            break;
        }

        // point to node _after_ insertion point
        mtx->mtx_aft = cur;

        // stop if column becomes higher
        if (cur->node_col > add->node_col)
            break;

        // point to node _before_ insertion point
        mtx->mtx_bef = cur;
    }

    return add;
}

// nodenew -- add new node to matrix
void
nodenew(mtx_t *mtx,int row,int col,int val)
{

    // get new node to insert
    node_t *add = calloc(1,sizeof(*add));
    add->node_row = row;
    add->node_col = col;
    add->node_val = val;

    mtx->mtx_bef = NULL;
    mtx->mtx_aft = NULL;

    // scan all nodes looking for place to insert row
    for (node_t *cur = mtx->mtx_head;  cur != NULL;  cur = cur->node_next) {
        // found start of matching row -- scan for matching column
        if (cur->node_row == add->node_row) {
            add = colscan(mtx,cur,add);
            break;
        }

        // went beyond row -- stop -- we insert before this node
        if (cur->node_row > add->node_row) {
            mtx->mtx_aft = cur;
            break;
        }

        // remember node that is lower than the one to insert
        mtx->mtx_bef = cur;
    }

    // insert new node
    do {
        // node is a duplicate -- value has already been updated (by colscan)
        if (add == NULL)
            break;

        node_t *prev = mtx->mtx_bef;

        // insert/append the new node
        if (prev != NULL) {
            add->node_next = prev->node_next;
            prev->node_next = add;
            break;
        }

        // add to empty list or before start of existing list
        add->node_next = mtx->mtx_aft;
        mtx->mtx_head = add;
    } while (0);
}

// nodefind -- find existing node
node_t *
nodefind(mtx_t *mtx,int row,int col)
{
    node_t *cur = mtx->mtx_head;

    for (;  cur != NULL;  cur = cur->node_next) {
        if ((cur->node_row == row) && (cur->node_col == col))
            break;
    }

    return cur;
}

// nodedel -- delete existing node
// RETURNS: 1=found, 0=no match
int
nodedel(mtx_t *mtx,int row,int col)
{
    node_t *cur = mtx->mtx_head;
    node_t *prev = NULL;
    node_t *next;
    int match = 0;

    for (;  cur != NULL;  prev = cur, cur = next) {
        next = cur->node_next;

        if ((cur->node_row == row) && (cur->node_col == col)) {
            if (prev != NULL)
                prev->node_next = next;
            else
                mtx->mtx_head = next;
            free(cur);
            match = 1;
            break;
        }
    }

    return match;
}

// mtxdimset -- get matrix dimensions
void
mtxdimset(mtx_t *mtx)
{
    node_t *cur = mtx->mtx_head;

    // NOTE: with some effort, nodenew/nodedel could keep the dimensions up to
    // date (but, currently, they don't)
    do {
        // empty matrix
        if (cur == NULL) {
            mtx->mtx_rowmin = -1;
            mtx->mtx_rowmax = -2;

            mtx->mtx_colmin = -1;
            mtx->mtx_colmax = -2;

            mtx->mtx_valmax = 0;
            break;
        }

        mtx->mtx_rowmin = cur->node_row;
        mtx->mtx_rowmax = cur->node_row;

        mtx->mtx_colmin = cur->node_col;
        mtx->mtx_colmax = cur->node_col;

        mtx->mtx_valmax = cur->node_val;

        cur = cur->node_next;

        for (;  cur != NULL;  cur = cur->node_next) {
            MAXOF(mtx->mtx_valmax,cur->node_val);

            MINOF(mtx->mtx_rowmin,cur->node_row);
            MAXOF(mtx->mtx_rowmax,cur->node_row);

            MINOF(mtx->mtx_colmin,cur->node_col);
            MAXOF(mtx->mtx_colmax,cur->node_col);
        }
    } while (0);
}

// mtxprint -- print matrix
void
mtxprint(mtx_t *mtx,const char *who)
{
    node_t *cur = mtx->mtx_head;
    char fmt[100];

    // get row/column/value ranges
    mtxdimset(mtx);

    // get maximum number size (pretty print)
    int xwid = 0;
    int rwid = sprintf(fmt,"%d",mtx->mtx_rowmax);
    int cwid = sprintf(fmt,"%d",mtx->mtx_colmax);
    int vwid = sprintf(fmt,"%d",mtx->mtx_valmax);
    MAXOF(xwid,rwid);
    MAXOF(xwid,cwid);
    MAXOF(xwid,vwid);

    // get the format for printing
    sprintf(fmt," %%%dd",xwid);

    if (who != NULL)
        printf("MATRIX R:%d-%d C:%d-%d (from %s)\n",
            mtx->mtx_rowmin,mtx->mtx_rowmax,
            mtx->mtx_colmin,mtx->mtx_colmax,
            who);

    // output column headers
    printf(" ");
    SEPOUT(' ');
    for (int col = mtx->mtx_colmin;  col <= mtx->mtx_colmax;  ++col)
        printf(fmt,col);
    printf("\n");

    // output all rows
    for (int row = mtx->mtx_rowmin;  row <= mtx->mtx_rowmax;  ++row) {
        // output row number
        printf(fmt,row);

        // output all rows in the column
        // NOTE: this is very slow due to the repeated calls to nodefind
        for (int col = mtx->mtx_colmin;  col <= mtx->mtx_colmax;  ++col) {
            cur = nodefind(mtx,row,col);

            if (cur != NULL) {
                printf(fmt,cur->node_val);
                continue;
            }

            SEPOUT(' ');
            fputc('*',stdout);
        }

        printf("\n");
    }
}

// mtxdump -- dump out matrix nodes in linear order
void
mtxdump(mtx_t *mtx)
{

    for (node_t *cur = mtx->mtx_head;  cur != NULL;  cur = cur->node_next)
        printf("DMP: [%d,%d] = %d\n",
            cur->node_row,cur->node_col,cur->node_val);
}

int
main(void)
{

    mtx_t *mtx = calloc(1,sizeof(*mtx));

    enum {
        ROWMAX = 20,
        COLMAX = 7
    };

    // ordinary matrix (for cross check)
    int arr[ROWMAX][COLMAX];
    for (int rowcur = 0;  rowcur < ROWMAX;  ++rowcur) {
        for (int colcur = 0;  colcur < COLMAX;  ++colcur)
            arr[rowcur][colcur] = -1;
    }

    // add random row/column/data
    for (int idx = 0;  idx < (ROWMAX * COLMAX) / 2;  ++idx) {
        int rowcur = rand() % ROWMAX;
        int colcur = rand() % COLMAX;
        int valcur = rand() % (COLMAX * ROWMAX);

#if DEBUG
        printf("\n");
#endif
        int valold = arr[rowcur][colcur];
        printf("%s: [%d,%d] = %d\n",
            (valold < 0) ? "ADD" : "UPD",rowcur,colcur,valcur);
        arr[rowcur][colcur] = valcur;
        nodenew(mtx,rowcur,colcur,valcur);

#if DEBUG
        mtxdump(mtx);
#endif
    }

    printf("\n");
    mtxprint(mtx,"FIN");

    // cross check
    printf("\n");
    printf("cross check ...\n");
    int miss = 0;
    for (int rowcur = 0;  rowcur < ROWMAX;  ++rowcur) {
        for (int colcur = 0;  colcur < COLMAX;  ++colcur) {
            int valexp = arr[rowcur][colcur];

            node_t *cur = nodefind(mtx,rowcur,colcur);

            int valact;
            if (cur != NULL)
                valact = cur->node_val;
            else
                valact = -1;

            if (valact != valexp) {
                printf("DIF: [%d,%d] = %d/%d\n",rowcur,colcur,valexp,valact);
                miss = 1;
            }
        }
    }
    if (! miss)
        printf("all values match\n");

    return 0;
}

Here is the program output:

ADD: [3,4] = 37
ADD: [15,1] = 115
ADD: [6,2] = 29
ADD: [1,2] = 47
ADD: [10,4] = 83
ADD: [6,3] = 106
ADD: [12,2] = 31
ADD: [8,5] = 9
ADD: [2,5] = 82
ADD: [3,1] = 75
ADD: [9,0] = 122
ADD: [18,3] = 107
ADD: [13,0] = 51
ADD: [2,1] = 53
ADD: [1,0] = 104
ADD: [17,6] = 44
UPD: [15,1] = 73
ADD: [6,0] = 120
ADD: [16,2] = 102
ADD: [10,1] = 101
ADD: [5,6] = 4
ADD: [7,3] = 5
ADD: [6,4] = 73
ADD: [17,5] = 115
ADD: [2,3] = 94
ADD: [7,4] = 84
ADD: [3,0] = 7
UPD: [8,5] = 98
ADD: [8,4] = 63
ADD: [11,1] = 99
ADD: [12,4] = 96
ADD: [8,0] = 92
ADD: [6,6] = 74
ADD: [19,4] = 70
ADD: [14,0] = 127
UPD: [1,0] = 62
ADD: [17,0] = 132
ADD: [16,4] = 100
UPD: [6,6] = 105
ADD: [9,6] = 99
ADD: [0,3] = 31
ADD: [17,3] = 11
ADD: [1,5] = 29
UPD: [7,4] = 136
UPD: [17,5] = 106
ADD: [5,1] = 43
ADD: [19,5] = 28
ADD: [11,4] = 109
UPD: [3,4] = 10
ADD: [8,3] = 115
ADD: [0,2] = 116
UPD: [3,4] = 5
ADD: [6,5] = 81
ADD: [15,4] = 8
ADD: [4,0] = 41
ADD: [10,0] = 20
ADD: [14,2] = 4
ADD: [14,3] = 36
UPD: [3,0] = 87
ADD: [5,3] = 56
UPD: [12,4] = 77
ADD: [8,2] = 107
ADD: [14,1] = 98
ADD: [15,0] = 137
ADD: [15,2] = 38
UPD: [8,5] = 31
ADD: [8,1] = 76
ADD: [4,4] = 23
ADD: [13,2] = 126
ADD: [0,4] = 58

MATRIX R:0-19 C:0-6 (from FIN)
       0   1   2   3   4   5   6
   0   *   * 116  31  58   *   *
   1  62   *  47   *   *  29   *
   2   *  53   *  94   *  82   *
   3  87  75   *   *   5   *   *
   4  41   *   *   *  23   *   *
   5   *  43   *  56   *   *   4
   6 120   *  29 106  73  81 105
   7   *   *   *   5 136   *   *
   8  92  76 107 115  63  31   *
   9 122   *   *   *   *   *  99
  10  20 101   *   *  83   *   *
  11   *  99   *   * 109   *   *
  12   *   *  31   *  77   *   *
  13  51   * 126   *   *   *   *
  14 127  98   4  36   *   *   *
  15 137  73  38   *   8   *   *
  16   *   * 102   * 100   *   *
  17 132   *   *  11   * 106  44
  18   *   *   * 107   *   *   *
  19   *   *   *   *  70  28   *

cross check ...
all values match

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1