例:多项式的表示
-
顺序结构直接表示:
使用数组来储存系数,相应的下表就是对应项的指数 -
顺序结构储存非零项
使用结构数组, 其分量是 ( a i , i ) (a^i,i) (ai,i)
按照指数的大小从大到小来排列在结构数组中,不再关心下标的作用
SUMMARY
- 顺序储存结构一般定义两个量:
- int Position
- 节点的结构指针
- 链式储存一般定义两个量
- 节点的结构指针
- 链表的结构指针
- 链表的结尾一定要指向NULL不然遍历链表的时候就会越界报错
- 链表最好有头节点,方便操作
1.线性表
1.1基本概念
- 定义:线性表是由同类的数据元素构成有序序列的线性结构;
- 长度:表中元素的个数;
- 空表:没有元素的线性表;
- 表头、表尾:表的起始和结束位置
- 抽象数据类型描述
1.2 存储方法
1.2.1顺序储存实现及相应的操作
typedef int Position;
typedef struct LNode *List;
struct LNode {
ElementType Data[MAXSIZE];
Position Last;
};
/* 初始化 */
List MakeEmpty()
{
List L;
L = (List)malloc(sizeof(struct LNode));
L->Last = -1;
return L;
}
/* 查找 */
#define ERROR -1
Position Find( List L, ElementType X )
{
Position i = 0;
while( i <= L->Last && L->Data[i]!= X )
i++;
if ( i > L->Last ) return ERROR; /* 如果没找到,返回错误信息 */
else return i; /* 找到后返回的是存储位置 */
}
/* 插入 */
/*注意:在插入位置参数P上与课程视频有所不同,课程视频中i是序列位序(从1开始),这里P是存储下标位置(从0开始),两者差1*/
bool Insert( List L, ElementType X, Position P )
{ /* 在L的指定位置P前插入一个新元素X */
Position i;
if ( L->Last == MAXSIZE-1) {
/* 表空间已满,不能插入 */
printf("表满");
return false;
}
if ( P<0 || P>L->Last+1 ) { /* 检查插入位置的合法性,这里不能在两端添加元素 */
printf("位置不合法");
return false;
}
for( i=L->Last; i>=P; i-- )
L->Data[i+1] = L->Data[i]; /* 将位置P及以后的元素顺序向后移动 */
L->Data[P] = X; /* 新元素插入 */
L->Last++; /* Last仍指向最后元素 */
return true;
}
/* 删除 */
/*注意:在删除位置参数P上与课程视频有所不同,课程视频中i是序列位序(从1开始),这里P是存储下标位置(从0开始),两者差1*/
bool Delete( List L, Position P )
{ /* 从L中删除指定位置P的元素 */
Position i;
if( P<0 || P>L->Last ) { /* 检查空表及删除位置的合法性 */
printf("位置%d不存在元素", P );
return false;
}
for( i=P+1; i<=L->Last; i++ )
L->Data[i-1] = L->Data[i]; /* 将位置P+1及以后的元素顺序向前移动 */
L->Last--; /* Last仍指向最后元素 */
return true;
}
1.2.2 链式储存实现及操作
typedef struct LNode *PtrToLNode;
struct LNode {
ElementType Data;
PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;
/* 查找 */
#define ERROR NULL
Position Find( List L, ElementType X )
{
Position p = L; /* p指向L的第1个结点 */
while ( p && p->Data!=X )
p = p->Next;
/* 下列语句可以用 return p; 替换 */
if ( p )
return p;
else
return ERROR;
}
/* 带头结点的插入 */
/*注意:在插入位置参数P上与课程视频有所不同,课程视频中i是序列位序(从1开始),这里P是链表结点指针,在P之前插入新结点 */
bool Insert( List L, ElementType X, Position P )
{ /* 这里默认L有头结点 */
Position tmp, pre;
/* 查找P的前一个结点 */
for ( pre=L; pre&&pre->Next!=P; pre=pre->Next ) ;
if ( pre==NULL ) { /* P所指的结点不在L中 */
printf("插入位置参数错误\n");
return false;
}
else { /* 找到了P的前一个结点pre */
/* 在P前插入新结点 */
tmp = (Position)malloc(sizeof(struct LNode)); /* 申请、填装结点 */
tmp->Data = X;
tmp->Next = P;
pre->Next = tmp;
return true;
}
}
/* 带头结点的删除 */
/*注意:在删除位置参数P上与课程视频有所不同,课程视频中i是序列位序(从1开始),这里P是拟删除结点指针 */
bool Delete( List L, Position P )
{ /* 这里默认L有头结点 */
Position tmp, pre;
/* 查找P的前一个结点 */
for ( pre=L; pre&&pre->Next!=P; pre=pre->Next ) ;
if ( pre==NULL || P==NULL) { /* P所指的结点不在L中 */
printf("删除位置参数错误\n");
return false;
}
else { /* 找到了P的前一个结点pre */
/* 将P位置的结点删除 */
pre->Next = P->Next;
free(P);
return true;
}
2 广义表 General List
广义表是对线性表的推广,线性表中每个元素都得是单元素,广义表中这些元素也可以是其他广义表。
typedef struct GNode *Glist;
struct GNode{
int Tag;/*标志域,0表示union中是单元素,1表示是链表*/
union{ /*union共用储存空间*/
ElementType Data;
GList SubList;
}URegion;
Glist Next;/*指向后继节点*/
};
3 多重链表
- 多重链表中的节点可能属于多个链;
- 节点中可能含有多个指针域,如SubList和Next
- 但是包含多个指针域的不一定是多重链表,如循环双向链表
例:稀疏矩阵的储存
稀疏矩阵中的零会占用很多的内存,使用十字链表储存节省空间
- 每个节点的数据域:行,列,值
- 每个节点的指针域:两个指针域把同行同列都穿起来(行列构建双向链表)
–行指针/右指针/Right
–列指针/左指针/Left
使用广义表的知识合并Head和Term节点,会发现第i行和第i列的Head节点实际上是一个节点;因为列head的left指针域与行head的right指针域没有使用,合并资源后可以看作一个
4 堆栈
堆栈是一种特殊的线性表,以后入先出的顺序操作其中的元素。
例:后缀表达式
计算机是如何处理我们遇到的加减乘除计算式的?
前
缀
表
达
式
:
a
+
b
∗
c
−
d
/
e
前缀表达式:a+b*c-d/e
前缀表达式:a+b∗c−d/e
对
应
的
后
缀
表
达
式
:
a
b
c
∗
+
d
e
/
−
对应的后缀表达式:abc*+de/-
对应的后缀表达式:abc∗+de/−
转化为对应的后缀表达式:
–遇到运算数就记住;
–遇到运算符就用最近记住的两个数进行计算。
这里就要用到堆栈的思想
4.1 堆栈的抽象数据类型描述
其中最主要的是push和pop两种操作
4.2 操作集的顺序储存实现
typedef int Position;
struct SNode {
ElementType *Data; /* 存储元素的数组 */
Position Top; /* 栈顶指针 */
int MaxSize; /* 堆栈最大容量 */
};
typedef struct SNode *Stack;
Stack CreateStack( int MaxSize )
{
Stack S = (Stack)malloc(sizeof(struct SNode));
S->Data = (ElementType *)malloc(MaxSize * sizeof(ElementType));
S->Top = -1;
S->MaxSize = MaxSize;
return S;
}
bool IsFull( Stack S )
{
return (S->Top == S->MaxSize-1);
}
bool Push( Stack S, ElementType X )
{
if ( IsFull(S) ) {
printf("堆栈满");
return false;
}
else {
S->Data[++(S->Top)] = X;
return true;
}
}
bool IsEmpty( Stack S )
{
return (S->Top == -1);
}
ElementType Pop( Stack S )
{
if ( IsEmpty(S) ) {
printf("堆栈空");
return ERROR; /* ERROR是ElementType的特殊值,标志错误 */
}
else
return ( S->Data[(S->Top)--] );
}
4.3 操作集的链式储存实现
typedef struct SNode *PtrToSNode;
struct SNode {
ElementType Data;
PtrToSNode Next;
};
typedef PtrToSNode Stack;
Stack CreateStack( )
{ /* 构建一个堆栈的头结点,返回该结点指针 */
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}
bool IsEmpty ( Stack S )
{ /* 判断堆栈S是否为空,若是返回true;否则返回false */
return ( S->Next == NULL );
}
bool Push( Stack S, ElementType X )
{ /* 将元素X压入堆栈S */
PtrToSNode TmpCell;
TmpCell = (PtrToSNode)malloc(sizeof(struct SNode));
TmpCell->Data = X;
TmpCell->Next = S->Next;
S->Next = TmpCell;
return true;
}
ElementType Pop( Stack S )
{ /* 删除并返回堆栈S的栈顶元素 */
PtrToSNode FirstCell;
ElementType TopElem;
if( IsEmpty(S) ) {
printf("堆栈空");
return ERROR;
}
else {
FirstCell = S->Next;
TopElem = FirstCell->Data;
S->Next = FirstCell->Next;
free(FirstCell);
return TopElem;
}
}
5 队列
从一头插入,从另一头取出
5.1 队列的抽象数据类型描述
为了使用方便我们采用循环队列:
又因为循环队列在语言中实际上并没有直接的数据类型,我们使用普通的数组和链表来实现;在这里我们需要用到除法来实现循环的功能。
5.2 操作集顺序储存实现
注意在顺序储存中 front 总是指向第一个元素的前一个位置,整个数组总最多放
n-1个元素,不放满。但是链表中没有大小限制了,front 指向第一个元素的地址。
typedef int Position;
struct QNode {
ElementType *Data; /* 存储元素的数组 */
Position Front, Rear; /* 队列的头、尾指针 */
int MaxSize; /* 队列最大容量 */
};
typedef struct QNode *Queue;
Queue CreateQueue( int MaxSize )
{
Queue Q = (Queue)malloc(sizeof(struct QNode));
Q->Data = (ElementType *)malloc(MaxSize * sizeof(ElementType));
Q->Front = Q->Rear = 0;
Q->MaxSize = MaxSize;
return Q;
}
bool IsFull( Queue Q )
{
return ((Q->Rear+1)%Q->MaxSize == Q->Front);
}
bool AddQ( Queue Q, ElementType X )
{
if ( IsFull(Q) ) {
printf("队列满");
return false;
}
else {
Q->Rear = (Q->Rear+1)%Q->MaxSize;
Q->Data[Q->Rear] = X;
return true;
}
}
bool IsEmpty( Queue Q )
{
return (Q->Front == Q->Rear);
}
ElementType DeleteQ( Queue Q )
{
if ( IsEmpty(Q) ) {
printf("队列空");
return ERROR;
}
else {
Q->Front =(Q->Front+1)%Q->MaxSize;
return Q->Data[Q->Front];
}
}
5.3 操作集链式储存实现
typedef struct Node *PtrToNode;
struct Node { /* 队列中的结点 */
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode Position;
struct QNode {
Position Front, Rear; /* 队列的头、尾指针 */
int MaxSize; /* 队列最大容量 */
};
typedef struct QNode *Queue;
bool IsEmpty( Queue Q )
{
return ( Q->Front == NULL);
}
ElementType DeleteQ( Queue Q )
{
Position FrontCell;
ElementType FrontElem;
if ( IsEmpty(Q) ) {
printf("队列空");
return ERROR;
}
else {
FrontCell = Q->Front;
if ( Q->Front == Q->Rear ) /* 若队列只有一个元素 */
Q->Front = Q->Rear = NULL; /* 删除后队列置为空 */
else
Q->Front = Q->Front->Next;
FrontElem = FrontCell->Data;
free( FrontCell ); /* 释放被删除结点空间 */
return FrontElem;
}
}
例 多项式加法的链式实现
其中的
A
t
t
a
c
h
(
)
Attach()
Attach()函数实现如下