栈
栈的基本概念
栈(Stack)是一种特殊的线性表,它具有以下基本概念和特点:
操作受限:栈遵循“后进先出”(Last In First Out,简称 LIFO)的原则。这意味着最后进入栈的元素将首先被取出。
操作方式:
- 入栈(Push):将元素添加到栈的顶部。
- 出栈(Pop):从栈的顶部移除元素。
应用场景:
- 函数调用:函数的调用和返回可以通过栈来管理。
- 表达式求值:例如中缀表达式转后缀表达式的计算过程。
- 回溯算法:在搜索问题中用于保存状态和回溯。
实现方式:
可以使用数组或链表来实现栈。
例如,假设我们有一个栈,依次进行入栈操作:Push(1),Push(2),Push(3)。此时栈内元素从栈顶到栈底的顺序为 3、2、1。
然后进行出栈操作 Pop,首先取出的元素将是 3,因为它是最后入栈的。
栈的基本操作
-
入栈(Push):
- 概念:将一个元素添加到栈的顶部。
- 示例:假设初始栈为
[]
,执行Push(5)
后,栈变为[5]
。
-
出栈(Pop):
- 概念:移除并返回栈顶的元素。
- 示例:如果栈为
[5, 3, 2]
,执行Pop
操作后,栈变为[5, 3]
,并返回2
。
-
获取栈顶元素(Top/Peek):
- 概念:返回栈顶的元素,但不将其从栈中移除。
- 示例:对于栈
[5, 8]
,Top
操作将返回8
,栈保持不变。
-
判断栈是否为空(IsEmpty):
- 概念:检查栈中是否没有元素。
- 示例:如果栈为
[]
,IsEmpty
操作将返回True
;若栈为[10]
,则返回False
。
-
获取栈的大小(Size):
- 概念:返回栈中元素的个数。
- 示例:对于栈
[3, 7, 1]
,Size
操作将返回3
。
栈的顺序存储结构
一、栈的顺序存储结构概述
栈的顺序存储结构是指用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时附设指针 top
指示栈顶元素在顺序栈中的位置。
在顺序存储结构中,栈底位置是固定不变的,可设置在数组下标小的一端(如 0 下标处),也可以设置在数组下标大的一端。
二、栈的顺序存储结构的基本操作
-
示例:InitStack(S)
:初始化一个空栈S
。int stack[MAX_SIZE]; int top = - 1;
-
如果StackEmpty(S)
:判断栈S
是否为空。top == -1
,则栈为空,返回true
;否则返回false
。 -
首先判断栈是否已满,如果Push(S, x)
:将元素x
进栈。top == MAX_SIZE - 1
,则栈满,不能进栈;否则将元素x
存入stack[++top]
处。 -
首先判断栈是否为空,如果Pop(S, x)
:出栈操作,并将出栈元素赋给x
。top == -1
,则栈为空,不能出栈;否则将栈顶元素赋给x
,并将top
减 1。 -
如果GetTop(S, x)
:获取栈顶元素,并将其赋给x
,但栈顶元素不出栈。top!= -1
,则将stack[top]
赋给x
;否则栈为空,无法获取栈顶元素。
三、栈的顺序存储结构的优缺点
-
优点
- 实现简单,易于理解和操作。
- 存储密度高,不需要额外的指针来链接元素。
-
缺点
- 栈的容量固定,可能会出现栈溢出或栈空间浪费的情况。
共享栈
共享栈是指两个栈共享同一片存储空间,这片存储空间不单独属于任何一个栈,某个栈需要的多一点,它就可能得到更多的存储空间。两个栈的栈底在这片存储空间的两端,当元素入栈时,两个栈的栈顶指针相向而行。
共享栈的实现需要考虑以下几个关键问题:
- 栈底位置:共享栈的栈底位置是固定不变的,可设置在数组下标小的一端(如 0 下标处),也可以设置在数组下标大的一端。
- 栈顶指针:需要设置两个栈顶指针,分别指示两个栈的栈顶位置。栈顶指针的移动方向取决于栈的增长方向,当栈顶指针相遇时,表示共享栈已满。
- 入栈操作:当元素入栈时,需要根据栈顶指针的位置和栈的增长方向,将元素存储在合适的位置,并更新栈顶指针。
- 出栈操作:当元素出栈时,需要根据栈顶指针的位置和栈的增长方向,取出栈顶元素,并更新栈顶指针。
以下是一个使用 C 语言实现共享栈的示例代码:
#include <stdio.h>
#include <stdlib.h>
#define MAX_SIZE 100
typedef struct {
int data[MAX_SIZE];
int top1;
int top2;
} SharedStack;
// 初始化共享栈
void initSharedStack(SharedStack *stack) {
stack->top1 = -1;
stack->top2 = MAX_SIZE;
}
// 入栈操作
void push(SharedStack *stack, int stackNumber, int value) {
if (stackNumber == 1) {
if (stack->top1 == stack->top2 - 1) {
printf("栈 1 已满,无法入栈\n");
return;
}
stack->data[++stack->top1] = value;
} else if (stackNumber == 2) {
if (stack->top2 == stack->top1 + 1) {
printf("栈 2 已满,无法入栈\n");
return;
}
stack->data[--stack->top2] = value;
} else {
printf("输入的栈号错误,请重新输入\n");
}
}
// 出栈操作
int pop(SharedStack *stack, int stackNumber) {
if (stackNumber == 1) {
if (stack->top1 == -1) {
printf("栈 1 为空,无法出栈\n");
return -1;
}
return stack->data[stack->top1--];
} else if (stackNumber == 2) {
if (stack->top2 == MAX_SIZE) {
printf("栈 2 为空,无法出栈\n");
return -1;
}
return stack->data[stack->top2++];
} else {
printf("输入的栈号错误,请重新输入\n");
return -1;
}
}
// 获取栈顶元素
int getTop(SharedStack *stack, int stackNumber) {
if (stackNumber == 1) {
if (stack->top1 == -1) {
printf("栈 1 为空,无法获取栈顶元素\n");
return -1;
}
return stack->data[stack->top1];
} else if (stackNumber == 2) {
if (stack->top2 == MAX_SIZE) {
printf("栈 2 为空,无法获取栈顶元素\n");
return -1;
}
return stack->data[stack->top2];
} else {
printf("输入的栈号错误,请重新输入\n");
return -1;
}
}
int main() {
SharedStack stack;
initSharedStack(&stack);
push(&stack, 1, 10);
push(&stack, 1, 20);
push(&stack, 2, 30);
push(&stack, 2, 40);
printf("栈 1 的栈顶元素为:%d\n", getTop(&stack, 1));
printf("栈 2 的栈顶元素为:%d\n", getTop(&stack, 2));
printf("栈 1 的出栈元素为:%d\n", pop(&stack, 1));
printf("栈 2 的出栈元素为:%d\n", pop(&stack, 2));
return 0;
}
栈的链式存储结构
一、栈的链式存储结构概述
栈的链式存储结构,也称为链栈。它是通过链表的方式来实现栈的功能。
在链栈中,每个节点包含数据域和指针域。数据域用于存储栈元素的值,指针域用于指向下一个节点。
链栈的栈顶在链表的头部,新元素入栈时,只需将新节点插入到链表头部;出栈时,删除链表头部的节点。
二、链栈的基本操作
InitStack()
:初始化链栈,创建一个空的链栈。示例:
struct StackNode {
int data;
struct StackNode *next;
};
struct StackNode *initStack() {
return NULL;
}
Push(StackNode *top, int x)
:入栈操作,将元素 x
插入到链栈的栈顶。
示例:
struct StackNode *push(struct StackNode *top, int x) {
struct StackNode *newNode = (struct StackNode *)malloc(sizeof(struct StackNode));
newNode->data = x;
newNode->next = top;
top = newNode;
return top;
}
Pop(StackNode *top, int *x)
:出栈操作,将栈顶元素弹出,并通过参数 x
返回该元素的值。示例:
struct StackNode *pop(struct StackNode *top, int *x) {
if (top == NULL) {
printf("栈为空,无法出栈\n");
return NULL;
}
struct StackNode *p = top;
*x = p->data;
top = top->next;
free(p);
return top;
}
StackEmpty(StackNode *top)
:判断链栈是否为空。如果为空,返回 1
;否则,返回 0
。示例:
int stackEmpty(struct StackNode *top) {
return top == NULL;
}
三、链栈的优缺点
-
优点
- 可以动态地分配内存空间,不存在栈满上溢的问题。
- 便于多个栈共享存储空间。
-
缺点
- 每个节点需要额外的指针空间来存储指向下一个节点的地址,存在一定的空间开销。
- 对栈的操作(入栈、出栈)需要对指针进行操作,相对于顺序存储结构,实现起来稍微复杂一些。
队列
队列的基本概念
一、队列的基本概念
队列(Queue)是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。
队列的操作特性符合“先进先出”(First In First Out,简称 FIFO)的原则。就像在日常生活中的排队一样,先排队的人先接受服务然后离开队列。
二、队列的基本组成
-
队头(front):指允许删除的一端,即最先进入队列的元素将最先被删除的位置。
-
队尾(rear):指允许插入的一端,新元素将从队尾进入队列。
三、队列的应用场景
-
排队系统:如银行排队叫号系统、超市收银台排队系统等。
-
任务调度:操作系统中的任务调度,按照任务进入队列的先后顺序进行处理。
-
缓冲区:例如打印机的打印任务缓冲区,先进入缓冲区的任务先被打印。
-
图的广度优先搜索算法:使用队列来存储待访问的节点。
四、队列的基本操作
初始化队列 Q
,创建一个空队列。
示例(使用链表实现):
struct QueueNode {
int data;
struct QueueNode *next;
};
struct Queue {
struct QueueNode *front;
struct QueueNode *rear;
};
void InitQueue(struct Queue *Q) {
Q->front = Q->rear = NULL;
}
EnQueue(Q, x)
:将元素 x
插入到队列 Q
的队尾。
示例(链表实现):
void EnQueue(struct Queue *Q, int x) {
struct QueueNode *newNode = (struct QueueNode *)malloc(sizeof(struct QueueNode));
newNode->data = x;
newNode->next = NULL;
if (Q->rear == NULL) { // 如果队列为空
Q->front = Q->rear = newNode;
} else {
Q->rear->next = newNode;
Q->rear = newNode;
}
}
DeQueue(Q, x)
:从队列 Q
的队头删除一个元素,并将其值赋给 x
。
示例(链表实现):
int DeQueue(struct Queue *Q, int *x) {
if (Q->front == NULL) {
return 0; // 队列为空,删除失败
}
struct QueueNode *p = Q->front;
*x = p->data;
if (Q->front == Q->rear) { // 队列中只有一个元素
Q->front = Q->rear = NULL;
} else {
Q->front = Q->front->next;
}
free(p);
return 1; // 删除成功
}
QueueEmpty(Q)
:判断队列 Q
是否为空。如果为空,返回 1
;否则,返回 0
。
示例(链表实现):
int QueueEmpty(struct Queue *Q) {
return Q->front == NULL;
}
GetHead(Q, x)
:获取队列 Q
的队头元素的值,并将其赋给 x
,但不删除队头元素。
示例(链表实现):
int GetHead(struct Queue *Q, int *x) {
if (Q->front == NULL) {
return 0; // 队列为空
}
*x = Q->front->data;
return 1; // 获取成功
}
队列的顺序存储
一、队列的顺序存储概述
队列的顺序存储是指用一组地址连续的存储单元依次存放从队头到队尾的元素。
一般使用一个数组 Queue[MaxSize]
来实现顺序队列,同时设置两个指针 front
和 rear
分别指示队头元素和队尾元素的下一个位置。
二、顺序队列的基本操作
InitQueue(Queue)
:初始化一个空的顺序队列
初始化时,将 front
和 rear
都设置为 0,即:
void InitQueue(int Queue[], int *front, int *rear) {
*front = *rear = 0;
}
EnQueue(Queue, x)
:将元素 x
入队
如果 (*rear + 1) % MaxSize == *front
,则表示队列已满,不能入队;否则将 x
放入 Queue[*rear]
,然后 *rear = (*rear + 1) % MaxSize
int EnQueue(int Queue[], int *front, int *rear, int x) {
if ((*rear + 1) % MaxSize == *front) {
return 0; // 队列已满,入队失败
}
Queue[*rear] = x;
*rear = (*rear + 1) % MaxSize;
return 1; // 入队成功
}
DeQueue(Queue, x)
:出队操作,并将出队元素赋值给 x
如果 *front == *rear
,则表示队列为空,不能出队;否则将 Queue[*front]
赋值给 x
,然后 *front = (*front + 1) % MaxSize
int DeQueue(int Queue[], int *front, int *rear, int *x) {
if (*front == *rear) {
return 0; // 队列为空,出队失败
}
*x = Queue[*front];
*front = (*front + 1) % MaxSize;
return 1; // 出队成功
}
QueueEmpty(front, rear)
:判断队列是否为空
如果 *front == *rear
,则队列为空,返回 1;否则返回 0
int QueueEmpty(int *front, int *rear) {
return *front == *rear;
}
三、顺序队列的“假溢出”问题与循环队列
在顺序队列中,当 rear
到达数组的末尾时,即使数组的前端还有空闲空间,也无法再进行入队操作,这种现象称为“假溢出”。
为了解决“假溢出”问题,引入了循环队列。循环队列将顺序队列的存储空间想象成一个环形,当 rear
或 front
到达数组末尾时,下一个位置又回到数组的开头。
四、循环队列
一、循环队列的基本概念
循环队列是队列的一种顺序存储结构的变形,是把存储队列元素的表从逻辑上看成一个环。
在循环队列中,为了区分队空和队满的情况,通常规定当队列中元素个数为队列长度(数组大小)时为队满,队中元素个数为 0 时为队空。
二、循环队列的基本操作
初始化
初始化一个循环队列,需要设置队头指针 front
和队尾指针 rear
都为 0,同时指定队列的最大容量 maxSize
。
#define MAXSIZE 100 // 假设队列最大容量为 100
struct CircularQueue {
int data[MAXSIZE];
int front;
int rear;
};
void initCircularQueue(struct CircularQueue *cq) {
cq->front = 0;
cq->rear = 0;
}
入队操作(EnQueue)
如果 (cq->rear + 1) % MAXSIZE == cq->front
,表示队列已满,不能入队;否则将元素插入队尾,并更新队尾指针 rear = (rear + 1) % MAXSIZE
。
int enQueue(struct CircularQueue *cq, int element) {
if ((cq->rear + 1) % MAXSIZE == cq->front) {
return 0; // 队列已满,入队失败
}
cq->data[cq->rear] = element;
cq->rear = (cq->rear + 1) % MAXSIZE;
return 1; // 入队成功
}
出队操作(DeQueue)
如果 cq->front == cq->rear
,表示队列为空,不能出队;否则取出队头元素,更新队头指针 front = (front + 1) % MAXSIZE
。
int deQueue(struct CircularQueue *cq, int *element) {
if (cq->front == cq->rear) {
return 0; // 队列为空,出队失败
}
*element = cq->data[cq->front];
cq->front = (cq->front + 1) % MAXSIZE;
return 1; // 出队成功
}
获取队头元素(GetFront)
如果队列为空,则无法获取队头元素;否则返回队头元素。
int getFront(struct CircularQueue *cq, int *element) {
if (cq->front == cq->rear) {
return 0; // 队列为空
}
*element = cq->data[cq->front];
return 1; // 获取成功
}
判断队列是否为空(isEmpty)
如果 cq->front == cq->rear
,则队列为空,返回 1;否则返回 0。
int isEmpty(struct CircularQueue *cq) {
return cq->front == cq->rear;
}
判断队列是否已满(isFull)
如果 (cq->rear + 1) % MAXSIZE == cq->front
,则队列已满,返回 1;否则返回 0。
int isFull(struct CircularQueue *cq) {
return (cq->rear + 1) % MAXSIZE == cq->front;
}
三、循环队列的应用场景
循环队列常用于需要按顺序处理元素,并且元素数量不确定或有一定限制的场景,如:
-
操作系统中的进程调度,多个进程按照进入队列的先后顺序等待被调度执行。
-
网络数据包的缓存和处理,先到达的数据包先被处理。
-
资源分配和管理系统中,请求资源的任务按顺序排队等待资源分配。
队列的链式存储结构
一、队列的链式存储结构概述
队列的链式存储结构也称为链队列。它是通过链表的形式来实现队列的功能。
在链队列中,需要两个指针:一个是队头指针 front
,指向链表的第一个节点(即队头元素);另一个是队尾指针 rear
,指向链表的最后一个节点(即队尾元素)。
二、链队列的节点结构
struct QueueNode {
int data;
struct QueueNode *next;
};
三、链队列的基本操作
InitQueue(LinkQueue *Q)
:初始化链队列
初始化时,将队头指针和队尾指针都置为 NULL
,表示一个空的链队列。
void InitQueue(LinkQueue *Q) {
Q->front = Q->rear = NULL;
}
EnQueue(LinkQueue *Q, int e)
:入队操作
创建一个新节点,将元素值赋给该节点的数据域,然后将该节点链接到队尾。如果队列为空,新节点既是队头也是队尾;否则,将新节点链接到原来的队尾节点之后,并更新队尾指针。
void EnQueue(LinkQueue *Q, int e) {
QueueNode *newNode = (QueueNode *)malloc(sizeof(QueueNode));
newNode->data = e;
newNode->next = NULL;
if (Q->rear == NULL) { // 队列为空
Q->front = newNode;
Q->rear = newNode;
} else {
Q->rear->next = newNode;
Q->rear = newNode;
}
}
DeQueue(LinkQueue *Q, int *e)
:出队操作
如果队列为空,则无法出队;否则,取出队头节点的数据,将队头指针指向下一个节点,如果出队后队列为空,还需要将队尾指针置为 NULL
,最后释放出队节点的内存空间。
int DeQueue(LinkQueue *Q, int *e) {
if (Q->front == NULL) { // 队列为空
return 0;
}
QueueNode *p = Q->front;
*e = p->data;
Q->front = Q->front->next;
if (Q->front == NULL) { // 出队后队列为空
Q->rear = NULL;
}
free(p);
return 1;
}
DestroyQueue(LinkQueue *Q)
:销毁链队列
依次释放队列中的所有节点,将队头指针和队尾指针置为 NULL
。
void DestroyQueue(LinkQueue *Q) {
QueueNode *p, *q;
p = Q->front;
while (p) {
q = p;
p = p->next;
free(q);
}
Q->front = Q->rear = NULL;
}
GetHead(LinkQueue *Q, int *e)
:获取队头元素
如果队列为空,则无法获取;否则,将队头元素的值赋给指定的变量。
int GetHead(LinkQueue *Q, int *e) {
if (Q->front == NULL) { // 队列为空
return 0;
}
*e = Q->front->data;
return 1;
}
四、链队列的应用场景
链队列常用于需要动态分配内存、队列长度不确定或者需要频繁进行入队和出队操作的场景,例如:
-
网络数据的收发缓冲队列。
-
多线程任务队列。
-
操作系统中的作业队列等。
双端队列
双端队列(deque,即 double-ended queue 的缩写)是一种具有队列和栈性质的数据结构,即可(也只能)在线性表的两端进行插入和删除。
双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。双端队列是限定插入和删除操作在表的两端进行的线性表。这两端分别称做端点1和端点2。
双端队列支持以下操作:
Deque()
:创建一个空的双向队列,它不需要参数,且会返回一个空的双端队列。addFront(item)
:将一个元素添加到双端队列的前端,它接受一个元素作为参数,没有返回值。addRear(item)
:将一个元素添加到双端队列的后端,它接受一个元素作为参数,没有返回值。removeFront()
:从双端队列的前端移除一个元素,它不需要参数,且会返回一个元素,并修改双端队列的内容。removeRear()
:从双端队列的后端移除一个元素,它不需要参数,且会返回一个元素,并修改双端队列的内容。isEmpty()
:检查双端队列是否为空,它不需要参数,且会返回一个布尔值。size()
:返回双端队列中的元素的数目,它不需要参数,且会返回一个整数。
数组
多维数组的储存
一、多维数组的存储方式
多维数组在计算机内存中的存储方式主要有两种常见的形式:行优先顺序(Row-major Order)和列优先顺序(Column-major Order)。
行优先顺序(以二维数组为例)
假设我们有一个二维数组 A[m][n]
,在行优先顺序存储中,先存储第一行的元素,接着存储第二行的元素,以此类推。
如果数组的起始地址为 base
,每个元素所占存储空间为 size
,那么对于数组中的元素 A[i][j]
,其存储地址 LOC(i, j)
的计算公式为:
LOC(i, j) = base + (i * n + j) * size
例如,有一个 2×3
的二维数组 A
:
A = [[1, 2, 3], [4, 5, 6]]
如果起始地址 base = 100
,每个元素占 2
个字节,那么 A[1][2]
的存储地址为:
LOC(1, 2) = 100 + (1 * 3 + 2) * 2 = 100 + 8 = 108
列优先顺序(以二维数组为例)
在列优先顺序存储中,先存储第一列的元素,接着存储第二列的元素,以此类推。
对于元素 A[i][j]
,其存储地址 LOC(i, j)
的计算公式为:
LOC(i, j) = base + (j * m + i) * size
二、多维数组的扩展存储(以三维数组为例)
对于一个三维数组 A[x][y][z]
行优先顺序
存储顺序为:先按第一页的第一行、第一页的第二行……第一页的最后一行;接着第二页的第一行、第二页的第二行……;以此类推。
存储地址 LOC(i, j, k)
的计算公式为:
LOC(i, j, k) = base + (i * (y * z) + j * z + k) * size
列优先顺序
存储顺序为:先按第一列的第一页、第一列的第二页……;接着第二列的第一页、第二列的第二页……;以此类推。
存储地址 LOC(i, j, k)
的计算公式为:
LOC(i, j, k) = base + (k * (x * y) + j * x + i) * size
三、多维数组的一般存储方式
对于n
维数组A[b1][b2]...[bn]
,如果以行序为主序存储,起始地址为LOC(0, 0,..., 0)
,每个元素占用L
个存储单元,对于任意元素A[i1][i2]...[in]
,其存储地址为:
LOC(i1, i2,..., in) = LOC(0, 0,..., 0) + [i1 * (b2 * b3 *...* bn) + i2 * (b3 *...* bn) +...+ in - 1 * bn + in] * L
矩阵
一、特殊矩阵的压缩存储概述
特殊矩阵是指具有一定特性和规律的矩阵,对其进行压缩存储可以节省存储空间,提高存储和处理效率。
二、对称矩阵的压缩存储
对于一个n
阶对称矩阵A
,其元素满足A[i][j] = A[j][i]
(i
,j
为矩阵的行和列索引)。
由于对称矩阵的这种对称性,我们只需存储其下三角(包括主对角线)或上三角部分的元素。
假设存储下三角部分(包括主对角线),将这些元素按行优先顺序存放到一个一维数组B
中。
设矩阵A
中某元素A[i][j]
(i >= j
)在一维数组B
中的下标为k
,则k = i * (i + 1) / 2 + j
例如,一个 3 阶对称矩阵:
1 2 3
2 4 5
3 5 6
压缩存储到一维数组B
中为[1, 2, 3, 4, 5, 6]
三、三角矩阵的压缩存储
上三角矩阵
上三角矩阵是指矩阵的下三角(不包括主对角线)元素全为零的矩阵。
同样按行优先顺序将非零元素存放到一维数组B
中。
设矩阵A
中某元素A[i][j]
(i <= j
)在一维数组B
中的下标为k
,则k = i * (2 * n - i + 1) / 2 + j - i
下三角矩阵
下三角矩阵是指矩阵的上三角(不包括主对角线)元素全为零的矩阵。
设矩阵A
中某元素A[i][j]
(i >= j
)在一维数组B
中的下标为k
,则k = i * (i + 1) / 2 + j
四、对角矩阵的压缩存储
对角矩阵是指所有非零元素都集中在主对角线及其两侧相邻对角线上的矩阵。
假设一个m
行n
列的对角矩阵,共有l
条对角线(包括主对角线),将其按行优先顺序存放到一维数组B
中。
设矩阵A
中某元素A[i][j]
在第p
条对角线上(从主对角线开始计数,主对角线为第 0 条对角线),在一维数组B
中的下标为k
,则k = 3 * p + j - i
稀疏矩阵
一、稀疏矩阵的概念
稀疏矩阵(Sparse Matrix)是指矩阵中大多数元素为零的矩阵。与之相对的是稠密矩阵(Dense Matrix),其中的元素大部分都是非零值。
二、稀疏矩阵的存储方式
三元组顺序表
使用三个整数来表示矩阵中的非零元素,分别是行下标、列下标和元素值。将所有的非零元素以三元组的形式存放在一个顺序表中。
例如,有一个稀疏矩阵:
0 0 3 0 4
0 0 5 7 0
0 0 0 0 0
0 2 6 0 0
对应的三元组顺序表为:((0, 2, 3), (0, 4, 4), (1, 2, 5), (1, 3, 7), (3, 1, 2), (3, 2, 6))
十字链表
十字链表是将稀疏矩阵中的每一行和每一列都用一个链表来表示,链表中的节点除了存储元素的值、行下标和列下标外,还有指向下一个行节点和下一个列节点的指针。