线性表是数据结构中最基础的一种数据结构类型。
2.1.1引子:多项式表示
问题1:如何用程序设计语言表示多项式?
分析:多项式中关键数据——项数n(有几项)、各项系数ai、x的指数。
重点是表示多项式的每一项
方法1:顺序存储结构直接表示
分析:
多项式的每一项系数ai放在数组中,即 a[i]存储,同时x的指数为i。
即创建数组a,按指数顺序存储各项系数。
如:f(x)=
下标i | 0 | 1 | 2 | 3 | 4 | 5 |
a[i] | 1 | 0 | -3 | 0 | 0 | 4 |
x |
优点:两个多项式相加简单,直接两个数组对应分量相加即可。
缺点:多项式较空(非零项很少)时,很浪费存储空间。
经过思考改进,可以得到第二种方法。
※方法2:顺序存储结构表示非零项
分析:
把多项式看成是一个(,i)的集合。
用结构数组表示:数组的分量由系数和指数i组成。
如:f(x)=
数组下标 | 0 | 1 | 2 | …… | |
系数 | 4 | -3 | 1 | …… | |
指数i | 5 | 2 | 0 | …… |
要点:按指数大小有序存储❗
相加过程:从头开始,比较两个多项式当前对应项的指数。
P1:(9,12), (15,8), (3,2)
P2:(26,19), (-4,8), (-13,6),(82,0)
P3:(26,19),(9,12), (15-4,8), (-13,6),(3,2),(82,0)
这种方法节省空间并且操作效率也不错。
同时,表示非零项还可以用链表的方式。
※方法3:链表结构存储非零项
分析:在这个链表里,每个节点包含两个信息,系数和指数i;用指针域把不同的项串起来。
要点:按指数大小有序存储❗
如:
P1:(9,12), (15,8), (3,2)
P2:(26,19), (-4,8), (-13,6),(82,0)
表示为:
相加过程:从头开始,比较两个多项式当前对应项的指数。
小归纳:多项式的问题
2.1.2线性表及顺序存储
由很多问题跟多项式问题存在共性,我们的目标是想管理一个有序的线性序列,把它归结为“线性表”问题。
线性表Linear List:由同类型数据元素构成有序序列的线性结构。
ADT线性表的抽象数据类型描述:
存储实现
线性表的顺序存储实现
利用数组的连续存储空间顺序存放线性表各元素。
定义
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+1
PtrL->Last+1
主要操作
初始化(建立空的顺序表)
List MakeEmpty()
{
List PtrL;
PtrL=(List)malloc(sizeof(struct LNode));
PtrL->Last=-1;
return PtrL;
}
2.查找
int Find(ElementType X,List PtrL)
{
int i=0;
while(i<=PtrL->Last&&X!=PtrL->Data[i])//循环条件:没找到且未溢出数组
{
i++;
}
if(i>PtrL->Last) return -1;
else return i;
}
3.插入(插到线性表的i位置上)i∈[1,n+1] (n是元素个数)
线性表长度为:
L.Last+1
PtrL->Last+1
//Last指向的是数组下标
这里P是存储下标位置(从0开始)
i=n+1时就插在线性表的最后
参考:(4条消息) 顺序存储结构的插入与删除_Uncertainty!!的博客-CSDN博客_顺序储存的插入与删除
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;
}
4.删除
这里P是存储下标位置(从0开始)
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;
}
线性表的链式存储
定义
不要求物理上相邻,把逻辑上相邻的两个元素通过链连接在一起。
创建:
typedef struct LNode* List;
struct LNode{
ElemnetType Data;
List Next;
};
struct LNode L;
List PtrL;
思考:在一个链表中,我们只知道链表的头,如何查找第i个元素?如何知道链表的长度?
这比顺序存储结构中要复杂的多。
主要操作及实现
求表长
int Length(List PtrL)//PtrL为头指针
{
List P=PtrL;
int j=0;
while(P)//当P不为空
{
P=P->Next;
j++;
}
return j;
}
//自己又想了一下
int Length(List PtrL)//PtrL为头指针
{
List P=PtrL;
int j=1;
while(P->Next)//当P不为空
{
P=P->Next;
j++;
}
return j;
}
2.查找
(1)按序号查找
List FindKth(int K,List PtrL)
{
List p=PtrL;
int i=1;
for(i=1;p!=NUll;i++)
{
if(i==K)
{
return p;
}
p=p->Next;
}
return NULL;
}
(2)按值查找
List Find(ElementType X,List PtrL)
{
List p=PtrL;
for(p=PtrL;p!=NULL;p=p->next)
{
if(p->Data==X)
{
return p;
}
}
return NULL;
}
3.插入(在第i-1个节点后插入一个值为X的新节点)
(1)先构造一个新节点,用s指向
(2)再找到链表的第i-1个节点,用p指向
(3)修改指针,插入节点
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)//参数错误,插入失败
{
printf("参数错");
return NULL;
}else{
s=(List)malloc(sizeof(struct LNode));
s->Data=X;
s->Next=p->Next;
p->Next=s;
return PtrL;//头结点不变
}
}
4.删除(删除链表的第i个位置上的节点)
思考:删除第i个节点需要找到第i-1个节点,然后接到第i+1个节点上去
(1)先找到链表的第i-1个节点,用p指向
(2)用s指针指向i节点
(3)p->next=s->next
(4) 释放i节点
List Delete(int i,List PtrL)
{
List p,s;
if(i==1)
{
s=PtrL;//s指向要删除的节点
if(PtrL!=NULL)
{
PtrL=PtrL->Next;
}else return NULL;
free(s);//释放要删除的节点
return PtrL;//返回新头指针
}
p=FindKth(i-1,PtrL);//只要删除的节点不是头结点就要找到它的前一个
if(p==NULL)
{
printf("第%d个节点不存在",i-1);
return NULL;
}else if(p->Next==NUll){//即要删除的节点不存在
printf("第%d个节点不存在",i);
return NULL;
} else{
s=p->Next;
p->Next=s->Next;
free(s);
return PtrL;//头指针不变
}
}
2.1.6广义表与多重链表
例子:二元多项式的表示
分析:
广义表定义
广义表(Generalized List):广义表是线性表的推广
广义表中出现的问题:一个域可能是不能分解的单元,也可能是一个指针。
处理:C语言提供了一种手段,联合Union
操作:
typedef struct GNode* GList;
struct GNode{
int Tag;//标志域:0表示是单元素,1表示节点是广义表
union{
ElementType Data;
GList SubList;
}URegion;
Glist Next;//指向后继节点
};
理解如图: