线性表
线性表——顺序表的定义及其上的操作
线性表——链表的定义及其上的操作
线性结构
线性结构是一个数据元素的有序集
线性结构的特点
在数据元素的非空有限集中:
- 集合中必存在唯一的一个第一元素
- 集合中必存在唯一的一个最后元素
- 除最后元素之外,均有唯一的后继
- 除第一元素之外,均有唯一的前驱
线性表是一种最简单的线性结构
线性表
线性表的类型定义
定义:一个线性表是n个数据元素的有限序列
例如:英文字母表
特征:
- 元素个数n——表长度 n=0时为空表
- i = 2,3,……,n时,ai的直接前驱是ai-1,ai无直接前驱
- i = 1,2,3,……,n-1时,ai的直接后继是ai+1,an无直接后继
- 元素同构,同一个线性表中的数据元素具有相同的特性
线性表上的基本操作
数据结构的运算是定义在逻辑结构层次上的,而运算的具体实现是建立在存储结构上的。下面定义的线性表基本运算仅是逻辑结构层面,具体实现在线性表确定了存储结构之后。
- 线性表初始化
InitList( &L )
初始条件:表L不存在
操作结果:构造一个空的线性表L
- 销毁一个线性表
DestroyList( &L )
初始条件:线性表L 已存在。
操作结果:销毁线性表L。
- 判断一个线性表是否为空表
ListEmpty( L )
初始条件:线性表L已存在。
操作结果:若L为空表,则返回TRUE,否则 FALSE。
- 求线性表的长度
ListLength( L )
初始条件:线性表L已存在。
操作结果:返回L中所含元素个数。
- 求数据元素的前驱
PriorElem( L, cur_e, &pre_e )
初始条件:线性表L已存在。
操作结果:若cur_e是L的元素,但 不是第一个,则用pre_e 返回它的 前驱,否则操作失败,pre_e无定义。
- 求数据元素的后继
NextElem( L, cur_e, &next_e )
初始条件:线性表L已存在。
操作结果:若cur_e是L的元素, 但不是最后一个,则用next_e 返回它的后继,否则操作失败, next_e无定义。
- 求线性表中某个数据元素
GetElem( L, i, &e )
初始条件:线性表L已存在, 且1≤i≤ListLength(L)。
操作结果:用e 返回L中第i个元素的值。
- 定位函数
★LocateElem( L, e, compare( ) )
初始条件:线性表L已存在,e为给定值,compare( )是元素判定函数。
操作结果:返回L中第1个与e满足关系 compare( )的元素的位序。若这样的元素 不存在,则返回值为0。
- 遍历线性表
ListTraverse(L, visit( ))
初始条件:线性表L已存在,Visit() 为某个访问函数。
操作结果:依次对L的每个元素调用 函数visit( )。一旦visit( )失败,则操 作失败。
- 线性表置空
ClearList( &L )
初始条件:线性表L已存在。
操作结果:将L重置为空表
- 插入数据元素
ListInsert( &L, i, e )
初始条件:线性表L已存在,插入位置正 确(1≤i≤ListLength(L)+1 )。
操作结果:在L的第i个元素之前插入新 的元素e,这样使原序号为i , i+1, ... , n 的数据元素的序号变为i+1,i+2, ... , n+1,插入后表长=原表长+1。
- 删除数据元素
ListDelete(&L, i, &e)
初始条件:线性表L已存在且非空, 1≤i≤ListLength(L)
操作结果:删除L的第i个元素,并用 e返回其值,删除后使序号为i+1, i+2,..., n 的元素变为序号为i, i+1,...,n-1,新表长=原表长-1。
线性表的顺序表示和实现
线性表顺序表示
定义:用一组地址连续的存储单元存放一个线性表

特点:
实现了逻辑上的相邻——物理地址相邻
实现随机存取
实现线性表——可以用一维数组实现
线性表实现
- 线性表的顺序结构定义
#define LIST_INIT_SIZE 100 // 线性表存储空间的初始分配量
#define LISTINCREMENT 10 // 线性表存储空间的分配增量
typedef struct {
ElemType *elem; // 存储空间基址
int length; // 当前长度
int listsize; // 当前分配的存储容量
// (以sizeof(ElemType)为单位)
}SqList; // 俗称顺序表
- 线性表基本操作在顺序表中的实现
初始化线性表:
Status InitList_Sq( SqList&L ){
// 构造一个空的线性表
L.elem = (ElemType*) malloc (LIST_ INIT_SIZE* sizeof (ElemType));
//或L.elem=new ElemType[LIST_INIT_SIZE];
if(!L.elem) exit(OVERFLOW);
L.length = 0;
L.listsize = LIST_INIT_SIZE
return OK;
}// InitList_Sq
销毁线性表:
voidDestroyList(SqList&L) {
if(L.elem)
delete[] L.elem;//释放存储空间
}
清空线性表:
void ClearList(SqList &L) {
L.length=0; //将线性表的长度置为0
}
求线性表L的长度:
int GetLength(SqList L) {
return (L.length);
}
判断线性表L是否为空:
int IsEmpty(SqList L) {
if (L.length==0)
return 1;
else
return 0;
}
取值(根据位置i获取相应位置数据元素的内容):
Status GetElem(SqListL,int i,ElemType& e) {
if(i<1||i>L.length)
return ERROR; //判断i值是否合理,若不合理,返回ERROR
e=L.elem[i-1];//第i-1的单元存储着第i个数据
return OK;
}
查找元素在线性表里是否存在:
int LocateElem_Sq(SqList L, ElemType e, Status(*compare)(ElemType, ElemType)) {
//在顺序表中查询第一个满足判定条件的数据元素
//若存在,则返回它的位序,否则返回0
i = 1; //i 的初值为第1 元素的位序
p = L.elem; //p 的初值为第1 元素的存储位置
while(i <= L.length && !(*compare)(*p++, e))
++i;
if(i <= L.length)
return i;
else
return 0;
}// LocateElem_Sq
插入元素:
Status ListInsert_Sq(sqlist &L,int i,ElemType e){
if (i<1||i>L.length+1)
return ERROR;
if (L.length>=L.listsize){
newbase =(ElemType*)realloc(L.elem, (L.listsize+LISTINCREMENT)*sizeof(ElemType));
if (!newbase)
exit (overflow);
L.elem =newbase;
L.listsize+=LISTINCREMENT;
}
q=&(L.elem[i-1]); // q指示插入位置
for (p=&(L.elem[L.length-1]);p>=q;--p)
*(p+1)=*p;
// 插入位置及之后的元素右移
*q=e; // 插入e
++L.length; // 表长增1
return OK;
}
删除元素:
Status listdelete_sq(sqlist &L,int i,ElemType &e){
if ((i<1)||(i>L.length))
return ERROR;
p=&(L.elem[i-1]); // p 为被删除元素的位置
e=*p; // 被删除元素的值赋给e
q=L.elem+L.length-1; // 表尾元素的位置
for (++p;p<=q;++p)
*(p-1)=*p;// 被删除元素之后的元素左移
--L.length; // 表长减1
return OK;
}
顺序存储结构的优缺点
优点:
逻辑相邻 物理相邻
可随机存取任一元素
存储空间使用紧凑
缺点:
插入、删除操作需要移动大量的元素
预先分配的空间按最大空间分配 利用不充分
表容量难以扩充
线性表的链式表示和实现
用一组任意的存储单元存储线性表的数据元素
利用指针实现了永不相邻的存储单元存放逻辑上相邻的元素
每个数据元素除了存储本身的信息外还需要存储其直接后继的信息
结点:
数据域:元素本身信息
指针域:指示直接后继的存储位置

线性链表的定义(单链表)
定义:结点中只含一个指针域的链表加线性单链表
Typedef struct LNode {
ElemType data; // 数据域
struct LNode *next; // 指针域
}LNode, *LinkList;
生成一个LNode型新结点:
p=(Linklist)malloc(sizeof(LNode));或p=new LNode;
系统回收p结点:free(p);或delete p;
单链表的操作
初始化(构造一个空表):
Status InitList_L(LinkList &L){
L=new LNode;
L->next=NULL;
return OK;
}
销毁:
Status DestroyList_L(LinkList &L){
LinkList p;
while(L) {
p=L;
L=L->next;
delete p;
}
return OK;
}
清空:
Status ClearList(LinkList &L){
// 将L重置为空表
LinkList p,q;
p=L->next; //p指向第一个结点
while(p) //没到表尾
{
q=p->next;
delete p;
p=q;
}
L->next=NULL; //头结点指针域为空
return OK;
}
求表长:
int ListLength_L(LinkList L){
//返回L中数据元素个数
LinkList p;
p=L->next; //p指向第一个结点
i=0;
while(p){
//遍历单链表,统计结点数
i++;
p=p->next;
}
return i;
}
判断表是否为空:
int ListEmpty(LinkList L){
//若L为空表,则返回1,否则返回0
if(L->next) //非空
return 0;
else
return 1;
}
查找(根据指定数据获取数据所在的位置):
LNode *LocateELem_L (LinkList L,Elemtype e) {
//返回L中值为e的数据元素的地址,查找失败返回NULL
p=L->next;
while(p &&p->data!=e)
p=p->next;
return p;
}
int LocateELem_L (LinkList L,Elemtype e) {
//返回L中值为e的数据元素的位置序号,查找失败返回0
p=L->next;
j=1;
while(p &&p->data!=e)
{
p=p->next;
j++;
}
if(p)
return j;
else
return 0;
}
查找第i 个数据元素:
Status GetElem_L(LinkList L, int i, ElemType &e) {
// L是带头结点的链表的头指针,以e 返回第i 个元素
p = L->next;
j = 1; // p指向第一个结点,j为计数器
while (p && j<i)
{
p = p->next;
++j;
} //顺指针向后查找,直到p 指向第i 个元素 //或p 为空
if( !p || j>i )
return ERROR; // 第i 个元素不存在
e = p->data; // 取得第i 个元素
return OK
}// GetElem_L
插入元素:
Status ListInsert_L(LinkList &L, int i, ElemType e) {
// L 为带头结点的单链表的头指针
// 在链表中第i个结点之前插入新的元素e
p = L;
j = 0;
while (p && j < i-1) {
p = p->next;
++j;
} // 注意表达技巧
if (!p || j > i-1)
return ERROR; // 两种情况的具体所指
s = (LinkList) malloc ( sizeof (LNode));
s->data = e;
s->next = p->next;
p->next = s; // 插入
return OK
}// LinstInsert_L
删除元素:
Status ListDelete_L(LinkList &L, int i, ElemType &e) {
// 删除以L 为头指针(带头结点)的单链表中第i 个结点
p = L;
j = 0;
while (p->next && j < i-1)
{
p = p->next;
++j;
} // 寻找第i 个结点,并令p 指向其前趋
if (!(p->next) || j > i-1)
return ERROR;
// 删除位置不合理
q = p->next;
p->next = q->next; // 删除并释放结点
e = q->data;
free(q);
return OK;
} // ListDelete_L
动态建立单链表算法
逆位序输入n个数据元素的值,建立带头节点的单链表。
操作步骤:
- 建立一个空表
- 输入数据元素an,建立结点并插入
- 输入数据元素an-1,建立结点并插入
- 以此类推,直到输入a1为止
void CreateList_L(LinkList &L, int n) {
// 逆序输入n 个数据元素,建立带头结点的单链表
L = (LinkList)malloc(sizeof (LNode));
L->next = NULL;// 先建立一个带头结点的单链表
for(i = n; i > 0; --i)
{
p = (LinkList)malloc(sizeof (LNode));
scanf(&p->data); // 输入元素值
p->next = L->next;
L->next = p; // 插入
}
}// CreateList_L
单链表特点
它是一种动态的结构,整个存储空间为多个链表共用
不需要预先分配空间
指针占用额外的存储空间
不能随机存取 查找速度慢
重新定义带头结点的单链表类型
typedef struct LNode { // 结点类型
ElemType data;
struct LNode *next;
}*Link, *Position;
typedef struct{ // 链表类型
Link head, tail;// 分别指向头结点和
//最后一个结点的指针
int len; // 指示链表长度
}LinkList;
循环链表
循环链表是表中最后一个结点的指针指向头结点,使链表构成环状
特点:
从表中任一节点出发均可找到表中其他节点,提高查找效率
操作与单链表基本一致 循环条件不同:
- 单链表p 或 p->next == NULL
- 循环链表p 或 p->next == H
双向链表
单链表具有单向性的缺点
双向链表结点定义:

typedef structDuLNode {
ElemType data;// 数据域
struct DuLNode *prior; // 指向前驱的指针域
struct DuLNode *next; //指向后继的指针域
}DuLNode, *DuLinkList;
双向循环链表
特点:
- 查询与单链表相同
- 插入和删除时需要同时修改两个方向上的指针
插入操作:
Status ListInsert_Dul(DuLinkList &L,int i,ElemType e){
//在带头结点的双向循环链表L中第i个位置之前插入元素e,
//i的合法值为1≤i≤表长+1
if (!(p=GetElemP_Dul(L,i))) //在L中确定插入位置指针P
return ERROR; //i等于表长+1时,p指向头结点,i大于表长+1时,p=NULL
if (!(s=(DuLinkList)malloc(sizeof(DuLNode))))
return ERROR;
s->data=e;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
return ok;
}
删除操作:
Status ListDelete_Dul(DuLinkList &L,int i,ElemType &e){
//删除带头结点的双向循环链表L的第i个元素
//i的合法值为1≤i≤表长
if (!(p=GetElemP_Dul(L,i)))
//在L中确定第i个元素的位置指针
return ERROR;
//p =NULL,即第i个元素不存在
e=p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
return OK;
}

线性表的应用——一元多项式的表示及相加
一元多项式

在计算机中,可以用一个线性表来表示:
P = (p0, p1, …,pn)
但是对于形如 S(x) = 1 + 3x10000–2x20000 的多项式,上述表示方法比较浪费空间

单链表的结点定义
typedef struct Lnode{
int coef,exp;
struct Lnode *next;
}Lnode,*LinkList;
一元多项式相加


void add_poly(LinkList &pa,LinkList pb) {
//一元多项式的表示及相加
LinkList p,q,u,pre;
int x;
p=pa->next;
q=pb->next;
pre=pa;//pre总是指向待进入链表的节点的前驱
while((p!=NULL) && ((q!=NULL)) {//结束条件
if(p->exp<q->exp){//p指数小于q指数
pre=p;
p=p->next;
}
else if(p->exp==q->exp){//相等
x=p->coef+q->coef; //系数相加
if(x!=0){
p->coef=x;
pre=p;
} //系数非0
else {//为0
pre->next=p->next;
free(p);
} // 删除q
p=pre->next;
u=q;
q=q->next;
free(u);
}
else{//p指数大于q指数
u=q->next;
q->next=p;
pre->next=q;
pre=q;
q=u;
}
if(q!=NULL)
pre->next=q;//第二条链表未结束
free(pb);
}
}
5032

被折叠的 条评论
为什么被折叠?



