数据结构系列内容的学习目录 → \rightarrow 浙大版数据结构学习系列内容汇总

1. 线性表

1.1 引入:多项式的表示

  例:一元多项式及其运算
    一元多项式: f ( x ) = a 0 + a 1 x + . . . + a n − 1 x n − 1 + a n x n f(x) = a_{0} + a_{1}x+...+a_{n-1}x^{n-1}+a_{n}x^{n} f(x)=a0+a1x+...+an1xn1+anxn
    主要运算: 多项式相加、相减、相乘等。
    分析: 如何表示多项式?
        ⋄ \diamond 多项式的关键数据:多项式项数n
        ⋄ \diamond 各项系数 a i a_{i} ai及指数 i i i

  • 方法1: 顺序存储结构直接表示
          数组各分量对应多项式各项,a[i]为项 x i x^{i} xi的系数 a i a_{i} ai。例如, f ( x ) = 4 x 5 − 3 x 2 + 1 f(x) = 4x^{5}-3x^{2}+1 f(x)=4x53x2+1表示成
     
    数据结构(二)—— 线性结构(1):线性表_数据结构 
          两个多项式相加:两个数组对应分量相加。
          问题: 如何表示多项式 x + 3 x 2000 x+3x^{2000} x+3x2000

  • 方法2: 顺序存储结构表示非零项
          每个非零项 a i x i a_{i}x^{i} aixi涉及两个信息:系数 a i a_{i} ai和指数 i i i,可以将一个多项式看成是一个 ( a i , i ) (a_{i}, i) (ai,i)二元组的集合。
          用结构数组表示:数组分量是由系数 a i a_{i} ai、指数 i i i组成的结构,对应一个非零项。
          例如, P 1 ( x ) = 9 x 12 + 15 x 8 + 3 x 2 P_{1}(x) = 9x^{12}+15x^{8}+3x^{2} P1(x)=9x12+15x8+3x2 P 2 ( x ) = 26 x 19 − 4 x 8 − 13 x 6 + 82 P_{2}(x) = 26x^{19}-4x^{8}-13x^{6}+82 P2(x)=26x194x813x6+82表示成
     
    数据结构(二)—— 线性结构(1):线性表_数据结构_02 
          按指数大小有序存储!
          相加过程:从头开始,比较两个多项式当前对应项的指数,若相同,将系数相加。即 P 3 ( x ) = P 1 ( x ) + P 2 ( x ) = 26 x 19 + 9 x 12 + 11 x 8 − 13 x 6 + 3 x 2 + 82 P_{3}(x) = P_{1}(x)+P_{2}(x)=26x^{19}+9x^{12}+11x^{8}-13x^{6}+3x^{2}+82 P3(x)=P1(x)+P2(x)=26x19+9x12+11x813x6+3x2+82

  • 方法3: 链表结构存储非零项
          链表中每个结点存储多项式中的一个非零项,包括系数和指数两个数据域以及一个指针域。
     数据结构(二)—— 线性结构(1):线性表_数据结构_03 
          例如 P 1 ( x ) = 9 x 12 + 15 x 8 + 3 x 2 P_{1}(x) = 9x^{12}+15x^{8}+3x^{2} P1(x)=9x12+15x8+3x2 P 2 ( x ) = 26 x 19 − 4 x 8 − 13 x 6 + 82 P_{2}(x) = 26x^{19}-4x^{8}-13x^{6}+82 P2(x)=26x194x813x6+82,链表存储形式如下所示。
     
    数据结构(二)—— 线性结构(1):线性表_数据结构_04 
          链表结构定义如下所示。

typedef struct PolyNode *Polynomial;
struct PolyNode {
    int coef;
    int expon;
    Polynomial link;
}

  ​多项式表示问题的启示:1.同一个问题可以有不同的表示(存储)方法;
  ​  ​  ​  ​  ​ ​  ​2.有一类共性问题:有序线性序列的组织和管理。

1.2 什么是线性表

  线性表是最基本、最简单、也是最常用的一种数据结构。线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
  线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的(注意,这句话只适用大部分线性表,而不是全部。比如,循环链表逻辑层次上也是一种线性表,存储层次上属于链式存储,但是把最后一个数据元素的尾指针指向了首位结点)。

  ​ “线性表(Linear List)”: 由同类型数据元素构成有序序列的线性结构。
     ⋄ \diamond 表中元素个数称为线性表的长度
     ⋄ \diamond 线性表没有元素时,称为空表
     ⋄ \diamond 表起始位置称为表头,表结束位置称表尾

1.3 线性表的抽象数据类型描述

  类型名称: 线性表(List)
  数据对象集: 线性表是 n (≥0) 个元素构成的有序序列 { a 1 , a 2 , … , a n } \{a_1,a_2,…,a_n\} {a1,a2,,an}
  操作集: 线性表L ∈ ∈ List,整数 i 表示位置,元素 X ∈ ∈ ElementType
       线性表基本操作主要有:

  1. List MakeEmpty(): 初始化一个空线性表L;
  2. ElementType FindKth(int K, List L):根据位序K,返回相应元素;
  3. int Find(ElementType X, List L):在线性表L中查找X的第一次出现位置;
  4. void Insert(ElementType X, int i, List L):在位序i前插入一个新元素X;
  5. void Delete(int i, List L):删除指定位序i的元素;
  6. int Length(List L):返回线性表 的长度n。

1.4 线性表的顺序存储实现

  利用数组的连续存储空间存放线性表的各元素。线性表的顺序存储中的下标从 0 开始。

数据结构(二)—— 线性结构(1):线性表_数据结构_05
   线性表的顺序存储数据结构定义如下所示。

typedef struct LNode *List;
struct LNode{
    ElementType Data[MAXSIZE];
    int Last;
};
struct LNode L;
List PtrL;

   访问下标为 i 的元素:L.Data[i]PtrL->Data[i]
   线性表的长度:L.Last+1PtrL->Last+1

   主要操作的实现:

   1. 初始化(建立空的顺序表)

List MakeEmpty( )
{
    List PtrL;
    PtrL= (List )malloc( sizeof(struct LNode)) ;
    PtrL->Last = -1;
    return PtrL;
}

   2. 查找

   (1)按值查找:Find

int Find(ElementType X,List PtrL)
{
    int i = 0;
    while( i <= PtrL->Last && PtrL->Data[i]!= X )
    {
        i++;
    }
    if (i > PtrL->Last)
        return -1;  //如果没找到,返回-1
    else  
        return i;  //找到后返回的是存储位置·
}

   (2)按序号查找:FindKth

ElementType FindKth(int K, List L) 
{
	if (K < 0 || L->Last < K)  //位置越界
	{  
		return NULL;
	}
	return L->Data[K];
}

   查找成功的平均比较次数为 ( n + 1 ) / 2 (n+1)/2 (n+1)/2,平均时间性能为 O ( n ) O(n) O(n)

   3. 插入 (第 i ( 1 ⩽ i ⩽ n + 1 ) i (1 \leqslant i \leqslant n+1) i(1in+1)个位置上插入一个值为X的新元素)

数据结构(二)—— 线性结构(1):线性表_数据结构_06

void Insert(ElementType x, int i, List PtrL)
{
    if (PtrL->Last == MAXSIZE-1)  //表空间已满,不能插入
    {
        cout << "表满" << endl;
        return;
    }
    if ( i < 1 || i > PtrL->Last+2) 
    {
        cout << "位置不合法" << endl;
        return;
    }
    for (int j = PtrL->Last; j >= i-1; j--)
    {
        PtrL->Data[j+1]=PtrL->Data[j];  //将ai~an倒序向后移动
    }
    PtrL->Data[i-1] = X;  //新元素插入
    PtrL->Last++;  //Last仍指向最后元素
    return;
}

  平均移动次数为 n / 2 n/2 n/2,平均时间性能为 O ( n ) O(n) O(n)

   4. 删除 (删除表的第 i i i ( i ( 1 ⩽ i ⩽ n ) i (1 \leqslant i \leqslant n) i(1in))个位置上的元素)

数据结构(二)—— 线性结构(1):线性表_数据结构_07

void Delete(int i, List PtrL)
{
    if(i < 1 || i > PtrL->Last+1 )
    {
        cout << "不存在第" << i << "个元素" << endl;
        return ;
    }
    for (int j = i; j <=PtrL->Last; j++)
    {
        PtrL->Data[j-1]= PtrL->Data[j]; //将a[i+1]~a[n]顺序向前移动
    }   
    PtrL->Last--;  //Last仍指向最后元素
    return;
}

  平均移动次数为 ( n − 1 ) / 2 (n-1) / 2 (n1)/2,平均时间性能为 O ( n ) O(n) O(n)

  线性表的顺序存储实现实例的代码如下所示。

#include<iostream>
using namespace std;
#define MAXSIZE 100  // MAXSIZE 定义为 Data 数组的大小
typedef int ElementType;  // ElementType 可定义为任意类型;typedef为现有类型创建一个新的名字
typedef struct LNode *List;  //typedef是预定义关键字,相当于类型重命名,起个简单的名字,这里就是用List表示struct LNode结构体指针 
struct LNode
{
	ElementType Data[MAXSIZE];
	int Last;  // Last 定义线性表的最后一个元素
};
List L;
//访问下标为 i 的元素:L->Data[i],下标从 0 开始
//线性表的长度:L->Last+1

List MakeEmpty(); //初始化顺序表 
int Find(ElementType X, List L); //查找 X 第一次出现的下标 
void Insert(ElementType X, int i, List L); //在下标为 i 的地方插入 X 
void Delete(int i, List L);   //删除下标为 i 的当前值 
ElementType FindKth(int K, List L);  //返回下标为 K 的当前值
int Length(List L);  //返回顺序表的长度 

//初始化 
List MakeEmpty()
{
	List L;
	L = (List)malloc(sizeof(struct LNode));  //malloc的返回值是一个指针,指向一段可用内存的起始地址
	L->Last = -1;
	return L;
}

// 插入
void Insert(ElementType X, int i, List L)
{
	if (L->Last == MAXSIZE - 1)  //位置已满
	{
		cout << "表满" << endl;
		return;
	}
	if (i < 0 || L->Last + 1 < i)  //位置越界,如果将数插入 L->Data[L->Last+1],下面都不用腾位置了
	{
		cout << "位置不合法" << endl;
		return;
	}
	for (int j = L->Last; j >= i; j--)   // 从后往前依次向后挪一个,给 a[i]腾出位置     
		L->Data[j + 1] = L->Data[j];
	L->Data[i] = X;    //新元素插入
	L->Last++;    // Last仍然指向最后元素
	return;
}

//删除
void Delete(int i, List L)
{
	if (i < 0 || L->Last < i)  //位置越界,而删除最多到 L->Data[L->Last]
	{
		cout << "L->Data[" << i << "]不存在元素" << endl;
		return;
	}
	for (int j = i; j <= L->Last; j++)   // 从前往后依次向前挪一个,将 a[i] 覆盖了 
		L->Data[j] = L->Data[j + 1];
	L->Last--;  // Last仍然指向最后元素
	return;
}

// 按值查找 
int Find(ElementType X, List L)
{
	int i = 0;
	while (i <= L->Last && L->Data[i] != X)
		i++;
	if (L->Last < i)  //如果没找到,返回 -1
		return -1;
	else    // 找到后返回下标 
		return i;
}

// 按序查找
ElementType FindKth(int K, List L)
{
	if (K < 0 || L->Last < K)  //位置越界
	{
		cout << "L->Data[" << K << "]不存在元素" << endl;
		return NULL;
	}
	return L->Data[K];
}

//表长
int Length(List L)
{
	return L->Last + 1;
}

int main() 
{
	L = MakeEmpty();  //MakeEmpty()清空定义的数据结构

	//插入
	Insert(11, 0, L); 
	cout << "在线性表L->Data[0]插入11" << endl;
	Insert(25, 0, L);
	cout << "在线性表L->Data[0]插入25" << endl;
	Insert(33, 0, L);
	cout << "在线性表L-Data[0]插入33" << endl;
	Insert(77, 0, L);
	cout << "在线性表L-Data[0]插入77" << endl;

	cout << "此时的线性表为:";
	for (int i = 0; i < Length(L); i++)
		cout << L->Data[i] << " ";
	cout << endl;

	//查找
	cout << "查找值为12的下标是:" << Find(12, L) << endl;
	cout << "查找值为33的下标是:" << Find(33, L) << endl;
	cout << "查找线性表中下标为3的值是:" << FindKth(3, L) << endl;
	cout << "查找线性表中下标为5的值是:" << FindKth(5, L) << endl;

	//删除
	Delete(2, L);
	cout << "删除线性表中下标为2的元素" << endl;
	Delete(2, L);
	cout << "删除线性表中下标为2的元素" << endl;

	cout << "此时的线性表为:";
	for (int i = 0; i < Length(L); i++)
		cout << L->Data[i] << " ";
	cout << endl;

	system("pause");
	return 0;
}

  代码运行结果如下图所示。

数据结构(二)—— 线性结构(1):线性表_数据结构_08

1.5 线性表的链式存储实现

  不要求逻辑上相邻的两个元素物理上也相邻,通过“链”建立起数据元素之间的逻辑关系。插入、删除不需要移动数据元素,只需要修改“链”。

  链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。

   线性表的链式存储数据结构定义如下所示。

typedef struct LNode *List;
struct LNode{
    ElementType Data;  //节点对应的数据
    List Next;  //下一个节点的位置
};
struct Lnode L;
List PtrL;

  线性表的链式存储中的下标从 1 开始。

数据结构(二)—— 线性结构(1):线性表_数据结构_09
   主要操作的实现:

   1. 求表长

int Length(List PtrL)
{
    List p = PtrL;  //p指向表的第一个结点
    int j = 0;
    while (p) 
    {
        p=p->Next;
        j++;  //当前p指向的是第j个结点
    }
    return j;
}

  时间性能为 O ( n ) O(n) O(n)

   2. 查找

   (1)按序号查找:FindKth

List FindKth(int K,List PtrL)
{  
    List p = PtrL;
    int i =1;
    while (p !=NULL && i < K )
    {
        p = p->Next;
        i++;
    }
    if ( i == K ) 
        return p;  //产找到第K个,返回指针
    else 
        return NULL;  //否则返回空
}

   (2)按值查找:Find

List Find(ElementType X, List PtrL)
{ 
    List p = PtrL;
    while ( p != NULL && p->Data != X )
        p = p->Next;
    return p;
}

  平均时间性能为 O ( n ) O(n) O(n)

   3. 插入 (在第 i − 1 ( 1 ⩽ i ⩽ n + 1 ) i-1(1\leqslant i \leqslant n+1) i1(1in+1)个结点后插入一个值为X的新结点)
    (1) 先构造一个新结点,用 s s s指向;
    (2) 再找到链表的第 i − 1 i-1 i1个结点,用 p p p指向;
    (3) 然后修改指针,插入结点( p p p之后插入新结点 s s s)。

    思考: 修改指针的两个步骤如果交换一下,将会发生什么?
数据结构(二)—— 线性结构(1):线性表_数据结构_10

List Insert(ElementType X, int i,List PtrL)
{
    List p, s;
    if ( i == 1 )  //新结点插入在表头
    {
        s = (List)malloc(sizeof(struct LNode));  //申请、填装结点
        s->Data = X;
        s->Next = PtrL;
        return s;
    }
    p = FindKth( i-1, PtrL );  //查找第i-1个结点
    if ( p == NULL )  //第i-1个不存在,不能插入
    {
        cout << "结点错误" << endl;
        return NULL;
    }
    else 
    {
        s= (List)malloc(sizeof(struct LNode));  /申请、填装结点
        s->Data = X;
        s->Next = p->Next;  //新结点插入在第i-1个结点的后面
        p->Next = s;
        return PtrL;
}

  平均查找次数为 n / 2 n/2 n/2,平均时间性能为 O ( n ) O(n) O(n)

   4. 删除 (删除链表的第 i ( 1 ⩽ i ⩽ n ) i (1\leqslant i\leqslant n) i(1in)个位置上的结点)
    (1) 先找到链表的第 i − 1 i-1 i1个结点,用 p p p指向;
    (2) 再用指针 s s s指向要被删除的结点( p p p的下一个结点);
    (3) 然后修改指针,删除 s s s所指结点;
    (4) 最后释放 s s s所指结点的空间。

    思考: 操作指针的几个步骤如果随意改变,将会发生什么?

数据结构(二)—— 线性结构(1):线性表_数据结构_11

List Delete(int i, List PtrL) 
{
    List p, s;
    if ( i == 1 )  //若要删除的是表的第一个结点
    {
        s = PtrL;  //s指向第1个结点
        if (PtrL != NULL) 
            PtrL = PtrL->Next;  //从链表中删除
        else 
            return NULL;
        free(s);
        return PtrL;
    }
    p = FindKth( i-1, PtrL );  //查找第i-1个结点
    if ( p == NULL)
    {
        cout<< "第" << i-1 << "个结点不存在" << endl;
        return NULL;
    }
    else if ( p->Next == NULL )
    {
        cout<< "第" << i << "个结点不存在" << endl;
        return NULL;
    }
    else
    {
        s= p->Next;  //s指向第i个结点*
        p->Next = s->Next;  //从链表中删除
        free(s);  //释放被删除结点
        return PtrL;
    }
}

  平均查找次数为 n / 2 n /2 n/2,平均时间性能为 O ( n ) O(n) O(n)

  线性表的链式存储实现实例的代码如下所示。

#include<iostream>
using namespace std;
typedef int ElementType; // ElementType 可定义为任意类型
typedef struct LNode *List;
struct LNode 
{
	ElementType Data;   //数据域 
	List Next;   // 下一个链表的地址 
};
List L;

List MakeEmpty(); //初始化链表 
int Length(List L);  // 以遍历链表的方法求链表长度 
List FindKth(int K, List L);  // 按序号查找 
List Find(ElementType X, List L);  // 按值查找 
List Insert(ElementType X, int i, List L);  //将 X 插入到第 i-1(i>0) 个结点之后 
List Delete(int i, List L); // 删除第 i(i>0) 个结点 
void Print(List L); // 输出链表元素 

// 初始化链表 
List MakeEmpty() 
{
	List L = (List)malloc(sizeof(struct LNode));
	L = NULL;
	return L;
}

//求表长 
int Length(List L) 
{
	List p = L;
	int len = 0;
	while (p)  // 当 p 不为空 
	{   
		p = p->Next;
		len++;
	}
	return len;
}

// 按序查找 
List FindKth(int K, List L) 
{
	List p = L;
	int i = 1;  //从 1 开始 
	while (p && i < K) {
		p = p->Next;
		i++;
	}
	if (i == K)    // 找到了 
		return p;
	else    // 未找到 
		return NULL;
}

// 按值查找  
List Find(ElementType X, List L) {
	List p = L;
	while (p && p->Data != X)
		p = p->Next;
	// 找到了,返回 p
	// 未找到,返回 NULL,此时 p 等于 NULL 
	return p;
}

/* 插入
1. 用 s 指向一个新的结点
2. 用 p 指向链表的第 i-1 个结点
3. s->Next = p->Next,将 s 的下一个结点指向 p 的下一个结点
4. p->Next = s,将 p 的下一结点改为 s   */
List Insert(ElementType X, int i, List L) {
	List p, s;
	if (i == 1) {     // 新结点插入在表头 
		s = (List)malloc(sizeof(struct LNode));
		s->Data = X;
		s->Next = L;
		return s;     //插入的结点为头结点 
	}
	p = FindKth(i - 1, L);   // 找到第 i-1 个结点
	if (!p) {   // 第 i-1 个结点不存在 
		cout << "结点错误" << endl;
		return NULL;
	}
	else {
		s = (List)malloc(sizeof(struct LNode));
		s->Data = X;
		s->Next = p->Next;   //将 s 的下一个结点指向 p 的下一个结点 
		p->Next = s;   // 将 p 的下一结点改为 s
		return L;
	}
}

/* 删除
1. 用 p 指向链表的第 i-1 个结点
2. 用 s 指向要被删除的的第 i 个结点
3. p->Next = s->Next,p 指针指向 s 后面
4. free(s),释放空间
*/
List Delete(int i, List L) {
	List p, s;
	if (i == 1) {   //如果要删除头结点 
		s = L;
		if (L)   // 如果不为空 
			L = L->Next;
		else
			return NULL;
		free(s);   // 释放被删除结点 
		return L;
	}
	p = FindKth(i - 1, L);    // 查找第 i-1 个结点
	if (!p || !(p->Next)) {     // 第 i-1 个或第 i 个结点不存在 
		cout << "结点错误" << endl;
		return NULL;
	}
	else {
		s = p->Next;    // s 指向第 i 个结点 
		p->Next = s->Next;  //从链表删除 
		free(s);  // 释放被删除结点 
		return L;
	}
}

// 输出链表元素 
void Print(List L) 
{
	List t;
	int flag = 1;
	cout << "当前链表为:";
	for (t = L; t; t = t->Next) 
	{
		cout << t->Data << " ";
		flag = 0;
	}
	if (flag)
		cout << "NULL";
	cout << endl;
}

int main() 
{
	L = MakeEmpty();
	Print(L);

	L = Insert(11, 1, L);
	L = Insert(25, 1, L);
	L = Insert(33, 2, L);
	L = Insert(77, 3, L);
	Print(L);

	cout << "当前链表长度为:" << Length(L) << endl;

	cout << "此时链表中第二个结点的值是:" << FindKth(2, L)->Data << endl;
	cout << "查找22是否在该链表中:";
	if (Find(22, L))
		cout << "是!" << endl;
	else
		cout << "否!" << endl;
	cout << "查找33是否在该链表中:";
	if (Find(33, L))
		cout << "是!" << endl;
	else
		cout << "否!" << endl;

	L = Delete(1, L);
	cout << "删除链表中下标为1的元素" << endl;
	L = Delete(3, L);
	cout << "删除链表中下标为3的元素" << endl;
	Print(L);

	system("pause");

	return 0;
}

  代码运行结果如下图所示。

数据结构(二)—— 线性结构(1):线性表_数据结构_12

1.6 广义表

  例: 之前已经表示了一元多项式,那么二元多项式又该如何表示?比如,给定二元多项式 P ( x , y ) = 9 x 12 y 2 + 4 x 12 + 15 x 8 y 3 − x 8 y + 3 x 2 P(x,y)= 9x^{12}y^{2}+4x^{12}+15x^{8}y^{3}-x^{8} y+3x^{2} P(x,y)=9x12y2+4x12+15x8y3x8y+3x2,进行表示。
  分析: 可以将上述二元多项式看成关于 x x x的一元多项式。
比如将 P ( x , y ) = ( 9 y 2 + 4 ) x 12 + ( 15 y 3 − y ) x 8 + 3 x 2 P(x,y)=(9y^{2}+4)x^{12}+(15y^{3}-y)x^{8}+3x^{2} P(x,y)=(9y2+4)x12+(15y3y)x8+3x2看成 a x 12 + b x 8 + c x 2 ax^{12} + bx^{8} + cx^{2} ax12+bx8+cx2,所以上述二元多项式可以用 “复杂”链表表示为:

数据结构(二)—— 线性结构(1):线性表_数据结构_13
  广义表(Generalized List)是线性表的推广。对于线性表而言,n个元素都是基本的单元素;广义表中,这些元素不仅可以是单元素也可以是另一个广义表。

数据结构(二)—— 线性结构(1):线性表_数据结构_14
  广义表数据结构定义如下所示。

typedef struct GNode *GList;
struct GNode{
    int Tag;  //标志域:0表示结点是单元素,1表示结点是广义表
    union  //子表指针域Sublist与单元素数据域Data复用,即共用存储空间
    {
        ElementType Data;
        GList SubList;
    }
    URegion;
    GList Next;  //指向后继结点
};

1.7 多重链表

  多重链表: 链表中的节点可能同时隶属于多个链。
    ⋄ \diamond 多重链表中结点的指针域会有多个,如前面例子包含了Next和SubList两个指针域;
    ⋄ \diamond 但包含两个指针域的链表并不一定是多重链表,比如双向链表不是多重链表。

  多重链表有广泛的用途:基本上如树、图这样相对复杂的数据结构都可以采用多重链表方式实现存储。

  例: 矩阵可以用二维数组表示,但二维数组表示有两个缺陷:
     ⋄ \diamond 一是数组的大小需要事先确定;
     ⋄ \diamond 二是对于“稀疏矩阵”,将造成大量的存储空间浪费。

A = [ 18 0 0 2 0 0 27 0 0 0 0 0 0 − 4 0 23 − 1 0 0 12 ] , B = [ 0 2 11 0 0 0 9 − 4 − 1 0 0 0 0 0 0 9 13 0 0 − 2 0 0 10 7 6 0 0 5 0 0 ] A=\begin{bmatrix} 18 & 0&0 &2 & 0\\ 0& 27& 0& 0& 0\\ 0& 0& 0& -4& 0\\ 23&-1 & 0 & 0&12 \end{bmatrix}, B=\begin{bmatrix} 0 & 2&11 &0 & 0& 0\\ 9& -4& -1& 0& 0& 0\\ 0& 0& 0& 9& 13& 0\\ 0& -2& 0& 0&10& 7\\ 6&0 & 0 & 5&0 & 0 \end{bmatrix} A=180023027010000204000012,B=090062402011100000905001310000070

  分析: 采用一种典型的多重链表——十字链表来存储稀疏矩阵。
      ⋄ \diamond 只存储矩阵非0元素项
       结点的数据域:行坐标Row、列坐标Col、数值value
      ⋄ \diamond 每个结点通过两个指针域,把同行、同列串起来
       ⋆ \star 行指针(或称为向右指针)Right
       ⋆ \star 列指针(或称为向下指针)Down

数据结构(二)—— 线性结构(1):线性表_数据结构_15
  用一个标识域Tag来区分头结点和非0元素结点:头节点的标识值为“Head”,矩阵非0元素结点的标识值为“Term”,如下图所示。

数据结构(二)—— 线性结构(1):线性表_数据结构_16