《数据结构》第二讲 线性结构
2.1线性表及其实现
2.1.1引子:多项式表示
例:一元多项式及其运算
一元多项式: f(x) = a0 + a1x + a2x^2 + ……+ a(n-1) x ^ (n-1) + an x^n
主要运算:多项式相加、相减、相乘等
如何表示多项式?
多项式的关键数据:多项式项数n,各项系数ai及指数i
方法1:顺序存储结构直接表示
数组各分量对应多项式各项:a[i]:项x^i的系数ai
例如:f(x) = 4*x^5 - 3*x^2 +1 表示成a[0] = 1,a[1] = 0,a[2] = -3,a[3] = 0,a[4] = 0 ,a[5] = 4
两个多项式相加:两个数组对应分量相加
问题:如何表示多项式x+3*x^2000?则至少需要大小为2001的数组,造成空间的巨大浪费。
方法2:顺序存储结构表示非零项
每个非零项涉及两个信息:系数ai,指数i,可以把多项式看成是一个(ai,i)二元组的集合
用结构数组表示:数组分量是由系数ai,指数i组成的结构,对应一个非零项。按指数大小有序储存
相加过程:从头开始,比较两个多项式当前对应项的指数,指数大的则输出,指数相同则对应系数相加后输出。
方法3:链表结构存储非零项
链表中每个结点存储多项式中的一个非零项,包括系数和指数两个数据域以及一个指针域。
typedef struct PolyNode* Polynomial;
struct PolyNode{
int coef; //系数
int expon; //指数
Polynomial link;
};
其加法运算的逻辑过程和方法2相同。
2.2.1线性表及顺序存储
什么是线性表
多项式表示问题的启示:1.同一个问题可以有不同的表示(存储)方法;2.有一类共性问题:有序线性序列的组织和管理
线性表(Linear List):由同类型数据元素构成有序序列的数据结构。表中元素个数称为线性表的长度;线性表没有元素时称为空表;表起始位置称表头,表结束位置称表尾。
线性表的顺序存储实现
利用数组的连续存储空间顺序存放线性表的各元素
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
主要操作的实现
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 && Ptrl->Data[i] != x)
i++;
if(i > Ptrl->last)
return -1; //如果没找到,返回-1
else
return i; //找到后返回的是存储位置
}
查找成功的平均次数为(n+1)/2,平均实现性能为O(n)
2.1.3顺序存储的插入和删除
3.插入(第i(1<=i<=n+1)个位置上插入一个值为x的新元素)
void insert(ElementType x,int i,List Ptrl)
{
int j;
if(Ptrl->last == MAXSIZE - 1) //表空间已满,不能插入
{
printf("表满\n");
return;
}
if(i < 1 || i > Ptrl->last + 2) //检查插入位置的合法性
{
printf("位置不合法\n");
return;
}
for(j = Ptrl->last;j >= i - 1; j--)
Ptrl->Data[j+1] = Ptrl->Data[j]; //将ai~an倒序向后移动
Ptrl->Data[j] = x; //将新元素插入
Ptrl->last++; //Last仍指向最后元素
return;
}
平均移动次数为n/2,平均时间性能为O(n)
4.删除(删除表的第i(1<=i<=n)个位置上的元素)
void delete(int i,List Ptrl)
{
int j;
if(i < 1 || i > Ptrl->last + 1) //检查空表及删除位置的合法性
{
printf("不存在第%d个元素",i);
return;
}
for(j = i;j < Ptrl->last;j++)
Ptrl->Data[j-1] = Ptrl->Data[j]; //将ai+1~an顺序向前移动
Ptrl->last--; //last仍指向最后元素
return;
}
平均移动次数(n-1)/2,平均时间性能为O(n)
2.1.4链式存储及查找
线性表的链式存储实现
不要求逻辑上相邻的两个元素物理上也相邻;通过“链”建立起数据元素之间的逻辑关系
插入、删除不需要移动数据元素,只需要修改“链”
typedef struct LNode* List;
struct LNode{
ElementType Data;
List Next;
};
struct LNode L;
List Ptrl;
主要操作的实现
1.求表长
int length(List Ptrl)
{
List p = Ptrl; //p指向表的第一个结点
int j = 0;
while(p){
p = p->Next;
j++; //当前p指向的是第j个结点
}
return j;
}
时间性能为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)
讨论2.1链式存储中findKth函数的另一种实现?
如果将链式存储中FindKth的函数实现(如下)做个修改:把函数最后的if语句判断条件改为判断p是否为NULL,即:
- if (p==NULL) return NULL;
- else return p;
或者说直接简化为:return p;
对于这样的修改,程序还正确吗?为什么?
- List *FindKth( int K, List *PtrL )
- { List *p = PtrL;
- int i = 1;
- while (p !=NULL && i < K ){
- p = p->Next;
- i++;
- }
- if(i == K)