线性表(Linear List)是最常用且最简单的一种数据结构。一个线性表是n个数据元素的有限序列,至于一个数据元素的具体含义,在不同情况下有不同的具体含义,但同一个线性表中的元素必定有相同的特性。
线性表是一个线性结构。
线性表的存储结构主要有两种:顺序存储结构--顺序表,链式存储结构--链表。
一.顺序表
用一组地址连续的存储单元依次存储线性表的元素,可以用数组来描述。
typedef int datatype;
#define MAXSIZE 1024;
typedef struct{
datatype data[MAXSIZE];
int last;
}sequenlist;
下标从0开始,到MAXSIZE-1结束。随机存储结构,特点是以元素在计算机内物理位置上的紧邻来表示线性表中数据元素之间的逻辑关系。
//建立顺序表
sequenlist *L;
sequenlist *Creat(){
int i=0; char ch;
L=(sequenlist *)malloc(sizeof(sequenlist)); //分配顺序表空间
L->last=-1;
while((ch=getche())!='#) //以输入'#'结束
{
L->data[i++]=ch;
L->last++;
}
return L; }
二,链表
1.单链表
每个结点包含数据域和指向下一个结点地址的指针域。每个结点只包含一个指针域。
每个结点的存储地址放在其前驱结点的next域中,而开始结点无前趋,故应设头指针head指向开始结点。同时,终端结点的指针域为空,即NULL.
单链表由头指针唯一确定,因此可以用头指针的名字来命名。
typedef int datatype;
typedef struct node{
datatype data;
struct node *next;
}linklist;
linklist *head,*p;
单链表的建立:
1)头插法建表是从一个空表开始,重复读入数据,生成新结点,将读入数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头上,直至读入结束标志为止。
linklist *CreatList(){
char ch;
linklist *head,*s;
head=NULL; //链表开始为空
while((ch=getche())!='#'){ //读入结点值,以‘#’结束
s=(linklist *)malloc(sizeof(linklist)); //生成新结点
s->data=ch;
s->next=head;
head=s;}
return head;}
头插法建表虽然简单,但生成的链表中结点的次序和输入顺序相反。
2)尾插法建表是将新结点插到当前链表的表尾上,为此需增加一个尾指针r,使其始终指向当前链表尾结点。
linklist *Creat(){
char ch;
linklist *head,*s,*r;
head=NULL; //链表初值为空
r=NULL; //尾指针初值为空
while(ch=getche()!='#'){
s=(linklist *)malloc(sizeof(linklist);
s->data=ch;
if(head==NULL) head=s;
else r->next=s;
r=s;}
if(r!=NULL) r->next=NULL;
return head;}
上述算法必须对第一个位置上的插入操作做特殊处理,如果在链表开始结点之前附加一个结点,并称它为头结点,那么上述算法可简化。
linklist *Creat(){
char ch;
linklist *head,*s,*r;
head=(linklist *)malloc(sizeof(linklist);
r=head;
while((ch=getche()!='#'){
s=(linklist *)malloc(sizeof(linklist);
s->data=ch;
r->next=s;
r=s;}
r->next=NULL;
retrurn head;}
此时头结点的数据域不存储信息,可利用该数据域来存放表的长度等附加信息。
例:将两个递增单链表合并为一个递增单链表,要求不得另外开辟新的空间。
linklist *Union(linklist *la,linklist *lb){
linklist *p,*q,*r,*s;
p=la->next; q=lb->next;
r=la;
while((p!=NULL)&&(q!=NULL)){
if(p->data>q->data){
u=q->next; r->next=q;
r=q; q->next=p; q=u;}
else{
r=p; p=p->next;
}
if(q!=NULL) r->next=q;
return la;
}
2,循环链表
表中最后一个结点的指针域指向头结点,整个链表形成一个环。从表中任意结点出发均可找到表中其他结点。
循环链表的运算和单链表基本一致,差别仅在算法中对最后一个结点的循环处理上。
实际中多采用尾指针表示的单循环链表,找头和找尾都比较容易。
判断是否遍历结束的标志:
p->next!=head;
3,双向链表
每个结点增加一个指向其直接前趋的指针。
typedef struct node{
datatype data;
struct node *prir,*next;
}dlinklist;
dlinklist *head;
双链表一般也是由头指针唯一确定。
最后,说一个链表的应用实例---多项式的表示及运算
我们采用循环链表来存储一元多项式,并实现两个多项式的相加。链表中每个结点分为系数,指数和指针三个域。
typedef struct pnode{
float coef; //系数
int exp; //指数
struct pnode *next;
}polynode;
采用把一个多项式归入另一个多项式的方法。
设p和q分别指向多项式A和B中的某一结点,比较结点中的指数项,若有p->exp>q->exp,此时q结点应为和多项式中的一项,q结点应插在p结点之前。若有p->exp==q->exp,系数相加,若和不为0,修改p结点的系数域;否则修改p结点。若有p->exp<q->exp,则p为和多项式中的一项。
#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
typedef struct pnode{
float coef;
int exp;
}polynode;
//建立多项式循环链表
polynode *Creat(){
float coef; int exp;
polynode *head,*s,*r;
head=(polynode *)malloc(sizeof(polynode));
head->coef=0;
head->exp=-1;
r=head;
printf("\n请输入各项的系数和指数(如2.5 3),均为0时结束输入\n");
while(1)
{
printf("coef exp:");
scanf("%f %d",&coef,&exp);
if(coef!=0){
s=(polynode *)malloc(sizeof(polynode));
s->coef=coef; s->exp=exp;
r->next=s;
r=s;
}
else break;
}
r->next=head;
return head;
}//Creat
//多项式相加运算A=A+B
polynode *PolyAdd(polynode *pa,polynode *pb){
polynode *p,*q,*r,*s;
float x;
p=pa->next; q=pb->next;
s=pa;
while((p!=pa)&&(q!=pb))
{
if(p->exp<q->exp)
{
s=p; p=p->next;
}
if(p->exp>q->exp)
{
r=q->next; q->next=p;
s->next=q; s=q; q=r;
}
else
{
x=p->coef+q->coef;
if(x!=0)
{
p->coef=x;
s=p;
}
else
{
s->next=p->next;
free(p);
}
p=s->next; r=q;
q=q->next; free(r);
}
}
if(q!=pb) //将B中剩余结点链入多项式A中
{
r=q;
while(r->next!=pb)
r=r->next;
s->next=q;
r->next=pa;
}
return pa;
}//PloyAdd
参考书籍:数据结构与算法分析 --西安电子科技大学出版社