5.3.4 矩阵及其十字链表结构

  • 产生背景
    矩阵的非零元个数和位置在操作过程中变化较大,不再适合采用顺序三元组表示。因此产生了一种用链式存储结构来表示三元组的线性表。
    便于元素的增删、移动。
    顺序表需要移动多个元素,链表只需要修改相关元素的指针。

  • 使用十字链表来表示矩阵
    1、矩阵中每个非零元可用一个含有5个域的结点来表示:i,j,e 表示该非零元所在的行数,列数,元素值;向右域 right 用以链接同一行中下一个非零元;向下域 down 用以链接同一列中下一个非零元。
    2、每个非零元同时是某个行列表或者列链表的结点,把一个矩阵转化成了一个十字交叉的链表。
    3、可用两个一维数组来表示,分别存储行链表的头指针和列链表的头指针。几行几个行链表,几列几个列链表,将所有行(列)链表的头节点各自存于一个一维数组中。
    读到

  • 稀疏矩阵的十字链表表示

//一个非零元的结构
typedef struct OLNode
{
	int i,j;//非零元所在行数,列数
	ElemType e;//非零元元素值
	struct OLNode *right, *down;//该非零元所在行表和列表的后继链域
}OLNode, *OLink;
//一个稀疏矩阵的十字链表结构
typedef struct
{
	OLink *rhead, *chead;//行列链表的头指针向量,头节点
	int mu, nu, tu;//稀疏矩阵的行数,列数,非零元个数
}CrossList;
  • 创建一个十字链表来表示稀疏矩阵
Status CreateCrossMatrix(CrossList &M)
{
	if(M)
	{
		free(M);
	}
	scanf(&M.mu, &M.nu, &M.tu);
	if(!(M.rhead = (OLink* )malloc((M.mu+1)*sizeof(OLink))))
	{
		exit(OVERFLOW);
	}
	if(!(M.chead = (OLink* )malloc((M.nu+1)*sizeof(OLink))))
	{
		exit(OVERFLOW);
	}
	M.rhead[] = NULL;
	M.chead[] = NULL;
	for(scanf(&i, &j, &e); i!=0; scanf(&i, &j, &e))
	{
		if(!(p = (OLink)malloc(sizeof(OLNode))))
		{
			exit(OVERFLOW);
		}
		p->i = i;
		p->j = j;
		p->e = e;

		if(M.rhead[i]==NULL || M.rhead[i]->j>j)
		{
			p->right = M.rhead[i];
			M.rhead[i] = P;
		}
		else
		{
			for(q=M.rhead[i]; 
			   (q->right)&&(q->right->j<j);
			    q=q->right );
			p->right = q->right;
			q->right = p;
		}
		if(M.chead[j]==NULL || M.chead[j]->i>i)
		{
			p->down = M.chead[j];
			M.chead[j] = p;
		}
		else
		{
			for(q = M.chead[j];
			    (q->down) && (q->down->i<i);
			     q = q->down);
			p->down = q->down;
			q->down = p;
		}
	}
	return OK;
}

此算法的时间复杂度为 O( t * max{ m, n } )
每建立一个非零元的结点时都要查询它在行表和列表的插入位置
其实一共就做两种事情,先处理行链表,再处理列链表,拆分开了就便于理解这些操作了。
窃以为十字链表牢记上面这句话就够了。

  • 十字矩阵实现两个稀疏矩阵相加
STATUS AddCrossList(CrossList &A, CrossList B)
{
	//初始化两个辅助量
	pre = NULL;
	for(int j=1; j<=A.nu; j++)
	{
		hl[j]=A.chead[j];
	}
	//主循环,行主序
	for(row=1; row<A.mu; row++)
	{
		pa = A.rhead[row];
		pb = B.rhead[row];
		
		while(pb)
		{
			if(pa == NULL || pa->j > pb->j)
			{
				// 只要pb非空,必然要将pb结点加到
   				// pa中,并将pb结点从它的行链表中
   				// 删除,只删行链表为了判断本行加
   				// 操作是否结束.
				p = pb;
				pb = pb->right;
				//这个if处理行链表的链接改动
				if(pre == NULL)
				{
					A.rhead[row] = p;
				}
				else
				{
					// pre哪来的?
					// 注意后面对pre的处理
					pre->right = p;
				}

				p->right = pa;
				pre = p;
				
				// 这个if处理列链表的改动
				if(!A.chead[p->j] || \
				    A.chead[p->j]->i > p->i)
				{
					p->down = A.chead[p->j];
					A.chead[p->j] = p;
				}
				else
				{
					while(hl[p->j]->down && \
						hl[p->j]->i < p->i)
					{
						hl[p->j] = hl[p->j]->down;
					}
					p->down = hl[p->j]->down;
					hl[p->j]->down = p;
				}
				hl[p->j] = p;
			}
			else if(pa != NULL && pa->j < pb->j)
			{
				pre = pa;
				pa = pa->right;
			}
			else if(pa->j == pb->j)
			{
				p = pb;
				pb = pb->right;
				pa->e += p->e;
				if(pa->e == 0)
				{
					//删去这个结点
					//这个if从行链表删除该节点	
					if(pre == NULL)
					{
						A.rhead[pa->i] = pa->right;
					}
					else
					{
						pre->right = pa->right;
					}
					//从列链表删除该节点
					while(hl[p->j]->down != pa)
					{
						hl[p->j] = hl[p->j]->down;
					}
					hl[p->j]->down = pa->down;
					free(pa);
				}
			}		
		}
	}
	return OK;
}
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值