稀疏矩阵、广义表、串------2022年1月4日--最后一节课

本文介绍了如何优化稀疏矩阵的存储和转置操作,使用三元组结构体和动态数组实现高效存储。同时,探讨了广义表的链式存储结构,包括两种实现方式,并展示了广义表的创建过程。这些数据结构在处理大量非零元素和复杂数据组织时具有重要意义。
摘要由CSDN通过智能技术生成

开始之前需要知道一下,矩阵的元素的地址位置怎么找。

归纳一下

加入已知首地址是 A
求 A[i]的位置 : A+ i*l ;

LOC(i)=LOC(i-1)+ l;

以三维数组为例:

在这里插入图片描述

三角矩阵的存储

用一维数组存储三角矩阵的上半部分(或者下半部分)。

这里的计算公式需要掌握

在这里插入图片描述

对称矩阵的存储

稀疏矩阵

顾名思义: 就是很少元素的矩阵。

面对这样的矩阵,我们需要用另一种方式存储,不需要直接用二维数组存储

三元表

用三元表存储就是: 记录行号、列号、 以及总共的元素个数。

比如以下矩阵:

在这里插入图片描述

用新的方式存储

在这里插入图片描述
采用一个结构体包括三个元素,分别存行号、列号、 以及总共的元素个数。

struct RCV { int row,col; 
 float value; 
 }; 
class SMatrix { 
     RCV *item; 
    int r,c,num; 
 public: 
    SMatrix(){ item=NULL; r=0; c=0;num=0; } 
    SMatrix( RCV a[], int n, int row,int col); //a[]是一个三元组 
    SMatrix& tran(); // 普通转置算法 
    SMatrix& tran1(); // 改进后的转置算法 
    SMatrix& plus(SMatrix& b); 
    SMatrix& mult(SMatrix& b); 
    void prnt(); 
 };

矩阵的转置操作

如果直接简单粗暴的改变行号和列号


item[i][j]=item[j][i]

会导致后面查找效率太低。

所以我们需要优化这个矩阵的存储情况。

在这里插入图片描述

优化矩阵的转置

在这里插入图片描述

这里用一个标注相同行的首元素的起点方法。

1、相同的行,计数,用rnum[i]记下第I行的元素个数。
2.根据第一步的统计,计算每一个行的起始元素的下标是多少。

rstart[i]=rstart[i-1]+rnum[i-1];

3、 在进行转置的时候,
需要对这个首地址进行自增的操作,因为每一次输入一个数字,该位置已经满了,所以需要往后找一个位置放入元素。

简单转置算法过慢

SMatrix& SMatrix::tran() {
	SMatrix& x = *new SMatrix; 
	int i, j, k;
	x.r = c; x.c = r; x.num = num;
	x.item = new RCV[num];
	if (num > 0)
	{
		k = 0;
		for (i = 0; i < c; i++)
			for (j = 0; j < num; j++)
				if (item[j].col == i)
				{
					x.item[k].row = item[j].col;
					x.item[k].col = item[j].row;
					x.item[k].value = item[j].value;
					k++;
				}
	}
	return(x);
}

转置优化代码

template<typename E>
struct Triple		//三元组
{
    int row, col;	//非零元素行号/列号
    E value;		//非零元素的值
    void operator = (Triple<E> &R){
		row = R.row;
		col = R.col;
		value = R.value;
	}
	Triple(int r=-1, int c=-1, E v=0){
		row = r;
		col = c;
		value = v;
	}
};		

template <typename E>
class SparseMatrix{//稀疏矩阵定义与教材略有不同
public: 
    SparseMatrix( int mT = dterms, int Rw = drows, int Cl = dcols);
	SparseMatrix(SparseMatrix<E>& x);//有动态分配,必须定义复制构造函数和析构函数
	~SparseMatrix(){
		delete []smArray;
	}
    SparseMatrix<E> Transpose();
    SparseMatrix<E> FastTranspos();
    SparseMatrix<E> Add(SparseMatrix<E> &b);
    SparseMatrix<E> Multiply(SparseMatrix<E> &b);
	int getRows(){
		return Rows;
	}
	int getCols(){
		return Cols;
	}
	friend ostream& operator << (ostream &out, SparseMatrix<E> &M){
		out << "rows = " << M.Rows << endl;
		out << "cols = " << M.Cols << endl;
		out << "Nonzero terms = " << M.Terms << endl;
		for (int i = 0; i < M.Terms; i++)		{
			out << "M[" << M.smArray[i].row << "][" << M.smArray[i].col
				<< "]=" << M.smArray[i].value << endl;
		}
		return out;
	}
	friend istream& operator >> (istream &in, SparseMatrix<E> &M){
		cout<<"pleae input the number of nonzero nodes :";
		cin>>M.Terms;
		cout<<endl;
		for (int i = 0; i < M.Terms; i++){
			cout << "Enter row, column, and value of term:" << i+1 << endl;
			in >> M.smArray[i].row >> M.smArray[i].col>> M.smArray[i].value;
		}
		return in;
	}
	SparseMatrix<E> & operator = (SparseMatrix<E> &x){ //改
		Rows = x.Rows;
		Cols = x.Cols;
		Terms=x.Terms;
		maxTerms =x.maxTerms;
		delete []smArray;                  //先清后建再复制
		smArray = new Triple<E>[maxTerms];
		assert(smArray);
		for(int i = 0; i < x.Terms; i++){
			smArray[i] = x.smArray[i];
		}
		return /*des*/*this;
	}
private:
    int Rows, Cols, Terms;
    Triple<E> *smArray;
	int maxTerms;
};

template <typename E>
SparseMatrix<E> SparseMatrix<E>::FastTranspos(){//快速转置
    int *rowSize = new int[Cols];       //行元素数量数组
    int *rowStart = new int[Cols];      //行起始位置数组
	SparseMatrix<E> /*B(Rows,Cols,Terms)*/B(maxTerms,Cols,Rows);
	B.Terms=Terms;
    if (Terms > 0)	{
        int i, j;
        for (i = 0; i < Cols; i++){
			rowSize[i] = 0;
		}
	    for (i = 0; i < Terms; i++){             //1.建立行元素数量数组
			rowSize[smArray[i].col]++; 
		}
        rowStart[0] = 0;	
        for (i = 1; i < Cols; i++){              //2.导出行起始位置数组
			rowStart[i] = rowStart[i-1]+rowSize[i-1];
		}
	    for (i = 0; i < Terms; i++)		{			
			j = rowStart[smArray[i].col];
			B.smArray[j].row = smArray[i].col;
			B.smArray[j].col = smArray[i].row;
			B.smArray[j].value = smArray[i].value;
			rowStart[smArray[i].col]++;//放置一个元素后,对应行起始位置项++,指出该行下一个元素位置		
		}
	}
	B.Terms=Terms;
    delete []rowSize;
	delete []rowStart;
	return B;
}

广义表 (注意区分二叉树表示的广义表)

广义表: 是多个元素的有限序列,一般记作LS=(d1,d2,…,dn),其中di可以是原子(不可
再分的元素),也可以是广义表。

• 长度
• 表头 第一个元素
• 表尾 除了第一个的剩下的全是表尾,(不是最后一个元素)
• 深度
• 空表
例1.广义表A=(a,(),(b,(a,b))),
• 深度为3,长度为3;表头是原子a,深度为0;
表尾是((),(b,(a,b))),深度为3,
长度为2。

• 例2.广义表B=((a),b,c)
• 深度为2,长度为3;表头是(a),深度是1,
长度是1;表尾是(b,c),深度是1,长度是2

2 • 例3.空表的表头、表尾都是空表。

广义表存储结构

在这里插入图片描述

链式存储

方法一:

在这里插入图片描述 结点中0 表示是原子元素,1表示是表元素(就是表中有表的情况)


class GenList;

class GenListNode { //结点结构 
friend class GenList; 
private: 
bool tag;//FALSE表示原子元素,TRUE表示表元素
    union
   {
       char data; 
	     GenListNode* head;
    };
   GenListNode* tail; 
}; 

class GenList { 
//广义表类结构 
public:
	//各种广义表的操作
private: GenListNode *first; 
};

方法二:

在这里插入图片描述这里需要对Union进行了解

union介绍

共用体,也叫联合体,在一个“联合”内可以定义多种不同的数据类型。
   一个被说明为该“联合”类型的变量中,允许装入该“联合”所定义的任何一种数据,这些数据共享同一段内存,以达到节省空间的目的。
   union变量所占用的内存长度等于最长的成员的内存长度。

class GenList;
class GenListNode
{ //结点结构
friend class GenList;
private:
 Boolean tag;//FALSE表示原子元素,TRUE表示表元素 
    union {
       char data;
       struct {
       GenListNode *head;
       GenListNode *tail;
        };
     };
};
class GenList
{
//广义表类结构 
public: 
//各种广义表的操作 
private: GenListNode *first; 
};
// 从广义表的字符串描述s出发, 建立一个带头结点的广义表,要求T为char型。
// 在表L1存储大写字母的表名, 在表L2存储表名对应子表结点的地址。
template <typename T>
void GenList<T>::CreateList(istream& in, GenListNode<T> *& ls, 
			SeqList<T> &L1, SeqList <GenListNode<T> *> &L2){
	T chr; 
	in >> chr;
	cout<<chr; 
	//读入一个字符,只可能读入#、左括号和字母
	if (isalpha(chr) && isupper(chr) || chr == '('){ //大写字母或'('	
		bool b=true;
		ls = new GenListNode<T>;          //建子表结点
		cout<<'&';
		ls->utype = 2;		
		if (isalpha(chr) && isupper(chr)){ //表名处理,有名子表		
			int n = L1.Length();
			int m = L1.Search(chr);
			if (m != 0){                  //该表已建立
				b=false;                  //共享
				ls->info.hlink = *L2.getData(m);//查子表地址
				ls->info.hlink->info.ref++;		//引用计数加1
				cout<<'@';
				in >> chr;
				if (chr != '('&&chr!=','&&chr != ')') exit(1);	//表名后必跟'('或','或')'
				if(chr=='(')
					do{
						in>>chr; //共享表可以只输入表名,其它内容程序忽略
					}while(chr!=')');
				else if(chr==','||chr == ')') in.putback(chr);//逗号或')'送回缓冲区
			} 	
			else{       //该表未建立
				ls->info.hlink = new GenListNode<T>;         //建附加头结点
				L1.Insert(n, chr);                           //保存表名及地址
				L2.Insert(n, ls->info.hlink);
				in >> chr;
				cout<<chr; 
				cout<<'%';  
				ls->info.hlink->utype = 0;
				ls->info.hlink->info.ref = 1;
				if (chr != '(') exit(1);	//表名后必跟'('
				CreateList(in, ls->info.hlink->tlink, L1, L2);//递归建子表
			}
		}
		else{//无名子表
			ls->info.hlink = new GenListNode<T>;
			cout<<'%';  
			ls->info.hlink->utype = 0;         //建头结点
			ls->info.hlink->info.ref = 1;
			CreateList(in, ls->info.hlink->tlink, L1, L2);//递归建子表
		}
		CreateList(in, ls, L1, L2);                   //递归建后继表
	}
	else if (isalpha(chr) && islower(chr)){	//建原子结点。本程序现在仅在T为char时有效
			ls = new GenListNode<T>;
			cout<<'*'; 
			ls->utype = 1;
			ls->info.value = chr;//这里应该有将字符与T类型具体数据一一对应的表,最终把具体数据存入info.value
			CreateList(in, ls, L1, L2);
		}
		else if (chr == ','){ 		//建后继结点	
				if (ls->tlink) delete ls->tlink;
				ls->tlink = new GenListNode<T>;
				CreateList(in, ls->tlink, L1, L2); 
			}
			else if (chr == ')')	ls->tlink = NULL;  //链收尾
				else if (chr == '#')	ls = NULL; //空表, 链收尾
}

主要的基本操作

1.求子串 substring(s,pos,len) 返回值为串s中第pos个字
符起,长度为len的字符序列
2.插入 insert(s,pos,t) 在串s的第pos个字符之后插入串t
3.删除 delete(s,pos,len) 从串s中删去第pos个字符起长
度为len的子串
4.定位 position(s,t) 若t在s中存在,则返回t在主串s中
的位置,否则函数值为0
4.替换 replace(s,t,v) 操作结果是以串v替换所有在串s
中出现的和非空串t相等的子串
6.判相等 equal(s,t) 若s和t相等,则返回true否则返回
false。
7.求长度 length(s) 返回s中字符的个数

顺序存储

顺序存储结构类型定义
const maxlen = 允许的串最大长度;
struct Tstr { int curlen;
char str [maxlen]; };

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值