赞
踩
线性表是具有相同数据类型的 n(n>=0)个数据元素的有限序列,其中 n 为表长,当 n=0 时线性表是一个空表。若用 L 命名线性表,其一般表示为:
L = (a1,a2,...,ai,ai+1,...,an)
式中,a1 是唯一的“第一个”数据元素,又称表头元素;an 是唯一的“最后一个”数据元素,又称表尾元素。除第一个元素外,每个元素有且只有一个直接前驱。除最后一个元素外,每个元素有且只有一个直接后继。
线性表具有如下特点:
注意:线性表是一种逻辑结构,表示元素之间一对一的相邻关系。顺序表和链表是指存储结构,两者属于不同层次的概念,不要将其混淆。
线性表的顺序存储又称顺序表。是用一组地址连续的存储单元依次存储线性表中的数据元素,使得逻辑上相邻的两个元素在物理位置上也相邻。顺序表中元素的逻辑顺序与其物理顺序相同。
注意:线性表中元素的次序是从 1 开始的,数组中元素的下标是从 0 开始的
线性表的顺序存储类型描述如下:
#define MaxSize 50 //定义线性表的最大长度
typedef struct{
ElemType date[MaxSize]; //顺序表的元素,ElemType为元素类型
int length; //顺序表的当前长度
}SqList; //顺序表的类型长度
一维数组可以是静态分配,也可以是动态分配。在静态分配时,由于数组的大小和空间事先已经固定,一旦空间占满,再加入数据便会产生溢出,进而导致程序奔溃。
动态分配时,存储数组的空间是在程序执行过程中通过动态存储分配语句分配的,一旦数据空间占满,就会另外开辟一块更大的存储空间,用以替换原来的存储空间,从而达到扩充存储数据空间的目的,不需要一次性划分所有空间。
动态分配顺序表存储空间类型描述如下:
#define InitSize 100 //表长度的初始定义
typedef struct{
ElemType *data; //指示动态分配数据的指针
int MaxSize,length; //数组的最大容量和当前个数
} SqlList; //动态分配数组顺序表的类型定义
C 的初始动态分配语句为:
L.data = (ElemType *)malloc(sizeof(ElemType)*InitSize);
注意:动态分配并不是链式存储,它也属于顺序存储结构,物理结构没有变化,依然是随机存取方式,只是分配的空间大小可以在运行时决定。
顺序表具有的特点如下所示:
(1)插入操作:指定位置插入数据元素
(2)删除操作:指定位置删除数据元素
(3)按值查找(顺序查找)
下面通过代码简单介绍顺序表的初始化、插入指定位置操作、删除指定位置操作以及查询操作,示例代码如下:
#include<stdio.h> #include<stdlib.h> #define OK 1; #define ERROR 0; #define LIST_INIT_SIZE 100 #define LISTINCREMENT 10 typedef int Status; typedef int ElemType; typedef struct { ElemType *elem; int length; int listsize; }SqList; Status initList(SqList &L) { L.elem=(ElemType *)malloc(LIST_INIT_SIZE * sizeof(ElemType)); if(!L.elem) { exit(0); } L.length=0; L.listsize=LISTINCREMENT; return OK; } Status createList(SqList &L,int n) { ElemType *newbase; int i; if(n<0) { return ERROR; } if(n>L.listsize) { newbase = (ElemType*)realloc(L.elem,(L.listsize + n)*sizeof(ElemType)); if(!newbase) { exit(0); } L.elem = newbase; L.listsize += n; } L.length = n; for(i=0;i<n;i++) { scanf("%d",&(L.elem[i])); } return OK; } Status showList(SqList &L) { int i; if(L.length<=0) { return ERROR; } for(i=0;i<L.length;i++) { printf("%d\t",L.elem[i]); } printf("\n"); return OK; } Status destroyList(SqList &L) { if(L.elem) { free(L.elem); } return OK; } Status deleteList(SqList &L,int i,ElemType &e) { ElemType *p,*q; if((i<1)||(i>L.length)) { return ERROR; } p=&(L.elem[i-1]); e=*p; q=L.elem+L.length-1; for(++p;p<=q;++p) *(p-1)=*p; --L.length; return OK; } Status insertList(SqList &L,int i,ElemType e) { ElemType *p,*q,*newbase; 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(0); } L.elem=newbase; L.listsize+=LISTINCREMENT; } q=&(L.elem[i-1]); for(p=&(L.elem[L.length-1]);p>=q;--p) *(p+1)=*p; *q=e; ++L.length; return OK; } int main() { SqList L; printf("申请顺序表当前长度以及存储空间初始分配量:\n"); initList(L); printf("%d\t%d\n",L.length,L.listsize); printf("顺序表中输入五个数据:\n"); createList(L,5); printf("输出顺序表中全部元素:\n"); showList(L); printf("顺序表中删除三号位置上的数据:\n"); int e; deleteList(L,3,e); showList(L); printf("被删除的元素e:\ne=%d\n",e); printf("顺序表二号位置插入一个数据10:\n"); insertList(L,2,10); showList(L); printf("初始化顺序表:\n"); destroyList(L); printf("\n"); return 0; }
运行结果图如下所示:
示例代码:结构体数组创建学生成绩信息及其指针操作代码如下所示:
#include<stdio.h> #include<string.h> #define max 3 struct student { char name[5]; char sex[5]; int age; char classes[10]; int chinese; int math; }; struct student stu[max]; void input() { printf("输入学生的相关信息:\n"); for(int i=0;i<max;i++) { printf("输入第%d个学生的相关信息\n",i+1); printf("学生姓名:\t"); scanf("%s",&stu[i].name); printf("学生性别:\t"); scanf("%s",&stu[i].sex); printf("学生年龄:\t"); scanf("%d",&stu[i].age); printf("学生班级:\t"); scanf("%s",&stu[i].classes); printf("语文成绩:\t"); scanf("%d",&stu[i].chinese); printf("数学成绩:\t"); scanf("%d",&stu[i].math); } } void show() { printf("\n\n输出学生的相关信息:\n"); printf("姓名\t性别\t年龄\t班级\t语文\t数学\n"); for(int i=0;i<max;i++) { printf("%s\t%s\t%d\t%s\t%d\t%d\n",stu[i].name,stu[i].sex,stu[i].age,stu[i].classes,stu[i].chinese,stu[i].math); } } void find() { char name[5]; char *p; p=name; printf("\n\n输入你要查询的学生姓名:\t"); scanf("%s",p); for(int i=0;i<max;i++) { if(strcmp(p,stu[i].name)==0) { printf("姓名\t性别\t年龄\t班级\t语文\t数学\n"); printf("%s\t%s\t%d\t%s\t%d\t%d\n",stu[i].name,stu[i].sex,stu[i].age,stu[i].classes,stu[i].chinese,stu[i].math); break; } else { printf("没有你要查询的学生信息!\n"); break; } } } int main() { input(); show(); find(); return 0; }
运行结果如下图所示:
线性表的链式存储又称单链表,是指通过一组任意的存储单元来存储线性表中的数据元素。
单链表在创建过程中,除了要存放自身的信息外,还需要存放一个指向其后继的指针。单链表结果示意图如下所示:
其中, data 为单链表的数据域,存放数据元素;next 为指针域,指向其后继结点的指针。
单链表的结点类型描述如下:
typedef struct LNode{ //定义单链表结点类型
ElemType data; //数据域
struct LNode *next; //指针域
}LNode,*LinkList;
单链表可以解决顺序表需要大量存储单元的缺点,但单链表附加指针域,也存在浪费存储空间的缺点。单链表的存储空间离散地分布在存储空间中,单链表是非随机存取地存储结构。在使用单链表查找数据元素时,不能直接找到某个特定地结点,需要从表头开始遍历,依次比较查找。
为了操作上地遍历,在单链表的第一个结点之前附加一个结点,称为头结点。头结点的数据域可以不设任何信息,也可以用来记录表长等信息。头结点结构示意图如下:
头指针和头结点的区分:不管带不带头结点,头指针始终指向链表的第一个结点,而头结点是带头结点的链表的第一个结点,结点内通常不存储信息。
引入头结点后,可以带来两个优点:
(1)头插法创建单链表
void HeadCreatList(List *L) //头插法建立链表
{
List *s; //不用像尾插法一样生成一个终端节点。
L->next = NULL;
for (int i = 0; i < 10; i++) {
s = (struct List*) malloc(sizeof(struct List));//s指向新申请的节点
s->data = i;//用新节点的数据域来接受i
s->next = L->next; //将L指向的地址赋值给S;//头插法与尾插法的不同之处主要在此,
//s所指的新节点的指针域next指向L中的开始节点
L->next = s; //头指针的指针域next指向s节点,使得s成为开始节点。
}
}
(2)尾插法创建单链表
void TailCreatList(List *L) //尾插法建立链表
{
List *s, *r;//s用来指向新生成的节点。r始终指向L的终端节点。
r = L; //r指向了头节点,此时的头节点是终端节点。
for (int i = 0; i < 10; i++) {
s = (struct List*) malloc(sizeof(struct List));//s指向新申请的节点
s->data = i; //用新节点的数据域来接受i
r->next = s; //用r来接纳新节点
r = s; //r指向终端节点
}
r->next = NULL; //元素已经全部装入链表L中
//L的终端节点指针域为NULL,L建立完成
}
单链表的创建以及基本操作(以头插法为例):
#include<stdio.h> #include<stdlib.h> #define OK 1; #define ERROR 0; typedef int Status; typedef int ElemType; typedef struct LNode { ElemType data; struct LNode *next; }*LinkList; void createList(LinkList &L,int n) { int i; LinkList p; L=(struct LNode *)malloc(sizeof(LNode)); L->next=NULL; for(i=1;i<=n;i++) { p=(struct LNode *)malloc(sizeof(LNode)); scanf("%d",&p->data); p->next=L->next; L->next=p; } } void showList(LinkList &L) { LinkList p=L->next; If(p==NULL) { printf(“单链表为空!”); return ERROR; } while(p!=NULL) { printf("%d\t",p->data); p=p->next; } printf("\n"); } Status insertList(LinkList &L,int i,ElemType e) { int j=0; LinkList p,q; p=L; while(p&&j<i-1) { p=p->next; ++j; } if(!p||j>i-1) { return ERROR; } q=(struct LNode *)malloc(sizeof(LNode)); q->data=e; q->next=p->next; p->next=q; return OK; } Status deleteList(LinkList &L,int i,ElemType &e) { int j=0; LinkList p,q; p=L; while(p->next&&j<i-1) { p=p->next; ++j; } if(!p->next&&j>i-1) { return ERROR; } q=p->next; p->next=q->next; e=q->data; free(q); return OK; } void destoryList(LinkList &L) { LinkList p; p=L; p->next=NULL; while(p==NULL) { p=L->next; free(p); L->next=p; } } int main() { LinkList L; printf("创建链表输入五个数据:\n"); createList(L,5); printf("输出链表中的全部数据:\n"); showList(L); printf("插入数据进入链表中的三号位置:\n"); insertList(L,3,10); showList(L); int e; printf("链表中删除四号位置的数据:\n"); deleteList(L,4,e); showList(L); printf("被删除的数据e=%d\n\n",e); printf("初始化线性链表:\n"); destoryList(L); printf("\n初始化后的线性链表:\n"); showList(L); return 0; }
运行结果如下图所示:
单链表结点中只有一个指向其后继的指针,使得单链表只能从头结点依次顺序地向后遍历。要访问某个结点地前驱结点(插入、删除操作)时,只能从头开始遍历,访问后继结点时地时间复杂度为 O(1),访问前驱结点的时间复杂度为 O(n)。
为了克服单链表上的缺陷,引入了双链表,双链表结点中有两个指针 prior 和 next,分别为指向其前驱结点和后继结点,双链表示意图如下所示:
双链表中结点类型的描述如下:
typedef struct DNode{ //定义双链表结点类型
ElemType data; //数据域
struct DNode *prior,*next; //前驱和后继指针
}DNode,*DLinklist;
(1)双链表的插入操作
1. s->next = p->next; //将结点 *s 插入到结点 *p 之后
2. p->next->prior = s;
3. s->prior = p;
4. p->next = s
注意:语句顺序虽然不唯一,但也不是任意的,第1和2两步必须在第4步之前,否则 *p 的后继结点的指针就会丢掉,导致插入失败。
(2)双链表的删除操作
p->next = q->next; //步骤 1
q->next->prior = p; //步骤2
free(q); //释放结点空间
双链表的基本操作源代码如下图所示:
#include<stdio.h> #include<stdlib.h> #define OK 1; #define ERROR 0; typedef int Status; typedef int ElemType; typedef struct DuLNode { ElemType data; struct DuLNode *next; struct DuLNode *prior; }*DuLinkList; Status initList(DuLinkList &L) { L=(struct DuLNode*)malloc(sizeof(DuLNode)); if(!L) { printf("双向链表空间创建失败!\n"); exit(0); } L->next=NULL; L->prior=NULL; return OK; } Status createList(DuLinkList &L,int n) { int i; DuLinkList p,q; p=L; printf("双向链表中输入五个数据:\n"); for(i=1;i<=n;i++) { q=(struct DuLNode *)malloc(sizeof(DuLNode)); if(!q) { printf("双向链表空间创建失败!\n"); exit(0); } scanf("%d",&q->data); q->next=NULL; q->prior=p; p->next=q; p=q; } return OK; } Status showList(DuLinkList &L) { DuLinkList p=L->next; while(p!=NULL) { printf("%d\t",p->data); p=p->next; } printf("\n"); return OK; } Status deleteList(DuLinkList &L,int i,ElemType &e) { int j=1; DuLinkList p; p=L; while(p&&j<=i) { p=p->next; ++j; } if(!p&&j>i) { return ERROR; } printf("删除三号位置上的数据后的链表数据:\n"); e=p->data; p->prior->next=p->next; p->next->prior=p->prior; free(p); return OK; } Status insertList(DuLinkList &L,int i,ElemType e) { int j=1; DuLinkList p=L,s; while(p&&j<i) { p=p->next; ++j; } if(!p&&j>i) { return ERROR; } printf("双向链表四号位置插入数据100:\n"); if(!(s=(struct DuLNode *)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 destory(DuLinkList &L) { DuLinkList p=L; p->next=NULL; p->prior=NULL; while(p==NULL) { p=L->next; free(p); L->next=p; } return OK; } int main() { DuLinkList L; int e; initList(L); createList(L,5); printf("输出双向链表中的全部数据:\n"); showList(L); deleteList(L,3,e); showList(L); printf("删除的数据e=%d\n",e); insertList(L,4,100); showList(L); destory(L); showList(L); return 0; }
运行结果如下图所示:
(1)循环单链表
(2)循环双链表
以循环单链表为例,其代码如下所示:
#include<stdio.h> #include<stdlib.h> #define OK 1; #define ERROR 0; typedef int Status; typedef int ElemType; typedef struct LNode { ElemType data; struct LNode *next; }*LinkList; Status InputList(LinkList &L,int n) { int i; LinkList p,q; L=(struct LNode *)malloc(sizeof(LNode)); q=L; q->next=q; printf("输入五个数据进入循环链表中:\n"); for(i=1;i<=n;i++) { p=(struct LNode *)malloc(sizeof(LNode)); scanf("%d",&p->data); p->next=q->next; q->next=p; } return OK; } Status printList(LinkList &L) { LinkList p=L->next; while(p!=L) { printf("%d\t",p->data); p=p->next; } printf("\n"); return OK; } Status InsertList(LinkList &L,int i,ElemType e) { int j=0; LinkList p,q; p=L; while(p&&j<i-1) { p=p->next; ++j; } if(!p||j>i-1) { return ERROR; } printf("在循环链表的四号位置插入数据10:\n"); q=(struct LNode *)malloc(sizeof(LNode)); q->data=e; q->next=p->next; p->next=q; return OK; } Status deleteList(LinkList &L,int i,ElemType &e) { int j=0; LinkList p,q; p=L; while(p->next&&j<i-1) { p=p->next; ++j; } if(!p->next&&j>i-1) { return ERROR; } printf("删除循环链表二号位置的元素:\n"); q=p->next; p->next=q->next; e=q->data; free(q); return OK; } Status destoryList(LinkList &L) { LinkList p; p=L; p->next=NULL; while(p==NULL) { p=L->next; free(p); L->next=p; } return OK; } int main() { int e; LinkList L; InputList(L,5); printf("输出循环链表中的全部数据:\n"); printList(L); InsertList(L,4,10); printList(L); deleteList(L,2,e); printList(L); printf("被删除的元素e=%d\n",e); destoryList(L); return 0; }
运行结果如下图所示:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。