严蔚敏-数组和广义表-数组的顺序实现-矩阵的压缩存储-广义表

数据结构学习笔记【第5章 数组和广义表】

5.1 数组的定义

  • n维数组
  • n=1时,n维数组就退化为定长的线性表;反之,n维数组也可以看成是线性表的推广
  • 数组一旦被定义,它的维数和维界就不再改变
  • 除了结构的初始化和销毁之外,数组只有存取元素和修改元素值的操作

5.2 数组的顺序表示和实现

#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
//标准头文件,提供宏va_start、va_arg和va_end

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2

typedef int Status;
typedef int ElemType;

#define MAX_ARRAY_DIM 8

typedef struct {
	ElemType* base;//数组元素基址
	int dim;//数组维数
	int* bounds;//数组维界基址
	int* constants;//数组映像函数常量基址
}Array;

Status InitArray(Array& A, int dim, ...);
//若维数dim和随后的各维长度合法,则构造相应的数组A,并返回OK
Status DestroyArray(Array& A);
//销毁数组A
Status Locate(Array A, va_list ap, int& off);
//若ap指示的各下标值合法,则求出该元素在A中相对地址off
Status Value(Array A, ElemType* e, ...);
//A是n维数组,e是元素变量,随后是n个下标值
//若下标不超界,则e赋值为所指定的A的元素值,并返回OK
Status Assign(Array& A, ElemType e, ...);
//A是n维数组,e是元素变量,随后是n个下标值
//若下标不超界,则将e的值赋给所指定的A的元素,并返回OK
void print_Array(Array A);

int main()
{
	Array A;
	int dim = 2;
	InitArray(A, dim, 2, 3);
	Assign(A, 1, 0, 0);
	Assign(A, 2, 0, 1);
	Assign(A, 3, 0, 2);
	Assign(A, 4, 1, 0);
	Assign(A, 5, 1, 1);
	Assign(A, 6, 1, 2);
	print_Array(A);
	ElemType e;
	Value(A, &e, 0, 0);
	printf("e:%d", e);
	DestroyArray(A);
	system("pause");
	return 0;
}
Status InitArray(Array& A, int dim, ...)
{
	if (dim<1 || dim>MAX_ARRAY_DIM)return ERROR;
	A.dim = dim;
	A.bounds = (int*)malloc(dim * sizeof(int));
	if (!A.bounds)exit(OVERFLOW);
	int elemtotal = 1;
	va_list ap;
	va_start(ap, dim);
	int i;
	for (i = 0; i < dim; i++) {
		A.bounds[i] = va_arg(ap, int);
		if (A.bounds[i] < 0)return ERROR;
		elemtotal *= A.bounds[i];
	}
	va_end(ap);
	A.base = (ElemType*)malloc(elemtotal * sizeof(ElemType));
	if (!A.base)exit(OVERFLOW);
	A.constants = (int*)malloc(dim * sizeof(int));
	if (!A.constants)exit(OVERFLOW);
	A.constants[dim - 1] = 1;
	for (i = dim - 2; i >= 0; i--) {
		A.constants[i] = A.bounds[i + 1] * A.constants[i + 1];
	}
	return OK;
}
Status DestroyArray(Array& A)
{
	if (!A.base)return ERROR;
	free(A.base); A.base = NULL;
	if (!A.bounds)return ERROR;
	free(A.bounds); A.bounds = NULL;
	if (!A.constants)return ERROR;
	free(A.constants); A.constants = NULL;
	return OK;
}
Status Locate(Array A, va_list ap, int& off)
{
	off = 0;
	int i;
	int ind;
	for (i = 0; i < A.dim; i++)
	{
		ind = va_arg(ap, int);
		if (ind < 0 || ind >= A.bounds[i])return OVERFLOW;
		off += A.constants[i] * ind;
	}
	return OK;
}
Status Value(Array A, ElemType* e,...)
{
	va_list ap;
	int result;
	int off;
	va_start(ap, e);
	if ((result = Locate(A, ap, off)) <= 0)return result;
	*e = *(A.base + off);
	va_end(ap);
	return OK;
}
Status Assign(Array& A, ElemType e, ...)
{
	va_list ap;
	int result;
	int off;
	va_start(ap, e);
	if ((result = Locate(A, ap, off)) <= 0)return result;
	*(A.base + off) = e;
	va_end(ap);
	return OK;
}
void print_Array(Array A)
{
	printf("Array:\n");
	if (!A.base) {
		printf("NULL\n");
	}
	int i, elemtotal=1;
	for (i = 0; i < A.dim; i++)
	{
		elemtotal *= A.bounds[i];
	}
	for (i = 0; i < elemtotal; i++)
	{
		printf("%d ", A.base[i]);
		if ((i + 1) % A.constants[0] == 0) 
		{ 
			printf("\n"); 
		}
	}
}

5.3 矩阵的压缩存储

  • 压缩存储:为多个值相同的元只分配一个存储空间,对零元不分配空间
  • 特殊矩阵:值相同的元素或零元素在矩阵中的分布有一定规律,反之称为稀疏矩阵

5.3.1 特殊矩阵

  • n阶对称矩阵
    为每一对对称元分配一个存储空间,则可将 n 2 n^2 个元压缩存储到 n ( n + 1 ) 2 \large\frac{n(n+1)}{2} 个元的空间中
  • 对角矩阵
    除了主对角线上和直接在对角线上、下方若干条对角线上的元之外,所有的其他的元均为零

5.3.2 稀疏矩阵

  • 稀疏因子
    假设在 m × n m×n 的矩阵中,有 t t 个元素不为零,令 δ = t m × n \delta=\Large\frac{t}{m×n} ,称 δ \delta 为矩阵的稀疏因子
  • 存储方式:除了存储非零元的值之外,还必须同时记下它所在行和列的位置 ( i , j ) (i,j) 。反之,一个三元组 ( i , j , a ) (i,j,a) 唯一确定了矩阵 A A 的一个非零元
  • 算法5.1 采用三元组表存储表示,求稀疏矩阵M的转置矩阵T
    时间复杂度 O ( n u t u ) O(nu\cdot tu) ,一般矩阵转置时间复杂度为 O ( m u n u ) O(mu\cdot nu) ,当 t u = m u × n u tu=mu\times nu 时,算法5.1的时间复杂度就为 O ( m u n u 2 ) O(mu\cdot nu^2) 了,因此该算法仅使用于 t u m u × n u tu\ll mu\times nu 的情况
Status TransposeSMatrix(TSMatrix M, TSMatrix& T)
{
	T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
	if (T.nu) 
	{
		int q = 1;
		int col;
		for (col = 1; col <= M.nu; col++)
		{
			int p;
			for (p = 1; p <= M.tu; p++) 
			{
				if (M.data[p].j == col)
				{
					T.data[q].i = M.data[p].j;
					T.data[q].j = M.data[p].i;
					T.data[q].e = M.data[p].e;
					q++;
				}
			}
		}
	}
	/*int ind;
	for (ind = 1; ind <= M.tu; ind++)
	{
		T.data[ind].i = M.data[ind].j;
		T.data[ind].j = M.data[ind].i;
		T.data[ind].e = M.data[ind].e;
	}*/
	return OK;
}
  • 算法5.2 采用三元组顺序表存储表示,求稀疏矩阵M的转置矩阵 T T
  • 按照 a . d a t a a.data 中三元组的次序进行转置,并将转置后的三元组置入 b b 中恰当。的位置预先确定矩阵 M M 中每一列(即 T T 中每一行)的第一个非零元在 b . d a t a b.data 中应有的位置
  • 附设 n u m num c p o t cpot 两个向量。 n u m [ c o l ] num[col] 表示矩阵 M M 中第 c o l col 列中非零元个数, c p o t [ c o l ] cpot[col] 指示 M M 中第 c o l col 列的第一个非零元在 b . d a t a b.data 中的恰当位置
  • 时间复杂度 O ( n u + t u ) O(nu+tu)
Status FastTransposeSMatrix(TSMatrix M, TSMatrix& T)
{
	T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
	int col;
	int num[MAXCOL + 1];
	int cpot[MAXCOL + 1];
	if (T.tu)
	{
		for (col = 1; col <= M.nu; col++)
		{
			num[col] = 0;
		}
		int t;
		for (t = 1; t <= M.nu; t++) {
			num[M.data[t].j]++;
		}
		cpot[1] = 1;
		for (col = 2; col <= M.nu; col++)
		{
			cpot[col] = cpot[col - 1] + num[col - 1];
		}
		int p, q;
		for (p = 1; p < M.tu; p++)
		{
			col = M.data[p].j;
			q = cpot[col];
			T.data[q].i = M.data[p].j; T.data[q].j = M.data[p].i;
			T.data[q].e = M.data[p].e;
			cpot[col]++;
		}
	}
	return OK;
}
  • 算法5.3算法5.4
    未完待续…
  • 完整代码
#include<stdio.h>
#include<stdlib.h>
#include<stdarg.h>
//标准头文件,提供宏va_start、va_arg和va_end

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2

typedef int Status;
typedef int ElemType;

#define MAXSIZE 12500  //非零元最大个数
#define MAXCOL 100

typedef struct {
	int i, j;
	ElemType e;  //非零元下标
}Triple;

typedef struct {
	Triple data[MAXSIZE + 1];
	int mu, nu, tu;  //矩阵的行数、列数和非零元个数
}TSMatrix;

Status TransposeSMatrix(TSMatrix M, TSMatrix& T);
//算法5.1 采用三元组表存储表示,求稀疏矩阵M的转置矩阵T
Status FastTransposeSMatrix(TSMatrix M, TSMatrix& T);
//算法5.2 采用三元组顺序表存储表示,求稀疏矩阵M的转置矩阵T

int main()
{
	system("pause");
	return 0;
}
Status TransposeSMatrix(TSMatrix M, TSMatrix& T)
{
	T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
	if (T.nu) 
	{
		int q = 1;
		int col;
		for (col = 1; col <= M.nu; col++)
		{
			int p;
			for (p = 1; p <= M.tu; p++) 
			{
				if (M.data[p].j == col)
				{
					T.data[q].i = M.data[p].j;
					T.data[q].j = M.data[p].i;
					T.data[q].e = M.data[p].e;
					q++;
				}
			}
		}
	}
	/*int ind;
	for (ind = 1; ind <= M.tu; ind++)
	{
		T.data[ind].i = M.data[ind].j;
		T.data[ind].j = M.data[ind].i;
		T.data[ind].e = M.data[ind].e;
	}*/
	return OK;
}
Status FastTransposeSMatrix(TSMatrix M, TSMatrix& T)
{
	T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
	int col;
	int num[MAXCOL + 1];
	int cpot[MAXCOL + 1];
	if (T.tu)
	{
		for (col = 1; col <= M.nu; col++)
		{
			num[col] = 0;
		}
		int t;
		for (t = 1; t <= M.nu; t++) {
			num[M.data[t].j]++;
		}
		cpot[1] = 1;
		for (col = 2; col <= M.nu; col++)
		{
			cpot[col] = cpot[col - 1] + num[col - 1];
		}
		int p, q;
		for (p = 1; p < M.tu; p++)
		{
			col = M.data[p].j;
			q = cpot[col];
			T.data[q].i = M.data[p].j; T.data[q].j = M.data[p].i;
			T.data[q].e = M.data[p].e;
			cpot[col]++;
		}
	}
	return OK;
}

5.4 广义表的定义

  • 广义表是线性表的推广,也称列表
  • 广义表中可包含单个元素(原子)也可包含广义表(子表
  • 3个结论
    列表的元素可以是子表
    列表可为其他列表所共享
    列表可以是一个递归的表,即列表也可以是其本身的一个子表

5.5 广义表的存储结构

  • 由于广义表中数据元素可以具有不同的结构,因此难以用顺序存储结构表示,通常采用链式存储结构
  • 一个表节点由3个域组成:标志域、指示表头的指针域和指示表尾的指针域
  • 原子节点由2个域组成:标志域和值域
  • 广义表的头尾链表存储表示
    广义表的头尾链表存储表示
typedef enum { ATOM, LIST }ElemTag;  //枚举类型
typedef struct GLNode {
	ElemTag tag;  //Tag=0为原子,Tag=1为子表
	union {  //共用体,共享同一段内存
		AtomType atom;  //原子节点值域
		struct { struct GLNode* hp, * tp; }ptr;  //hp表头,tp表尾
	};
}*GList;
  • 广义表的扩展线性链表存储表示
    广义表的扩展线性链表存储表示
typedef enum { ATOM, LIST }ElemTag;
typedef struct GLNode {
	ElemTag tag;
	union {
		AtomType atom;
		struct GLNode* hp;
	};
	struct  GLNode* tp;
}*GList;

5.6 m元多项式的表示

  • 未完待续…

5.7 广义表的递归算法

  • 未完待续…
发布了40 篇原创文章 · 获赞 12 · 访问量 5734

猜你喜欢

转载自blog.csdn.net/weixin_43488958/article/details/104149048