Algorithm and data structure [C + +]: sparse table

Keywords: network

In many situations, the best way to store data is tables.

 
Array is the best choice when the data is densely clustered in a certain coordinate range.
For example, to store the scores of students in a class, there are 30 students in the class, numbered from 1-30, 10 courses, numbered from 1-10
Then you can use an array of 30x10 to store this score sheet, which is economical and convenient to use

But sometimes the data is very sparse. For example, a school has opened 1000 courses, and some students can choose courses at will. Now, a data structure is needed to store the scores of each student in each course.
Obviously, a student can only take more than ten courses a semester, so the data in the table is very sparse, most of the nodes are empty nodes, without data. If you use arrays, you need to allocate space in advance, not only the score table itself is very large, but also a lot of space is wasted.

At this point, you can use sparse tables.


Imagine now that there is a record of 3000 students in 1000 subjects.
Now we abstract every non empty table element into a node. Each column is strung up to form many columns, which are put into an array together. Each row is strung up to form many rows, which are put into an array together. In this way, the nodes selected from the whole dense array are weaved into a sparse net.
Just maintain and store this network

The following picture:

 

The attributes of the sparse table are as follows:


int rowTotalNum; / / total rows
int colTotalNum; / / total columns
LinkedList[] row; / / the pointer of the row list. The size should be the same as the following
LinkedList[] col; / / the pointer of the column list. The size should be the same as the following

The sparse table provides the following methods:


SparseTable(): constructor, initialization parameter
Void insert (int value, int rowIndex, int colIndex): inserts a value into a point (rowIndex, colIndex) of the sparse table
void remove(int rowIndex, int colIndex): delete the node of a coordinate (rowIndex, colIndex)
int getValue(int rowIndex, int colIndex): get the value of the specified coordinate (rowIndex, colIndex). If there is no such node, return - 1. There are two methods inside the method, by column and by row
void printSelfByRow(): print sparse table through row linked list
void printSelfByCol(): print sparse table through column linked list

LinkedList getCol(int colIndex): get the column list of a column
LinkedList getRow(int rowIndex): get the row list of a row

 

Here is the C + + Code:

#include<iostream>
#include<string>
#include<stdio.h>
using namespace std;

//Node class, representing the nodes of the linked list
class Node{
	public:
		int row;
		int col;
		int value;	//Value of storage node 
		Node* next;	//Store next node's pointer 
		
		Node(int aValue, int aRow, int aCol, Node* aNext = NULL){	//Constructor, the value of the node must be passed in, and the next node defaults to NULL
			this->value = aValue;
			this->next = aNext;
			this->row = aRow;
			this->col = aCol;
		}	
};

class LinkedList{	//Common unidirectional list class
	public:
		int length;		//The length of the linked list is not important. It is not used in the following methods, but it is maintained
		Node* head;		//Pointer to the chain header node
		Node* tail;		//Pointer to the node at the end of the list
	LinkedList(){
		length = 0;
		head = tail = 0;
	}

	//Treat the list as a list of columns, and insert the node at row row with value
	void insertToCol(int value, int row, int col){
		//Search in col where nextCol > col
		//Find the node whose coordinate is row in a column, and return the value of the previous node of the node
		//The returned results are as follows
			//1. NULL: indicates that the node is found and it is the head node, so there is no precursor node;
			//2. NULL: the list is empty
			//3. It is not empty, indicating that the precursor node is found, and the basis is that the coordinates of the successor node are larger than the coordinates to be inserted
		Node* aheadOfInsert = searchInCol(row); 
		//insertNode in that position

		//If the link list is empty and NULL is returned, the node needs to be inserted in the first position to update the head and tail
		if (aheadOfInsert == NULL && isEmpty()){
			head = tail = new Node(value, row, col);	
		}
		//If NULL is returned and the linked list is not empty, the first node should be added and the head should be updated
		else if (aheadOfInsert == NULL && !isEmpty()){
			head = new Node(value, row, col, head);
		}
		//Otherwise, insert normally
		else {
			aheadOfInsert->next = new Node(value, row, col, aheadOfInsert->next);
		}
		length++;
	}
	
	//Similarly, insert a node into the col coordinate of the row list
	void insertToRow(int value, int row, int col){
		//Search in col where nextCol > col
		Node* aheadOfInsert = searchInRow(col); 
		
		//insertNode in that position
		if (aheadOfInsert == NULL && isEmpty()){
			head = tail = new Node(value, row, col);	
		}
		else if (aheadOfInsert == NULL && !isEmpty()){
			head = new Node(value, row, col, head);
		}
		else {
			aheadOfInsert->next = new Node(value, row, col, aheadOfInsert->next);
		}
		length++;
	}
	

	//Take the current linked list as a column linked list, and delete the node whose coordinate is row
	void deleteByCol(int row, int col){
		//Search in col where nextCol > col
		//Search for the precursor node to delete
		//The returned results are as follows
			//1. NULL: indicates that the node is found and it is the head node, so there is no precursor node;
			//2. NULL: the list is empty
			//3. NULL: the node was not found
			//4. It is not empty, indicating that the precursor node is found, and the basis for finding is that the coordinates of the successor node are larger than the coordinates to be inserted, so the successor node is not necessarily the node to be deleted
		Node* aheadOfDelete = searchInCol(row);
		
		Node* deletedNode=NULL;
		//Case 1: it may be necessary to delete the head node and compare the coordinates. If it is necessary to delete it, delete the head node and update the head
		if (aheadOfDelete == NULL && !isEmpty()
			&& head->col==col && head->row==row){
			deletedNode = head;
			head = head->next;
			length--;
		}
		//Cases 2 and 3: no node found to delete, return
		else if (aheadOfDelete==NULL)
			return ;
		//Case 3: no node found to delete, return
		else if(aheadOfDelete->next == NULL)
			return ;
		//Otherwise, delete the node normally
		else if (aheadOfDelete->next->col==col && aheadOfDelete->next->row==row){
			deletedNode = aheadOfDelete->next;
			aheadOfDelete->next = aheadOfDelete->next->next;
			length--;
		}
		//Free the memory of this node
		delete deletedNode;
	}
	
	//Take the current linked list as a row linked list, and delete the node whose coordinate is col
	//Empathy
	void deleteByRow(int row, int col){
		//Search in col where nextCol > col
		Node* aheadOfDelete = searchInRow(col);
		//cout<<"deleting by row"<<endl;
		
		Node* deletedNode=NULL;
		if (aheadOfDelete == NULL && !isEmpty() 
			&& head->col==col && head->row==row){
			
			deletedNode = head;
			head = head->next;
			length--;
		}
		else if (aheadOfDelete==NULL)
			return ;
			
		else if(aheadOfDelete->next == NULL)
			return ;
		else if (aheadOfDelete->next->col==col && aheadOfDelete->next->row==row){
			deletedNode = aheadOfDelete->next;
			aheadOfDelete->next = aheadOfDelete->next->next;
			length--;
		}
		
		delete deletedNode;
	}
	
	//According to the column, find the value of the coordinate (row, col)
	int getValueByCol(int row, int col){
		//Search in col where nextCol > col
		//Search for the precursor node to find
		//The returned results are as follows
			//1. NULL: this node is found and it is the head node, so there is no precursor node;
			//2. NULL: this node is not found, which means that the list is not empty but NULL is returned;
			//3. NULL: the list is empty
			//4. NULL: the node was not found
			//5. It is not empty, indicating that the precursor node is found, and the basis for finding is that the coordinates of the successor node are larger than the coordinates to be inserted, so the successor node is not necessarily the node to be deleted
		
		Node* aheadOfNode = searchInCol(row);
		
		//Situation 1
		if (aheadOfNode == NULL && !isEmpty()
			&& head->col==col && head->row==row){
			return head->value;
		}
		//Situation 2
		else if (aheadOfNode == NULL && !isEmpty())
			return -1;
		//Situation 3
		else if (aheadOfNode==NULL && isEmpty())
			return -1;
		//Situation 4
		else if(aheadOfNode->next == NULL)
			return -1;
		//Situation 5
		else if (aheadOfNode->next->col==col && aheadOfNode->next->row==row){
			return aheadOfNode->next->value;
		}
		return -1;
	}

	//Find the value of the coordinate (row, col) according to the row
	int getValueByRow(int row, int col){
		//Search in col where nextCol > col
		//Search for the precursor node to find
		//The returned results are as follows
			//1. NULL: indicates that the node is found and it is the head node, so there is no precursor node;
			//2. NULL: the list is empty
			//3. NULL: the node was not found
			//4. It is not empty, indicating that the precursor node is found, and the basis for finding is that the coordinates of the successor node are larger than the coordinates to be inserted, so the successor node is not necessarily the node to be deleted
		
		Node* aheadOfNode = searchInRow(col);
		
		//Situation 1
		if (aheadOfNode == NULL && !isEmpty()
			&& head->col==col && head->row==row){
			return head->value;
		}
		//Situation 2
		else if (aheadOfNode == NULL && !isEmpty())
			return -1;
		//Situation 3
		else if (aheadOfNode==NULL && isEmpty())
			return -1;
		//Situation 4
		else if(aheadOfNode->next == NULL)
			return -1;
		//Situation 5
		else if (aheadOfNode->next->col==col && aheadOfNode->next->row==row){
			return aheadOfNode->next->value;
		}
		return -1;
	}
	
	
	//Take the current linked list as a column linked list and search the node whose row coordinate is row
	Node* searchInCol(int row){
		//return NULL if list is empty; 
		//If the link list is empty or the row coordinate of the head node is greater than the row coordinate of the node to be searched, return null
		if (isEmpty() || head->row>=row)
			return NULL;
		Node* now;
		//Loop lookup
		//There are two conditions for cycle termination:
			//1. At the end of the list, it is still not found. Corresponding to now - > next! = null
			//2. The row coordinate of the next node is smaller than the row coordinate to be searched, corresponding to now - > next - > row < row
		for (now=head; now->next!=NULL && now->next->row<row; now=now->next);
		return now;
	}

	//Empathy
	//Take the current linked list as a row linked list, and search for nodes whose column coordinates are row
	Node* searchInRow(int col){
		//return NULL if list is empty; 
		if (isEmpty() || head->col>=col)
			return NULL;
		Node* now;
		for (now=head; now->next!=NULL && now->next->col<col; now=now->next);
		return now;
	}

	
	
	int isEmpty(){			//Judge whether the chain list is empty or not. If the head pointer is 0, it means empty
		return head == 0;
	}	
	
	void printSelf(){	//Print linked list content	
		
	}
	
};

//Sparse table
class SparseTable{
	public:
	int rowTotalNum;	//Total number of banks
	int colTotalNum;	//Total column number
	LinkedList* row[500];	//The pointer of the row list. The size should be the same as the following
	LinkedList* col[500];	//Pointer to the column list, the size should be consistent with the following
	
	//Construction method
	SparseTable(){
		rowTotalNum = 500;
		colTotalNum = 500;
		for (int i=0; i<rowTotalNum; i++)
			row[i] = new LinkedList();
		for (int i=0; i<colTotalNum; i++)
			col[i] = new LinkedList();
	}
	//Insert value value into a point (rowIndex, colIndex) of sparse table
	void insert(int value ,int rowIndex, int colIndex){
		if (rowIndex >= rowTotalNum || colIndex >= colTotalNum)
			return ;
		if(getValue(rowIndex, colIndex) != -1)
			return ; 
		//Insert nodes into row list and column list respectively
		row[rowIndex]->insertToRow(value, rowIndex, colIndex);
		col[colIndex]->insertToCol(value, rowIndex, colIndex);
	}
	
	//Delete a node of a coordinate (rowIndex, colIndex)
	void remove(int rowIndex, int colIndex){
		if (rowIndex >= rowTotalNum || colIndex >= colTotalNum)
			return ;
		//Delete nodes on row and column lists
		row[rowIndex]->deleteByRow(rowIndex, colIndex);
		col[colIndex]->deleteByCol(rowIndex, colIndex);
	}

	//Get the value of the specified coordinate (rowIndex, colIndex)
	//If there is no such node, return - 1
	//There are two ways to do this, by column and by row
	int getValue(int rowIndex, int colIndex){
		if (rowIndex >= rowTotalNum || colIndex >= colTotalNum)
			return -1;
		//return col[colIndex]->getValueByCol(rowIndex, colIndex);
		return row[rowIndex]->getValueByRow(rowIndex, colIndex);
	}

	//Print sparse table through row linked list
	void printSelfByRow(){
		cout<<"By row--------------------------"<<endl;
		for (int i=0;i<=10;i++){
			for(Node* now=row[i]->head; now!=NULL; now=now->next){
				cout<<"("<<now->row<<", "<<now->col<<"): "<<now->value<<" ";
			}
			cout<<endl;
		}
	}

	//Printing sparse tables with column linked lists
	void printSelfByCol(){
		cout<<"By col--------------------------"<<endl;
		for (int i=0;i<=5;i++){
			for(Node* now=col[i]->head; now!=NULL; now=now->next){
				cout<<"("<<now->row<<", "<<now->col<<"): "<<now->value<<" ";
			}
			cout<<endl;
		}
		
	}

	//Get column list of a column
	LinkedList* getCol(int colIndex){
		return col[colIndex];
	}
	//Get row list of a row
	LinkedList* getRow(int rowIndex){
		return row[rowIndex];
	}
	
};

//Test program
int main(){

	SparseTable* st = new SparseTable();
	st->insert(1,1,2);
	st->insert(2,1,3);
	
	st->insert(3,2,4);
	st->insert(4,3,3);
	st->insert(44,3,5);
	st->insert(42,3,2);
	st->insert(42,3,2);
	st->printSelfByRow();
	st->printSelfByCol();
	
	
	st->remove(2,4);
	st->remove(2,4);
	st->printSelfByRow();
	st->printSelfByCol();

	st->remove(1,2);
	st->printSelfByRow();
	st->printSelfByCol();

	cout<<"Get value: (3,5)"<<st->getValue(3,5)<<endl;
	cout<<"Get value: (3,4)"<<st->getValue(3,4)<<endl;
	cout<<"Get value: (3,2)"<<st->getValue(3,2)<<endl;
} 



 

83 original articles published, 53 praised, 40000 visitors+
Private letter follow

Posted by jswash on Mon, 27 Jan 2020 05:55:39 -0800