看数据结构写代码(21) 稀疏矩阵(十字链表方式)

Posted clnchanpin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了看数据结构写代码(21) 稀疏矩阵(十字链表方式)相关的知识,希望对你有一定的参考价值。

写完 这个样例,花费了 我不少时间。大部分时间 花费在 调试 内存问题上。

比如在销毁十字链表时。多次释放节点空间,造成 _CrtIsValidHeapPointer(pUserData) 异常。

当使用malloc 分配 一个 空间时,会将这个空间的起始地址和长度 加到一个链表中去。free(p)的时候 ,会从 链表里 查找 是否 有 这个地址空间,找到了就将这个节点从链表中删除。_CrtIsValidHeapPointer(pUserData)  这个函数 正是 检查 这个空间是否 在链表里,若在,返回 true,否则返回 flase,。

多次 释放节点空间,必然 造成 _CrtIsValidHeapPointer(pUserData) 异常。

仅仅要释放的 不是 分配的起始 地址 都会 报 这个异常。

还有 一个 地方,就是 使用了 释放的 空间的数据。

c/c++ 内存问题。是个 头疼的问题。

以下 进入 正题:

稀疏矩阵 的 十字链表 方式,是 给 全部的行 和 列 都 当成 一个 链表 来处理。

第 i 行 j列的 非0 节点,既在 第 i行的链表上,又在 第 j列的 链表上,所以 叫 十字链表。

结构图例如以下:

技术分享

以下 上代码

欢迎指出代码不足

// CrossList.cpp : 定义控制台应用程序的入口点。
//稀疏矩阵的十字链表实现

#include "stdafx.h"
#include <stdlib.h>

typedef int ElementType;
enum E_State
{
	E_State_Error = 0,
	E_State_Ok = 1,
};

struct MatrixNode
{
	int row;
	int col;
	ElementType data;
	MatrixNode * rightNext;
	MatrixNode * downNext;
};

MatrixNode * makeNode(int row,int col,ElementType data){
	MatrixNode * newNode = (MatrixNode *) malloc(sizeof(MatrixNode));
	if (newNode != NULL)
	{
		newNode->row = row;
		newNode->col = col;
		newNode->data = data;
		newNode->rightNext = NULL;
		newNode->downNext = NULL;
	}
	return newNode;
}
//十字链表
struct CrossList
{
	MatrixNode ** rowHead;
	MatrixNode ** colHead;
	int rowNum;
	int colNum;
	int totalNum;
};

E_State listInit(CrossList * list,int row,int col){
	list->rowHead = (MatrixNode**)malloc(sizeof(MatrixNode*) * row);
	list->colHead = (MatrixNode**)malloc(sizeof(MatrixNode*) * col);
	if (list->rowHead && list->colHead)
	{
		list->rowNum = row;
		list->colNum = col;
		list->totalNum = 0;
		//建立头指针节点
		for (int i = 0; i < row; i++)
		{
			list->rowHead[i] = makeNode(-1,-1,-1);
			if (list->rowHead[i] == NULL)
			{
				return E_State_Error;
			}
		}
		for (int i = 0; i < col; i++)
		{
			list->colHead[i] = makeNode(-1,-1,-1);
			if (list->colHead[i] == NULL)
			{
				return E_State_Error;
			}
		}
		return E_State_Ok;
	}
	return E_State_Error;
}

//销毁十字链表
void listDestory(CrossList * list){
	//销毁链表节点
	for (int i = 0; i < list->rowNum; i++)
	{
		MatrixNode * head = list->rowHead[i];
		MatrixNode * next = head->rightNext;
		while (next != NULL)
		{
			MatrixNode * freeNode = next;
			next = next->rightNext;
			free(freeNode);
		}
		//别忘了销毁头节点
		free(head);
	}
	
	for (int i = 0; i < list->colNum; i++)
	{
		MatrixNode * head = list->colHead[i];
		free(head);
		/*销毁了两遍,呵呵
		MatrixNode * next = head->downNext;
		while (next != NULL)
		{
			MatrixNode * freeNode = next;
			next = next->downNext;
			free(freeNode);
		}*/
		
	}
	free(list->colHead);
	free(list->rowHead);
	list->colHead = NULL;
	list->rowHead = NULL;
	list->rowNum = 0;
	list->colNum = 0;
	list->totalNum = 0;
}

//在 row 行 col 列 插入 一个 data 元素(从 0行。0列 開始)
E_State listInsert(CrossList * list,ElementType data,int row,int col){
	if (row < 0 || row >= list->rowNum || col >= list->colNum || col < 0)
	{
		return E_State_Error;
	}
	MatrixNode * newNode = makeNode(row,col,data);
	if (newNode != NULL)
	{
		//增加行链表
		MatrixNode * pre = list->rowHead[row];
		MatrixNode * next = pre->rightNext;
		while (next != NULL)//寻找 第一个列值 小于 col 的节点
		{
			if (next->col > col)
			{
				break;
			}
			pre = next;
			next = next ->rightNext;
		}
		newNode->rightNext = pre->rightNext;
		pre->rightNext = newNode;
		//加到列链表
		pre = list->colHead[col];
		next = pre->downNext;
		while (next != NULL)//寻找 第一个列值 小于 col 的节点
		{
			if (next->row > row)
			{
				break;
			}
			pre = next;
			next = next ->downNext;
		}
		newNode->downNext = pre->downNext;
		pre->downNext = newNode;
		list->totalNum ++;
		return E_State_Ok;
	}
	return E_State_Error;
}

//row ,col 从 0行 0 列 開始
E_State listDelete(CrossList * list,int row,int col,ElementType * delData){
	if (row >= list->rowNum || row < 0 || col >= list->colNum || col <0)
	{
		return E_State_Error;
	}
	*delData = 0;
	//查找行链表
	MatrixNode * pre = list->rowHead[row];
	MatrixNode * next = pre->rightNext;
	while (next != NULL)
	{
		if (next->col == col)
		{
			pre->rightNext = next->rightNext;
			break;
		}
		else if(next->col > col)// 删除的节点 值域为0
		{
			return E_State_Ok;
		}
		pre = next;
		next = next->rightNext;
	}
	//查找列链表
	pre = list->colHead[col];
	next = pre->downNext;
	while (next != NULL)
	{
		if (next->row == row)
		{
			pre->downNext = next->downNext;
			*delData = next->data;
			list->totalNum --;
			//在查找完了 列链表之后 才干 释放 空间,要不 会内存 错误.
			free(next);
			break;
		}
		else if(next->row > row)// 删除的节点 值域为0
		{
			return E_State_Ok;
		}
		pre = next;
		next = next->downNext;
	}
	return E_State_Ok;
}

//list1 = list1 + list2
E_State listAdd(CrossList * list1,CrossList list2){
	if (list1->rowNum != list2.rowNum || list1->colNum != list2.colNum)
	{
		return E_State_Error;
	}
	for (int row = 0; row < list1->rowNum; row++)
	{
		MatrixNode * next1 = list1->rowHead[row]->rightNext;
		MatrixNode * next2 = list2.rowHead[row]->rightNext;
		while (next1 && next2)
		{
			int col1 = next1->col;
			int col2 = next2->col;
			if (col1 == col2)//元素 同行 同列
			{
				ElementType sum = next1->data + next2->data;
				if (sum == 0)//相加为0
				{
					ElementType del;
					next1 = next1->rightNext;//跟下一句颠倒过来。会有内存错误
					listDelete(list1,row,col1,&del);
				}
				else//相加不为0
				{
					next1->data = sum;
					next1 = next1->rightNext;
				}
				next2 = next2->rightNext;
			}
			else if(col1 < col2)//元素1 小于 元素2 的 列
			{
				next1 = next1->rightNext;
			}
			else//元素1 大于 元素2的列,插入元素2
			{
				listInsert(list1,next2->data,row,col2);
				next2 = next2->rightNext;
			}
		}
		//插入剩余的 next2 元素
		while (next2 != NULL )
		{
			listInsert(list1,next2->data,row,next2->col);
			next2 = next2->rightNext;
		}
	}
	return E_State_Ok;
}

// list1 = list1 - list2
E_State listSub(CrossList * list1,CrossList list2){
	if (list1->rowNum != list2.rowNum || list1->colNum != list2.colNum)
	{
		return E_State_Error;
	}
	for (int i = 0; i < list2.rowNum; i++)
	{
		MatrixNode * next = list2.rowHead[i]->rightNext;
		while (next != NULL)
		{
			next->data = -next->data;
			next = next->rightNext;
		}
	}
	return listAdd(list1,list2);
}

//list3 = list1 * list2
E_State listMult(CrossList list1, CrossList list2,CrossList * list3){
	if (list1.colNum != list2.rowNum)
	{
		return E_State_Error;
	}
	listInit(list3,list1.rowNum,list2.colNum);
	for (int row = 0; row < list1.rowNum; row++)
	{
		for (int col = 0; col < list2.colNum; col++)
		{
			MatrixNode * nextCol2 = list2.colHead[col]->downNext;
			ElementType mul = 0;
			while (nextCol2)
			{
				MatrixNode * nextRow1 = list1.rowHead[row]->rightNext;
				while (nextRow1)
				{
					if (nextRow1->col == nextCol2->row)
					{
						mul += nextRow1 -> data * nextCol2 ->data;
					}
					nextRow1 = nextRow1->rightNext;
				}
				nextCol2 = nextCol2->downNext;
			}
			if (mul != 0)
			{
				listInsert(list3,mul,row,col);
			}
		}
	}
	return E_State_Ok;
}

void listTraverse(CrossList list){
	printf("--------------------遍历開始-----------------\n");
	for (int i = 0; i < list.rowNum; i++)
	{
		MatrixNode * next = list.rowHead[i]->rightNext;
		while (next != NULL)
		{
			printf("%d行 %d列 : %d\n",next->row+1,next->col+1,next->data);
			next = next->rightNext;
		}
	}
	printf("--------------------遍历结束------------------\n");
}

int initData[5][10] = {
	{1,0,0,0,0,0,0,0,0,0},
	{0,0,2,0,0,0,0,0,5,0},
	{0,0,0,3,0,0,0,0,0,0},
	{0,2,0,0,0,0,0,0,0,0},
	{1,0,0,0,0,0,0,0,0,9},
};

int initAddData[5][10]= {
	{1,0,0,0,3,0,0,0,0,0},
	{0,0,2,0,4,0,0,0,5,0},
	{0,0,0,3,0,0,0,0,0,0},
	{0,2,0,0,2,0,0,0,0,0},
	{1,0,0,0,1,0,0,0,0,9},
};

int initData2 [10][2] = {
	{1,0},
	{0,0},
	{0,0},
	{0,0},
	{0,0},
	{0,6},
	{0,0},
	{0,0},
	{5,0},
	{0,0},
};

int _tmain(int argc, _TCHAR* argv[])
{
	//初始化数据
	printf("--------------------矩阵1------------\n");
	CrossList list1;
	listInit(&list1,5,10);
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			int data = initData[i][j];
			if (data != 0)
			{
				listInsert(&list1,data,i,j);
			}
		}
	}
	listTraverse(list1);
	printf("--------------------矩阵2------------\n");
	CrossList list2;
	listInit(&list2,5,10);
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			int data = initAddData[i][j];
			if (data != 0)
			{
				listInsert(&list2,data,i,j);
			}
		}
	}
	listTraverse(list2);
	printf("--------------------矩阵1 = 矩阵1 + 矩阵2------------\n");
	listAdd(&list1,list2);
	listTraverse(list1);
	printf("--------------------矩阵1 = 矩阵1 - 矩阵2------------\n");
	listSub(&list1,list2);
	listTraverse(list1);
	printf("--------------------矩阵3------------\n");
	CrossList list3;
	listInit(&list3,10,2);
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 2; j++)
		{
			int data = initData2[i][j];
			if (data != 0)
			{
				listInsert(&list3,data,i,j);
			}
		}
	}
	listTraverse(list3);
	printf("--------------------矩阵4 = 矩阵1 * 矩阵3------------\n");
	CrossList list4;
	listMult(list1,list3,&list4);
	listTraverse(list4);
	//释放内存空间
	listDestory(&list1);
	listDestory(&list2);
	listDestory(&list3);
	listDestory(&list4);
	return 0;
}
执行截图:

技术分享

技术分享









以上是关于看数据结构写代码(21) 稀疏矩阵(十字链表方式)的主要内容,如果未能解决你的问题,请参考以下文章

5-4-十字链表(稀疏矩阵)-数组和广义表-第5章-《数据结构》课本源码-严蔚敏吴伟民版

稀疏矩阵的加法(用十字链表实现A=A+B)

稀疏矩阵一般的压缩存储方法有两种

稀疏矩阵定义以及存储格式(COO,CSR,CSC)

图的存储代码实现

关于稀疏矩阵三元组的转置