线性表的逻辑特性
只有一个表头元素,只有一个表尾元素,表头元素没有前驱,表尾元素没有后继,除表头和表尾元素之外,其他元素只有一个直接前驱,也只有一个直接后继。
线性表的存储结构
顺序存储结构(称为顺序表)和链式存储结构(称为链表)
顺序表——1)随机访问特性;2)要求占用连续的存储空间。
链表——1)不支持随机访问;2)链表中结点的存储空间利用率较顺序表稍低一些;3)支持存储空间的动态分配。
顺序表做插入(或删除操作)的时候要移动多个元素,而在料表中进行插入操作无需移动元素。
链表的五种形式
- 单链表
- 双链表
- 循环单链表
- 循环双链表
- 静态链表
静态链表来自于一个结构体数组,数组中的每个结点含有两个分量:一个是数据元素分量data;另一个是指针分量,知识了当前结点的直接后继结点在数组中的位置。
顺序表和链表的比较
- 基于空间的比较:1)存储分配的方式: 顺序表的存储空间是一次性分配的,链表的存储空间是多次分配的。2)存储密度(=结点值域所占的存储量/ 结点结构所占的存储总量):顺序表的存储密度=1,链表的存储密度<1
- 基于时间的比较:1)存取方式:顺序表可以随机存取,链表只能顺序存取。2)插入/删除时移动元素的个数:顺序表平均需要移动近一半元素,而链表不需要移动元素,只需要修改指针。
/*顺序表的结构体定义*/
typedef struct
{
int data[maxSize]; //存放顺序表元素的数组
int length; //存放顺序表的长度
}Sqlist;
/*考试中常用*/
int A[maxSize];
int n;
/*单链表的结点定义*/
typedef struct LNode
{
int data;
struct LNode *next;
}LNode;
/*双链表的结点定义*/
typedef struct DLNode
{
int data;
struct DLNode *prior;
struct DLNode *next;
}DLNode;
LNode *A=(LNode*)malloc(sizeof(LNode));
/*按元素值查找算法*/
int findElem(Sqlist L,int e)
{
int i;
for (i=0;i<L.length;i++)
if (e==L.data[i])
{
return i; //若找到,则返回下标值
break;
}
return -1; //没找到,则返回-1作为失败标记
}
/*插入数据元素的算法*/
int insertElem(Sqlist &L,int p, int e) //在顺序表的第P个位置上插入新的元素e
{
int i;
if (p<0||p>L.length-1||L.length==maxSize) //位置错误或者表长已经达到顺序表的最大允许值
return 0; //此时插入不成功,返回值为0
for (i=L.length-1;i>=p;i--) //从后往前
L.data[i+1]=L.data[i]; //逐个将元素往后移动一个位置
L.data[p]=e; //将e放在插入位置p上
++(L.length); //表长自增1
return 1; //插入成功,返回1
}
/*删除顺序表L中下标为p的元素*/
int deleteElem(Sqlist &L,int p,int &e)
{
int i;
if (p<0||p>L.length-1)
return 0;
e=L.data[p];
for (i=p;i<L.length-1;i++)
L.data[i]=L.data[i+1];
--(L.length);
renturn 1;
}
/*初始化顺序表的算法*/
void initList(Sqlist &L)
{
L.length=0;
}
/*求指定位置元素的算法*/
int getElem(Sqlist L;int p;int &e)
{
if (p<0||p>L.length-1)
return 0;
e=L.data[p];
return 1;
}
/*尾插法归并*/
void merge(LNode *A,LNode *B,LNode *&C)
{
LNode *p=A->next; //p来跟踪A的最小结点值
LNode *q=B->next; //q来跟踪B的最小结点值
LNode *r; //r始终指向C的终端结点
C=A; //用A的头结点来做C的头结点
C->next=NULL; //将C置空
free(B); //B的头结点已无用,释放空间
r=C; //r此时指向C的头结点,因为C此时为空,其头结点也是终端结点
while (p!=NULL&&q!=NULL)
{
if (p->data<=q->data)
{
r->next=p;
p=p->next;
}
else
if (q->data<=p->data)
{
r->next=q;
q=q->next;
}
}
r->next=NULL;
if (p!=NULL) r->next=p;
if (q!=NULL) r->next=q;
}
/*尾插法建立单链表*/
void createlistR(LNode *&C,int a[],int n) //要改变的变量用引用型
{
LNode *s,*r; //s用来指向新申请的结点,r始终指向C的终端结点
int i;
C=(LNode *)malloc(sizeof(LNode)); //申请C的头结点空间
C->next=NULL;
r=C; //r指向头结点,因为此时头结点就是终端结点
for (i=0;i<n;i++) //循环申请n个结点来接收数组a中的元素
{
s=(LNode *)malloc(sizeof(LNode)); //s指向新申请的结点
s->data=a[i]; //用新申请的结点接收a中的一个元素
r->next=s; //用r来接纳新结点
r=r->next; //r指向新的终端结点,以便接纳下一个到来的结点
}
r->next=NULL; //数组a中的元素都已经装入链表C中,C的终端结点指针置为NULL,C建立成功
}
/*头插法建立单链表*/
void createlistF(LNode *&C,int a[],int n) //要改变的变量用引用型
{
LNode *s; //s用来指向新申请的结点
int i;
C=(LNode *)malloc(sizeof(LNode)); //申请C的头结点空间
C->next=NULL;
for (i=0;i<n;i++) //循环申请n个结点来接收数组a中的元素
{
s=(LNode *)malloc(sizeof(LNode)); //s指向新申请的结点
s->data=a[i]; //用新申请的结点接收a中的一个元素
/*以下两歩是头插法关键步骤*/
s->next=C->next;
c->next=s;
}
r->next=NULL; //数组a中的元素都已经装入链表C中,C的终端结点指针置为NULL,C建立成功
}
/*头插法归并(逆原序列顺序)*/
void merge(LNode *A,LNode *B,LNode *&C)
{
LNode *p=A->next; //p来跟踪A的最小结点值
LNode *q=B->next; //q来跟踪B的最小结点值
LNode *s;
C=A; //用A的头结点来做C的头结点
C->next=NULL; //将C置空
free(B); //B的头结点已无用,释放空间
r=C; //r此时指向C的头结点,因为C此时为空,其头结点也是终端结点
while (p!=NULL&&q!=NULL)
{
/*下边的if else语句体现了链表的头插法*/
if (p->data<=q->data)
{
s=p;
p=p->next;
s->next=C->next;
C->next=s;
}
else
if (q->data<=p->data)
{
s=q;
q=q->next;
s->next=C->next;
C->next=s;
}
}
r->next=NULL;
/*头插法必须将剩余元素逐个插入C的头部才能得到最终的递减序列*/
if (p!=NULL)
{
s=p;
p=p->next;
s->next=C->next;
C->next=s;
}
if (q!=NULL)
{
s=q;
q=q->next;
s->next=C->next;
C->next=s;
}
}
int findAndDelete(LNode *C,int x)
{
LNode *p,*q;
p=C;
/*查找部分开始*/
while(p->next!=NULL)
{
if (p->next->data==x)
break;
p=p->next;
}
/*查找部分结束*/
if(p->next==NULL)
return 0;
else
{
/*删除部分开始*/
q=p->next;
p->next==p->next->next;
free(q);
/*删除部分结束*/
return 1;
}
}
要删除一个结点必须知道其前驱结点的位置
双链表的操作
/*尾插法建立双链表*/
void createDlistR(DLNode *&L,int a[],int n) //要改变的变量用引用型
{
DLNode *s,*r; //s用来指向新申请的结点,r始终指向L的终端结点
int i;
L=(DLNode *)malloc(sizeof(DLNode)); //申请L的头结点空间
L->prior=NULL;
L->next=NULL;
r=L; //r指向头结点,因为此时头结点就是终端结点
for (i=0;i<n;i++) //循环申请n个结点来接收数组a中的元素
{
s=(DLNode *)malloc(sizeof(DLNode)); //s指向新申请的结点
s->data=a[i]; //用新申请的结点接收a中的一个元素
r->next=s; //用r来接纳新结点
s->prior=r;
r=r->next; //r指向新的终端结点,以便接纳下一个到来的结点
}
r->next=NULL; //数组a中的元素都已经装入双链表L中,L的终端结点指针置为NULL,L建立成功
}
/*在双链表中查找第一个值为x的结点*/
DLNode* findNode(DLNode *C,int x)
{
DLNode *p=C->next;
while (p!=NULL)
{
if (p->data==x)
break;
p=p->next;
}
return p; //如果找到,则p中内容是结点地址;
} //如果没找到,则p中内容是NULL。
/*在双链表中插入结点的语句(p所指的结点之后插入一个结点s)*/
s->next=p->next;
s->prior=p;
p->next=s;
s->next->prior=s; //加入p指向最后一个结点,则本行可删去
/*在双链表中删除结点的算法(要删除p结点的后继结点)*/
q=p->next;
p->next=q->next;
q->next->prior=p;
free(q);